roojs-bootstrap.js
[roojs1] / roojs-debug.js
1 /*
2  * Based on:
3  * Ext JS Library 1.1.1
4  * Copyright(c) 2006-2007, Ext JS, LLC.
5  *
6  * Originally Released Under LGPL - original licence link has changed is not relivant.
7  *
8  * Fork - LGPL
9  * <script type="text/javascript">
10  */
11  
12
13
14
15
16 // for old browsers
17 window["undefined"] = window["undefined"];
18
19 /**
20  * @class Roo
21  * Roo core utilities and functions.
22  * @static
23  */
24 var Roo = {}; 
25 /**
26  * Copies all the properties of config to obj.
27  * @param {Object} obj The receiver of the properties
28  * @param {Object} config The source of the properties
29  * @param {Object} defaults A different object that will also be applied for default values
30  * @return {Object} returns obj
31  * @member Roo apply
32  */
33
34  
35 Roo.apply = function(o, c, defaults){
36     if(defaults){
37         // no "this" reference for friendly out of scope calls
38         Roo.apply(o, defaults);
39     }
40     if(o && c && typeof c == 'object'){
41         for(var p in c){
42             o[p] = c[p];
43         }
44     }
45     return o;
46 };
47
48
49 (function(){
50     var idSeed = 0;
51     var ua = navigator.userAgent.toLowerCase();
52
53     var isStrict = document.compatMode == "CSS1Compat",
54         isOpera = ua.indexOf("opera") > -1,
55         isSafari = (/webkit|khtml/).test(ua),
56         isFirefox = ua.indexOf("firefox") > -1,
57         isIE = ua.indexOf("msie") > -1,
58         isIE7 = ua.indexOf("msie 7") > -1,
59         isIE11 = /trident.*rv\:11\./.test(ua),
60         isEdge = ua.indexOf("edge") > -1,
61         isGecko = !isSafari && ua.indexOf("gecko") > -1,
62         isBorderBox = isIE && !isStrict,
63         isWindows = (ua.indexOf("windows") != -1 || ua.indexOf("win32") != -1),
64         isMac = (ua.indexOf("macintosh") != -1 || ua.indexOf("mac os x") != -1),
65         isLinux = (ua.indexOf("linux") != -1),
66         isSecure = window.location.href.toLowerCase().indexOf("https") === 0,
67         isIOS = /iphone|ipad/.test(ua),
68         isAndroid = /android/.test(ua),
69         isTouch =  (function() {
70             try {
71                 if (ua.indexOf('chrome') != -1 && ua.indexOf('android') == -1) {
72                     window.addEventListener('touchstart', function __set_has_touch__ () {
73                         Roo.isTouch = true;
74                         window.removeEventListener('touchstart', __set_has_touch__);
75                     });
76                     return false; // no touch on chrome!?
77                 }
78                 document.createEvent("TouchEvent");  
79                 return true;  
80             } catch (e) {  
81                 return false;  
82             } 
83             
84         })();
85     // remove css image flicker
86         if(isIE && !isIE7){
87         try{
88             document.execCommand("BackgroundImageCache", false, true);
89         }catch(e){}
90     }
91     
92     Roo.apply(Roo, {
93         /**
94          * True if the browser is in strict mode
95          * @type Boolean
96          */
97         isStrict : isStrict,
98         /**
99          * True if the page is running over SSL
100          * @type Boolean
101          */
102         isSecure : isSecure,
103         /**
104          * True when the document is fully initialized and ready for action
105          * @type Boolean
106          */
107         isReady : false,
108         /**
109          * Turn on debugging output (currently only the factory uses this)
110          * @type Boolean
111          */
112         
113         debug: false,
114
115         /**
116          * True to automatically uncache orphaned Roo.Elements periodically (defaults to true)
117          * @type Boolean
118          */
119         enableGarbageCollector : true,
120
121         /**
122          * True to automatically purge event listeners after uncaching an element (defaults to false).
123          * Note: this only happens if enableGarbageCollector is true.
124          * @type Boolean
125          */
126         enableListenerCollection:false,
127
128         /**
129          * URL to a blank file used by Roo when in secure mode for iframe src and onReady src to prevent
130          * the IE insecure content warning (defaults to javascript:false).
131          * @type String
132          */
133         SSL_SECURE_URL : "javascript:false",
134
135         /**
136          * URL to a 1x1 transparent gif image used by Roo to create inline icons with CSS background images. (Defaults to
137          * "http://Roojs.com/s.gif" and you should change this to a URL on your server).
138          * @type String
139          */
140         BLANK_IMAGE_URL : "http:/"+"/localhost/s.gif",
141
142         emptyFn : function(){},
143         
144         /**
145          * Copies all the properties of config to obj if they don't already exist.
146          * @param {Object} obj The receiver of the properties
147          * @param {Object} config The source of the properties
148          * @return {Object} returns obj
149          */
150         applyIf : function(o, c){
151             if(o && c){
152                 for(var p in c){
153                     if(typeof o[p] == "undefined"){ o[p] = c[p]; }
154                 }
155             }
156             return o;
157         },
158
159         /**
160          * Applies event listeners to elements by selectors when the document is ready.
161          * The event name is specified with an @ suffix.
162 <pre><code>
163 Roo.addBehaviors({
164    // add a listener for click on all anchors in element with id foo
165    '#foo a@click' : function(e, t){
166        // do something
167    },
168
169    // add the same listener to multiple selectors (separated by comma BEFORE the @)
170    '#foo a, #bar span.some-class@mouseover' : function(){
171        // do something
172    }
173 });
174 </code></pre>
175          * @param {Object} obj The list of behaviors to apply
176          */
177         addBehaviors : function(o){
178             if(!Roo.isReady){
179                 Roo.onReady(function(){
180                     Roo.addBehaviors(o);
181                 });
182                 return;
183             }
184             var cache = {}; // simple cache for applying multiple behaviors to same selector does query multiple times
185             for(var b in o){
186                 var parts = b.split('@');
187                 if(parts[1]){ // for Object prototype breakers
188                     var s = parts[0];
189                     if(!cache[s]){
190                         cache[s] = Roo.select(s);
191                     }
192                     cache[s].on(parts[1], o[b]);
193                 }
194             }
195             cache = null;
196         },
197
198         /**
199          * Generates unique ids. If the element already has an id, it is unchanged
200          * @param {String/HTMLElement/Element} el (optional) The element to generate an id for
201          * @param {String} prefix (optional) Id prefix (defaults "Roo-gen")
202          * @return {String} The generated Id.
203          */
204         id : function(el, prefix){
205             prefix = prefix || "roo-gen";
206             el = Roo.getDom(el);
207             var id = prefix + (++idSeed);
208             return el ? (el.id ? el.id : (el.id = id)) : id;
209         },
210          
211        
212         /**
213          * Extends one class with another class and optionally overrides members with the passed literal. This class
214          * also adds the function "override()" to the class that can be used to override
215          * members on an instance.
216          * @param {Object} subclass The class inheriting the functionality
217          * @param {Object} superclass The class being extended
218          * @param {Object} overrides (optional) A literal with members
219          * @method extend
220          */
221         extend : function(){
222             // inline overrides
223             var io = function(o){
224                 for(var m in o){
225                     this[m] = o[m];
226                 }
227             };
228             return function(sb, sp, overrides){
229                 if(typeof sp == 'object'){ // eg. prototype, rather than function constructor..
230                     overrides = sp;
231                     sp = sb;
232                     sb = function(){sp.apply(this, arguments);};
233                 }
234                 var F = function(){}, sbp, spp = sp.prototype;
235                 F.prototype = spp;
236                 sbp = sb.prototype = new F();
237                 sbp.constructor=sb;
238                 sb.superclass=spp;
239                 
240                 if(spp.constructor == Object.prototype.constructor){
241                     spp.constructor=sp;
242                    
243                 }
244                 
245                 sb.override = function(o){
246                     Roo.override(sb, o);
247                 };
248                 sbp.override = io;
249                 Roo.override(sb, overrides);
250                 return sb;
251             };
252         }(),
253
254         /**
255          * Adds a list of functions to the prototype of an existing class, overwriting any existing methods with the same name.
256          * Usage:<pre><code>
257 Roo.override(MyClass, {
258     newMethod1: function(){
259         // etc.
260     },
261     newMethod2: function(foo){
262         // etc.
263     }
264 });
265  </code></pre>
266          * @param {Object} origclass The class to override
267          * @param {Object} overrides The list of functions to add to origClass.  This should be specified as an object literal
268          * containing one or more methods.
269          * @method override
270          */
271         override : function(origclass, overrides){
272             if(overrides){
273                 var p = origclass.prototype;
274                 for(var method in overrides){
275                     p[method] = overrides[method];
276                 }
277             }
278         },
279         /**
280          * Creates namespaces to be used for scoping variables and classes so that they are not global.  Usage:
281          * <pre><code>
282 Roo.namespace('Company', 'Company.data');
283 Company.Widget = function() { ... }
284 Company.data.CustomStore = function(config) { ... }
285 </code></pre>
286          * @param {String} namespace1
287          * @param {String} namespace2
288          * @param {String} etc
289          * @method namespace
290          */
291         namespace : function(){
292             var a=arguments, o=null, i, j, d, rt;
293             for (i=0; i<a.length; ++i) {
294                 d=a[i].split(".");
295                 rt = d[0];
296                 /** eval:var:o */
297                 eval('if (typeof ' + rt + ' == "undefined"){' + rt + ' = {};} o = ' + rt + ';');
298                 for (j=1; j<d.length; ++j) {
299                     o[d[j]]=o[d[j]] || {};
300                     o=o[d[j]];
301                 }
302             }
303         },
304         /**
305          * Creates namespaces to be used for scoping variables and classes so that they are not global.  Usage:
306          * <pre><code>
307 Roo.factory({ xns: Roo.data, xtype : 'Store', .....});
308 Roo.factory(conf, Roo.data);
309 </code></pre>
310          * @param {String} classname
311          * @param {String} namespace (optional)
312          * @method factory
313          */
314          
315         factory : function(c, ns)
316         {
317             // no xtype, no ns or c.xns - or forced off by c.xns
318             if (!c.xtype   || (!ns && !c.xns) ||  (c.xns === false)) { // not enough info...
319                 return c;
320             }
321             ns = c.xns ? c.xns : ns; // if c.xns is set, then use that..
322             if (c.constructor == ns[c.xtype]) {// already created...
323                 return c;
324             }
325             if (ns[c.xtype]) {
326                 if (Roo.debug) { Roo.log("Roo.Factory(" + c.xtype + ")"); }
327                 var ret = new ns[c.xtype](c);
328                 ret.xns = false;
329                 return ret;
330             }
331             c.xns = false; // prevent recursion..
332             return c;
333         },
334          /**
335          * Logs to console if it can.
336          *
337          * @param {String|Object} string
338          * @method log
339          */
340         log : function(s)
341         {
342             if ((typeof(console) == 'undefined') || (typeof(console.log) == 'undefined')) {
343                 return; // alerT?
344             }
345             
346             console.log(s);
347         },
348         /**
349          * Takes an object and converts it to an encoded URL. e.g. Roo.urlEncode({foo: 1, bar: 2}); would return "foo=1&bar=2".  Optionally, property values can be arrays, instead of keys and the resulting string that's returned will contain a name/value pair for each array value.
350          * @param {Object} o
351          * @return {String}
352          */
353         urlEncode : function(o){
354             if(!o){
355                 return "";
356             }
357             var buf = [];
358             for(var key in o){
359                 var ov = o[key], k = Roo.encodeURIComponent(key);
360                 var type = typeof ov;
361                 if(type == 'undefined'){
362                     buf.push(k, "=&");
363                 }else if(type != "function" && type != "object"){
364                     buf.push(k, "=", Roo.encodeURIComponent(ov), "&");
365                 }else if(ov instanceof Array){
366                     if (ov.length) {
367                             for(var i = 0, len = ov.length; i < len; i++) {
368                                 buf.push(k, "=", Roo.encodeURIComponent(ov[i] === undefined ? '' : ov[i]), "&");
369                             }
370                         } else {
371                             buf.push(k, "=&");
372                         }
373                 }
374             }
375             buf.pop();
376             return buf.join("");
377         },
378          /**
379          * Safe version of encodeURIComponent
380          * @param {String} data 
381          * @return {String} 
382          */
383         
384         encodeURIComponent : function (data)
385         {
386             try {
387                 return encodeURIComponent(data);
388             } catch(e) {} // should be an uri encode error.
389             
390             if (data == '' || data == null){
391                return '';
392             }
393             // http://stackoverflow.com/questions/2596483/unicode-and-uri-encoding-decoding-and-escaping-in-javascript
394             function nibble_to_hex(nibble){
395                 var chars = '0123456789ABCDEF';
396                 return chars.charAt(nibble);
397             }
398             data = data.toString();
399             var buffer = '';
400             for(var i=0; i<data.length; i++){
401                 var c = data.charCodeAt(i);
402                 var bs = new Array();
403                 if (c > 0x10000){
404                         // 4 bytes
405                     bs[0] = 0xF0 | ((c & 0x1C0000) >>> 18);
406                     bs[1] = 0x80 | ((c & 0x3F000) >>> 12);
407                     bs[2] = 0x80 | ((c & 0xFC0) >>> 6);
408                     bs[3] = 0x80 | (c & 0x3F);
409                 }else if (c > 0x800){
410                          // 3 bytes
411                     bs[0] = 0xE0 | ((c & 0xF000) >>> 12);
412                     bs[1] = 0x80 | ((c & 0xFC0) >>> 6);
413                     bs[2] = 0x80 | (c & 0x3F);
414                 }else if (c > 0x80){
415                        // 2 bytes
416                     bs[0] = 0xC0 | ((c & 0x7C0) >>> 6);
417                     bs[1] = 0x80 | (c & 0x3F);
418                 }else{
419                         // 1 byte
420                     bs[0] = c;
421                 }
422                 for(var j=0; j<bs.length; j++){
423                     var b = bs[j];
424                     var hex = nibble_to_hex((b & 0xF0) >>> 4) 
425                             + nibble_to_hex(b &0x0F);
426                     buffer += '%'+hex;
427                }
428             }
429             return buffer;    
430              
431         },
432
433         /**
434          * Takes an encoded URL and and converts it to an object. e.g. Roo.urlDecode("foo=1&bar=2"); would return {foo: 1, bar: 2} or Roo.urlDecode("foo=1&bar=2&bar=3&bar=4", true); would return {foo: 1, bar: [2, 3, 4]}.
435          * @param {String} string
436          * @param {Boolean} overwrite (optional) Items of the same name will overwrite previous values instead of creating an an array (Defaults to false).
437          * @return {Object} A literal with members
438          */
439         urlDecode : function(string, overwrite){
440             if(!string || !string.length){
441                 return {};
442             }
443             var obj = {};
444             var pairs = string.split('&');
445             var pair, name, value;
446             for(var i = 0, len = pairs.length; i < len; i++){
447                 pair = pairs[i].split('=');
448                 name = decodeURIComponent(pair[0]);
449                 value = decodeURIComponent(pair[1]);
450                 if(overwrite !== true){
451                     if(typeof obj[name] == "undefined"){
452                         obj[name] = value;
453                     }else if(typeof obj[name] == "string"){
454                         obj[name] = [obj[name]];
455                         obj[name].push(value);
456                     }else{
457                         obj[name].push(value);
458                     }
459                 }else{
460                     obj[name] = value;
461                 }
462             }
463             return obj;
464         },
465
466         /**
467          * Iterates an array calling the passed function with each item, stopping if your function returns false. If the
468          * passed array is not really an array, your function is called once with it.
469          * The supplied function is called with (Object item, Number index, Array allItems).
470          * @param {Array/NodeList/Mixed} array
471          * @param {Function} fn
472          * @param {Object} scope
473          */
474         each : function(array, fn, scope){
475             if(typeof array.length == "undefined" || typeof array == "string"){
476                 array = [array];
477             }
478             for(var i = 0, len = array.length; i < len; i++){
479                 if(fn.call(scope || array[i], array[i], i, array) === false){ return i; };
480             }
481         },
482
483         // deprecated
484         combine : function(){
485             var as = arguments, l = as.length, r = [];
486             for(var i = 0; i < l; i++){
487                 var a = as[i];
488                 if(a instanceof Array){
489                     r = r.concat(a);
490                 }else if(a.length !== undefined && !a.substr){
491                     r = r.concat(Array.prototype.slice.call(a, 0));
492                 }else{
493                     r.push(a);
494                 }
495             }
496             return r;
497         },
498
499         /**
500          * Escapes the passed string for use in a regular expression
501          * @param {String} str
502          * @return {String}
503          */
504         escapeRe : function(s) {
505             return s.replace(/([.*+?^${}()|[\]\/\\])/g, "\\$1");
506         },
507
508         // internal
509         callback : function(cb, scope, args, delay){
510             if(typeof cb == "function"){
511                 if(delay){
512                     cb.defer(delay, scope, args || []);
513                 }else{
514                     cb.apply(scope, args || []);
515                 }
516             }
517         },
518
519         /**
520          * Return the dom node for the passed string (id), dom node, or Roo.Element
521          * @param {String/HTMLElement/Roo.Element} el
522          * @return HTMLElement
523          */
524         getDom : function(el){
525             if(!el){
526                 return null;
527             }
528             return el.dom ? el.dom : (typeof el == 'string' ? document.getElementById(el) : el);
529         },
530
531         /**
532         * Shorthand for {@link Roo.ComponentMgr#get}
533         * @param {String} id
534         * @return Roo.Component
535         */
536         getCmp : function(id){
537             return Roo.ComponentMgr.get(id);
538         },
539          
540         num : function(v, defaultValue){
541             if(typeof v != 'number'){
542                 return defaultValue;
543             }
544             return v;
545         },
546
547         destroy : function(){
548             for(var i = 0, a = arguments, len = a.length; i < len; i++) {
549                 var as = a[i];
550                 if(as){
551                     if(as.dom){
552                         as.removeAllListeners();
553                         as.remove();
554                         continue;
555                     }
556                     if(typeof as.purgeListeners == 'function'){
557                         as.purgeListeners();
558                     }
559                     if(typeof as.destroy == 'function'){
560                         as.destroy();
561                     }
562                 }
563             }
564         },
565
566         // inpired by a similar function in mootools library
567         /**
568          * Returns the type of object that is passed in. If the object passed in is null or undefined it
569          * return false otherwise it returns one of the following values:<ul>
570          * <li><b>string</b>: If the object passed is a string</li>
571          * <li><b>number</b>: If the object passed is a number</li>
572          * <li><b>boolean</b>: If the object passed is a boolean value</li>
573          * <li><b>function</b>: If the object passed is a function reference</li>
574          * <li><b>object</b>: If the object passed is an object</li>
575          * <li><b>array</b>: If the object passed is an array</li>
576          * <li><b>regexp</b>: If the object passed is a regular expression</li>
577          * <li><b>element</b>: If the object passed is a DOM Element</li>
578          * <li><b>nodelist</b>: If the object passed is a DOM NodeList</li>
579          * <li><b>textnode</b>: If the object passed is a DOM text node and contains something other than whitespace</li>
580          * <li><b>whitespace</b>: If the object passed is a DOM text node and contains only whitespace</li>
581          * @param {Mixed} object
582          * @return {String}
583          */
584         type : function(o){
585             if(o === undefined || o === null){
586                 return false;
587             }
588             if(o.htmlElement){
589                 return 'element';
590             }
591             var t = typeof o;
592             if(t == 'object' && o.nodeName) {
593                 switch(o.nodeType) {
594                     case 1: return 'element';
595                     case 3: return (/\S/).test(o.nodeValue) ? 'textnode' : 'whitespace';
596                 }
597             }
598             if(t == 'object' || t == 'function') {
599                 switch(o.constructor) {
600                     case Array: return 'array';
601                     case RegExp: return 'regexp';
602                 }
603                 if(typeof o.length == 'number' && typeof o.item == 'function') {
604                     return 'nodelist';
605                 }
606             }
607             return t;
608         },
609
610         /**
611          * Returns true if the passed value is null, undefined or an empty string (optional).
612          * @param {Mixed} value The value to test
613          * @param {Boolean} allowBlank (optional) Pass true if an empty string is not considered empty
614          * @return {Boolean}
615          */
616         isEmpty : function(v, allowBlank){
617             return v === null || v === undefined || (!allowBlank ? v === '' : false);
618         },
619         
620         /** @type Boolean */
621         isOpera : isOpera,
622         /** @type Boolean */
623         isSafari : isSafari,
624         /** @type Boolean */
625         isFirefox : isFirefox,
626         /** @type Boolean */
627         isIE : isIE,
628         /** @type Boolean */
629         isIE7 : isIE7,
630         /** @type Boolean */
631         isIE11 : isIE11,
632         /** @type Boolean */
633         isEdge : isEdge,
634         /** @type Boolean */
635         isGecko : isGecko,
636         /** @type Boolean */
637         isBorderBox : isBorderBox,
638         /** @type Boolean */
639         isWindows : isWindows,
640         /** @type Boolean */
641         isLinux : isLinux,
642         /** @type Boolean */
643         isMac : isMac,
644         /** @type Boolean */
645         isIOS : isIOS,
646         /** @type Boolean */
647         isAndroid : isAndroid,
648         /** @type Boolean */
649         isTouch : isTouch,
650
651         /**
652          * By default, Ext intelligently decides whether floating elements should be shimmed. If you are using flash,
653          * you may want to set this to true.
654          * @type Boolean
655          */
656         useShims : ((isIE && !isIE7) || (isGecko && isMac)),
657         
658         
659                 
660         /**
661          * Selects a single element as a Roo Element
662          * This is about as close as you can get to jQuery's $('do crazy stuff')
663          * @param {String} selector The selector/xpath query
664          * @param {Node} root (optional) The start of the query (defaults to document).
665          * @return {Roo.Element}
666          */
667         selectNode : function(selector, root) 
668         {
669             var node = Roo.DomQuery.selectNode(selector,root);
670             return node ? Roo.get(node) : new Roo.Element(false);
671         },
672                 /**
673                  * Find the current bootstrap width Grid size
674                  * Note xs is the default for smaller.. - this is currently used by grids to render correct columns
675                  * @returns {String} (xs|sm|md|lg|xl)
676                  */
677                 
678                 getGridSize : function()
679                 {
680                         var w = Roo.lib.Dom.getViewWidth();
681                         switch(true) {
682                                 case w > 1200:
683                                         return 'xl';
684                                 case w > 992:
685                                         return 'lg';
686                                 case w > 768:
687                                         return 'md';
688                                 case w > 576:
689                                         return 'sm';
690                                 default:
691                                         return 'xs'
692                         }
693                         
694                 }
695         
696     });
697
698
699 })();
700
701 Roo.namespace("Roo", "Roo.util", "Roo.grid", "Roo.dd", "Roo.tree", "Roo.data",
702                 "Roo.form", "Roo.menu", "Roo.state", "Roo.lib", "Roo.layout",
703                 "Roo.app", "Roo.ux",
704                 "Roo.bootstrap",
705                 "Roo.bootstrap.dash");
706 /*
707  * Based on:
708  * Ext JS Library 1.1.1
709  * Copyright(c) 2006-2007, Ext JS, LLC.
710  *
711  * Originally Released Under LGPL - original licence link has changed is not relivant.
712  *
713  * Fork - LGPL
714  * <script type="text/javascript">
715  */
716
717 (function() {    
718     // wrappedn so fnCleanup is not in global scope...
719     if(Roo.isIE) {
720         function fnCleanUp() {
721             var p = Function.prototype;
722             delete p.createSequence;
723             delete p.defer;
724             delete p.createDelegate;
725             delete p.createCallback;
726             delete p.createInterceptor;
727
728             window.detachEvent("onunload", fnCleanUp);
729         }
730         window.attachEvent("onunload", fnCleanUp);
731     }
732 })();
733
734
735 /**
736  * @class Function
737  * These functions are available on every Function object (any JavaScript function).
738  */
739 Roo.apply(Function.prototype, {
740      /**
741      * Creates a callback that passes arguments[0], arguments[1], arguments[2], ...
742      * Call directly on any function. Example: <code>myFunction.createCallback(myarg, myarg2)</code>
743      * Will create a function that is bound to those 2 args.
744      * @return {Function} The new function
745     */
746     createCallback : function(/*args...*/){
747         // make args available, in function below
748         var args = arguments;
749         var method = this;
750         return function() {
751             return method.apply(window, args);
752         };
753     },
754
755     /**
756      * Creates a delegate (callback) that sets the scope to obj.
757      * Call directly on any function. Example: <code>this.myFunction.createDelegate(this)</code>
758      * Will create a function that is automatically scoped to this.
759      * @param {Object} obj (optional) The object for which the scope is set
760      * @param {Array} args (optional) Overrides arguments for the call. (Defaults to the arguments passed by the caller)
761      * @param {Boolean/Number} appendArgs (optional) if True args are appended to call args instead of overriding,
762      *                                             if a number the args are inserted at the specified position
763      * @return {Function} The new function
764      */
765     createDelegate : function(obj, args, appendArgs){
766         var method = this;
767         return function() {
768             var callArgs = args || arguments;
769             if(appendArgs === true){
770                 callArgs = Array.prototype.slice.call(arguments, 0);
771                 callArgs = callArgs.concat(args);
772             }else if(typeof appendArgs == "number"){
773                 callArgs = Array.prototype.slice.call(arguments, 0); // copy arguments first
774                 var applyArgs = [appendArgs, 0].concat(args); // create method call params
775                 Array.prototype.splice.apply(callArgs, applyArgs); // splice them in
776             }
777             return method.apply(obj || window, callArgs);
778         };
779     },
780
781     /**
782      * Calls this function after the number of millseconds specified.
783      * @param {Number} millis The number of milliseconds for the setTimeout call (if 0 the function is executed immediately)
784      * @param {Object} obj (optional) The object for which the scope is set
785      * @param {Array} args (optional) Overrides arguments for the call. (Defaults to the arguments passed by the caller)
786      * @param {Boolean/Number} appendArgs (optional) if True args are appended to call args instead of overriding,
787      *                                             if a number the args are inserted at the specified position
788      * @return {Number} The timeout id that can be used with clearTimeout
789      */
790     defer : function(millis, obj, args, appendArgs){
791         var fn = this.createDelegate(obj, args, appendArgs);
792         if(millis){
793             return setTimeout(fn, millis);
794         }
795         fn();
796         return 0;
797     },
798     /**
799      * Create a combined function call sequence of the original function + the passed function.
800      * The resulting function returns the results of the original function.
801      * The passed fcn is called with the parameters of the original function
802      * @param {Function} fcn The function to sequence
803      * @param {Object} scope (optional) The scope of the passed fcn (Defaults to scope of original function or window)
804      * @return {Function} The new function
805      */
806     createSequence : function(fcn, scope){
807         if(typeof fcn != "function"){
808             return this;
809         }
810         var method = this;
811         return function() {
812             var retval = method.apply(this || window, arguments);
813             fcn.apply(scope || this || window, arguments);
814             return retval;
815         };
816     },
817
818     /**
819      * Creates an interceptor function. The passed fcn is called before the original one. If it returns false, the original one is not called.
820      * The resulting function returns the results of the original function.
821      * The passed fcn is called with the parameters of the original function.
822      * @addon
823      * @param {Function} fcn The function to call before the original
824      * @param {Object} scope (optional) The scope of the passed fcn (Defaults to scope of original function or window)
825      * @return {Function} The new function
826      */
827     createInterceptor : function(fcn, scope){
828         if(typeof fcn != "function"){
829             return this;
830         }
831         var method = this;
832         return function() {
833             fcn.target = this;
834             fcn.method = method;
835             if(fcn.apply(scope || this || window, arguments) === false){
836                 return;
837             }
838             return method.apply(this || window, arguments);
839         };
840     }
841 });
842 /*
843  * Based on:
844  * Ext JS Library 1.1.1
845  * Copyright(c) 2006-2007, Ext JS, LLC.
846  *
847  * Originally Released Under LGPL - original licence link has changed is not relivant.
848  *
849  * Fork - LGPL
850  * <script type="text/javascript">
851  */
852
853 Roo.applyIf(String, {
854     
855     /** @scope String */
856     
857     /**
858      * Escapes the passed string for ' and \
859      * @param {String} string The string to escape
860      * @return {String} The escaped string
861      * @static
862      */
863     escape : function(string) {
864         return string.replace(/('|\\)/g, "\\$1");
865     },
866
867     /**
868      * Pads the left side of a string with a specified character.  This is especially useful
869      * for normalizing number and date strings.  Example usage:
870      * <pre><code>
871 var s = String.leftPad('123', 5, '0');
872 // s now contains the string: '00123'
873 </code></pre>
874      * @param {String} string The original string
875      * @param {Number} size The total length of the output string
876      * @param {String} char (optional) The character with which to pad the original string (defaults to empty string " ")
877      * @return {String} The padded string
878      * @static
879      */
880     leftPad : function (val, size, ch) {
881         var result = new String(val);
882         if(ch === null || ch === undefined || ch === '') {
883             ch = " ";
884         }
885         while (result.length < size) {
886             result = ch + result;
887         }
888         return result;
889     },
890
891     /**
892      * Allows you to define a tokenized string and pass an arbitrary number of arguments to replace the tokens.  Each
893      * token must be unique, and must increment in the format {0}, {1}, etc.  Example usage:
894      * <pre><code>
895 var cls = 'my-class', text = 'Some text';
896 var s = String.format('<div class="{0}">{1}</div>', cls, text);
897 // s now contains the string: '<div class="my-class">Some text</div>'
898 </code></pre>
899      * @param {String} string The tokenized string to be formatted
900      * @param {String} value1 The value to replace token {0}
901      * @param {String} value2 Etc...
902      * @return {String} The formatted string
903      * @static
904      */
905     format : function(format){
906         var args = Array.prototype.slice.call(arguments, 1);
907         return format.replace(/\{(\d+)\}/g, function(m, i){
908             return Roo.util.Format.htmlEncode(args[i]);
909         });
910     }
911   
912     
913 });
914
915 /**
916  * Utility function that allows you to easily switch a string between two alternating values.  The passed value
917  * is compared to the current string, and if they are equal, the other value that was passed in is returned.  If
918  * they are already different, the first value passed in is returned.  Note that this method returns the new value
919  * but does not change the current string.
920  * <pre><code>
921 // alternate sort directions
922 sort = sort.toggle('ASC', 'DESC');
923
924 // instead of conditional logic:
925 sort = (sort == 'ASC' ? 'DESC' : 'ASC');
926 </code></pre>
927  * @param {String} value The value to compare to the current string
928  * @param {String} other The new value to use if the string already equals the first value passed in
929  * @return {String} The new value
930  */
931  
932 String.prototype.toggle = function(value, other){
933     return this == value ? other : value;
934 };
935
936
937 /**
938   * Remove invalid unicode characters from a string 
939   *
940   * @return {String} The clean string
941   */
942 String.prototype.unicodeClean = function () {
943     return this.replace(/[\s\S]/g,
944         function(character) {
945             if (character.charCodeAt()< 256) {
946               return character;
947            }
948            try {
949                 encodeURIComponent(character);
950            } catch(e) { 
951               return '';
952            }
953            return character;
954         }
955     );
956 };
957   
958 /*
959  * Based on:
960  * Ext JS Library 1.1.1
961  * Copyright(c) 2006-2007, Ext JS, LLC.
962  *
963  * Originally Released Under LGPL - original licence link has changed is not relivant.
964  *
965  * Fork - LGPL
966  * <script type="text/javascript">
967  */
968
969  /**
970  * @class Number
971  */
972 Roo.applyIf(Number.prototype, {
973     /**
974      * Checks whether or not the current number is within a desired range.  If the number is already within the
975      * range it is returned, otherwise the min or max value is returned depending on which side of the range is
976      * exceeded.  Note that this method returns the constrained value but does not change the current number.
977      * @param {Number} min The minimum number in the range
978      * @param {Number} max The maximum number in the range
979      * @return {Number} The constrained value if outside the range, otherwise the current value
980      */
981     constrain : function(min, max){
982         return Math.min(Math.max(this, min), max);
983     }
984 });/*
985  * Based on:
986  * Ext JS Library 1.1.1
987  * Copyright(c) 2006-2007, Ext JS, LLC.
988  *
989  * Originally Released Under LGPL - original licence link has changed is not relivant.
990  *
991  * Fork - LGPL
992  * <script type="text/javascript">
993  */
994  /**
995  * @class Array
996  */
997 Roo.applyIf(Array.prototype, {
998     /**
999      * 
1000      * Checks whether or not the specified object exists in the array.
1001      * @param {Object} o The object to check for
1002      * @return {Number} The index of o in the array (or -1 if it is not found)
1003      */
1004     indexOf : function(o){
1005        for (var i = 0, len = this.length; i < len; i++){
1006               if(this[i] == o) { return i; }
1007        }
1008            return -1;
1009     },
1010
1011     /**
1012      * Removes the specified object from the array.  If the object is not found nothing happens.
1013      * @param {Object} o The object to remove
1014      */
1015     remove : function(o){
1016        var index = this.indexOf(o);
1017        if(index != -1){
1018            this.splice(index, 1);
1019        }
1020     },
1021     /**
1022      * Map (JS 1.6 compatibility)
1023      * @param {Function} function  to call
1024      */
1025     map : function(fun )
1026     {
1027         var len = this.length >>> 0;
1028         if (typeof fun != "function") {
1029             throw new TypeError();
1030         }
1031         var res = new Array(len);
1032         var thisp = arguments[1];
1033         for (var i = 0; i < len; i++)
1034         {
1035             if (i in this) {
1036                 res[i] = fun.call(thisp, this[i], i, this);
1037             }
1038         }
1039
1040         return res;
1041     },
1042     /**
1043      * equals
1044      * @param {Array} o The array to compare to
1045      * @returns {Boolean} true if the same
1046      */
1047     equals : function(b)
1048     {
1049             // https://stackoverflow.com/questions/3115982/how-to-check-if-two-arrays-are-equal-with-javascript
1050         if (this === b) {
1051             return true;
1052         }
1053         if (b == null) {
1054             return false;
1055         }
1056         if (this.length !== b.length) {
1057             return false;
1058         }
1059           
1060         // sort?? a.sort().equals(b.sort());
1061           
1062         for (var i = 0; i < this.length; ++i) {
1063             if (this[i] !== b[i]) {
1064             return false;
1065             }
1066         }
1067         return true;
1068     } 
1069     
1070     
1071     
1072     
1073 });
1074
1075 Roo.applyIf(Array, {
1076  /**
1077      * from
1078      * @static
1079      * @param {Array} o Or Array like object (eg. nodelist)
1080      * @returns {Array} 
1081      */
1082     from : function(o)
1083     {
1084         var ret= [];
1085     
1086         for (var i =0; i < o.length; i++) { 
1087             ret[i] = o[i];
1088         }
1089         return ret;
1090       
1091     }
1092 });
1093 /*
1094  * Based on:
1095  * Ext JS Library 1.1.1
1096  * Copyright(c) 2006-2007, Ext JS, LLC.
1097  *
1098  * Originally Released Under LGPL - original licence link has changed is not relivant.
1099  *
1100  * Fork - LGPL
1101  * <script type="text/javascript">
1102  */
1103
1104 /**
1105  * @class Date
1106  *
1107  * The date parsing and format syntax is a subset of
1108  * <a href="http://www.php.net/date">PHP's date() function</a>, and the formats that are
1109  * supported will provide results equivalent to their PHP versions.
1110  *
1111  * Following is the list of all currently supported formats:
1112  *<pre>
1113 Sample date:
1114 'Wed Jan 10 2007 15:05:01 GMT-0600 (Central Standard Time)'
1115
1116 Format  Output      Description
1117 ------  ----------  --------------------------------------------------------------
1118   d      10         Day of the month, 2 digits with leading zeros
1119   D      Wed        A textual representation of a day, three letters
1120   j      10         Day of the month without leading zeros
1121   l      Wednesday  A full textual representation of the day of the week
1122   S      th         English ordinal day of month suffix, 2 chars (use with j)
1123   w      3          Numeric representation of the day of the week
1124   z      9          The julian date, or day of the year (0-365)
1125   W      01         ISO-8601 2-digit week number of year, weeks starting on Monday (00-52)
1126   F      January    A full textual representation of the month
1127   m      01         Numeric representation of a month, with leading zeros
1128   M      Jan        Month name abbreviation, three letters
1129   n      1          Numeric representation of a month, without leading zeros
1130   t      31         Number of days in the given month
1131   L      0          Whether it's a leap year (1 if it is a leap year, else 0)
1132   Y      2007       A full numeric representation of a year, 4 digits
1133   y      07         A two digit representation of a year
1134   a      pm         Lowercase Ante meridiem and Post meridiem
1135   A      PM         Uppercase Ante meridiem and Post meridiem
1136   g      3          12-hour format of an hour without leading zeros
1137   G      15         24-hour format of an hour without leading zeros
1138   h      03         12-hour format of an hour with leading zeros
1139   H      15         24-hour format of an hour with leading zeros
1140   i      05         Minutes with leading zeros
1141   s      01         Seconds, with leading zeros
1142   O      -0600      Difference to Greenwich time (GMT) in hours (Allows +08, without minutes)
1143   P      -06:00     Difference to Greenwich time (GMT) with colon between hours and minutes
1144   T      CST        Timezone setting of the machine running the code
1145   Z      -21600     Timezone offset in seconds (negative if west of UTC, positive if east)
1146 </pre>
1147  *
1148  * Example usage (note that you must escape format specifiers with '\\' to render them as character literals):
1149  * <pre><code>
1150 var dt = new Date('1/10/2007 03:05:01 PM GMT-0600');
1151 document.write(dt.format('Y-m-d'));                         //2007-01-10
1152 document.write(dt.format('F j, Y, g:i a'));                 //January 10, 2007, 3:05 pm
1153 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
1154  </code></pre>
1155  *
1156  * Here are some standard date/time patterns that you might find helpful.  They
1157  * are not part of the source of Date.js, but to use them you can simply copy this
1158  * block of code into any script that is included after Date.js and they will also become
1159  * globally available on the Date object.  Feel free to add or remove patterns as needed in your code.
1160  * <pre><code>
1161 Date.patterns = {
1162     ISO8601Long:"Y-m-d H:i:s",
1163     ISO8601Short:"Y-m-d",
1164     ShortDate: "n/j/Y",
1165     LongDate: "l, F d, Y",
1166     FullDateTime: "l, F d, Y g:i:s A",
1167     MonthDay: "F d",
1168     ShortTime: "g:i A",
1169     LongTime: "g:i:s A",
1170     SortableDateTime: "Y-m-d\\TH:i:s",
1171     UniversalSortableDateTime: "Y-m-d H:i:sO",
1172     YearMonth: "F, Y"
1173 };
1174 </code></pre>
1175  *
1176  * Example usage:
1177  * <pre><code>
1178 var dt = new Date();
1179 document.write(dt.format(Date.patterns.ShortDate));
1180  </code></pre>
1181  */
1182
1183 /*
1184  * Most of the date-formatting functions below are the excellent work of Baron Schwartz.
1185  * They generate precompiled functions from date formats instead of parsing and
1186  * processing the pattern every time you format a date.  These functions are available
1187  * on every Date object (any javascript function).
1188  *
1189  * The original article and download are here:
1190  * http://www.xaprb.com/blog/2005/12/12/javascript-closures-for-runtime-efficiency/
1191  *
1192  */
1193  
1194  
1195  // was in core
1196 /**
1197  Returns the number of milliseconds between this date and date
1198  @param {Date} date (optional) Defaults to now
1199  @return {Number} The diff in milliseconds
1200  @member Date getElapsed
1201  */
1202 Date.prototype.getElapsed = function(date) {
1203         return Math.abs((date || new Date()).getTime()-this.getTime());
1204 };
1205 // was in date file..
1206
1207
1208 // private
1209 Date.parseFunctions = {count:0};
1210 // private
1211 Date.parseRegexes = [];
1212 // private
1213 Date.formatFunctions = {count:0};
1214
1215 // private
1216 Date.prototype.dateFormat = function(format) {
1217     if (Date.formatFunctions[format] == null) {
1218         Date.createNewFormat(format);
1219     }
1220     var func = Date.formatFunctions[format];
1221     return this[func]();
1222 };
1223
1224
1225 /**
1226  * Formats a date given the supplied format string
1227  * @param {String} format The format string
1228  * @return {String} The formatted date
1229  * @method
1230  */
1231 Date.prototype.format = Date.prototype.dateFormat;
1232
1233 // private
1234 Date.createNewFormat = function(format) {
1235     var funcName = "format" + Date.formatFunctions.count++;
1236     Date.formatFunctions[format] = funcName;
1237     var code = "Date.prototype." + funcName + " = function(){return ";
1238     var special = false;
1239     var ch = '';
1240     for (var i = 0; i < format.length; ++i) {
1241         ch = format.charAt(i);
1242         if (!special && ch == "\\") {
1243             special = true;
1244         }
1245         else if (special) {
1246             special = false;
1247             code += "'" + String.escape(ch) + "' + ";
1248         }
1249         else {
1250             code += Date.getFormatCode(ch);
1251         }
1252     }
1253     /** eval:var:zzzzzzzzzzzzz */
1254     eval(code.substring(0, code.length - 3) + ";}");
1255 };
1256
1257 // private
1258 Date.getFormatCode = function(character) {
1259     switch (character) {
1260     case "d":
1261         return "String.leftPad(this.getDate(), 2, '0') + ";
1262     case "D":
1263         return "Date.dayNames[this.getDay()].substring(0, 3) + ";
1264     case "j":
1265         return "this.getDate() + ";
1266     case "l":
1267         return "Date.dayNames[this.getDay()] + ";
1268     case "S":
1269         return "this.getSuffix() + ";
1270     case "w":
1271         return "this.getDay() + ";
1272     case "z":
1273         return "this.getDayOfYear() + ";
1274     case "W":
1275         return "this.getWeekOfYear() + ";
1276     case "F":
1277         return "Date.monthNames[this.getMonth()] + ";
1278     case "m":
1279         return "String.leftPad(this.getMonth() + 1, 2, '0') + ";
1280     case "M":
1281         return "Date.monthNames[this.getMonth()].substring(0, 3) + ";
1282     case "n":
1283         return "(this.getMonth() + 1) + ";
1284     case "t":
1285         return "this.getDaysInMonth() + ";
1286     case "L":
1287         return "(this.isLeapYear() ? 1 : 0) + ";
1288     case "Y":
1289         return "this.getFullYear() + ";
1290     case "y":
1291         return "('' + this.getFullYear()).substring(2, 4) + ";
1292     case "a":
1293         return "(this.getHours() < 12 ? 'am' : 'pm') + ";
1294     case "A":
1295         return "(this.getHours() < 12 ? 'AM' : 'PM') + ";
1296     case "g":
1297         return "((this.getHours() % 12) ? this.getHours() % 12 : 12) + ";
1298     case "G":
1299         return "this.getHours() + ";
1300     case "h":
1301         return "String.leftPad((this.getHours() % 12) ? this.getHours() % 12 : 12, 2, '0') + ";
1302     case "H":
1303         return "String.leftPad(this.getHours(), 2, '0') + ";
1304     case "i":
1305         return "String.leftPad(this.getMinutes(), 2, '0') + ";
1306     case "s":
1307         return "String.leftPad(this.getSeconds(), 2, '0') + ";
1308     case "O":
1309         return "this.getGMTOffset() + ";
1310     case "P":
1311         return "this.getGMTColonOffset() + ";
1312     case "T":
1313         return "this.getTimezone() + ";
1314     case "Z":
1315         return "(this.getTimezoneOffset() * -60) + ";
1316     default:
1317         return "'" + String.escape(character) + "' + ";
1318     }
1319 };
1320
1321 /**
1322  * Parses the passed string using the specified format. Note that this function expects dates in normal calendar
1323  * format, meaning that months are 1-based (1 = January) and not zero-based like in JavaScript dates.  Any part of
1324  * the date format that is not specified will default to the current date value for that part.  Time parts can also
1325  * be specified, but default to 0.  Keep in mind that the input date string must precisely match the specified format
1326  * string or the parse operation will fail.
1327  * Example Usage:
1328 <pre><code>
1329 //dt = Fri May 25 2007 (current date)
1330 var dt = new Date();
1331
1332 //dt = Thu May 25 2006 (today's month/day in 2006)
1333 dt = Date.parseDate("2006", "Y");
1334
1335 //dt = Sun Jan 15 2006 (all date parts specified)
1336 dt = Date.parseDate("2006-1-15", "Y-m-d");
1337
1338 //dt = Sun Jan 15 2006 15:20:01 GMT-0600 (CST)
1339 dt = Date.parseDate("2006-1-15 3:20:01 PM", "Y-m-d h:i:s A" );
1340 </code></pre>
1341  * @param {String} input The unparsed date as a string
1342  * @param {String} format The format the date is in
1343  * @return {Date} The parsed date
1344  * @static
1345  */
1346 Date.parseDate = function(input, format) {
1347     if (Date.parseFunctions[format] == null) {
1348         Date.createParser(format);
1349     }
1350     var func = Date.parseFunctions[format];
1351     return Date[func](input);
1352 };
1353 /**
1354  * @private
1355  */
1356
1357 Date.createParser = function(format) {
1358     var funcName = "parse" + Date.parseFunctions.count++;
1359     var regexNum = Date.parseRegexes.length;
1360     var currentGroup = 1;
1361     Date.parseFunctions[format] = funcName;
1362
1363     var code = "Date." + funcName + " = function(input){\n"
1364         + "var y = -1, m = -1, d = -1, h = -1, i = -1, s = -1, o, z, v;\n"
1365         + "var d = new Date();\n"
1366         + "y = d.getFullYear();\n"
1367         + "m = d.getMonth();\n"
1368         + "d = d.getDate();\n"
1369         + "if (typeof(input) !== 'string') { input = input.toString(); }\n"
1370         + "var results = input.match(Date.parseRegexes[" + regexNum + "]);\n"
1371         + "if (results && results.length > 0) {";
1372     var regex = "";
1373
1374     var special = false;
1375     var ch = '';
1376     for (var i = 0; i < format.length; ++i) {
1377         ch = format.charAt(i);
1378         if (!special && ch == "\\") {
1379             special = true;
1380         }
1381         else if (special) {
1382             special = false;
1383             regex += String.escape(ch);
1384         }
1385         else {
1386             var obj = Date.formatCodeToRegex(ch, currentGroup);
1387             currentGroup += obj.g;
1388             regex += obj.s;
1389             if (obj.g && obj.c) {
1390                 code += obj.c;
1391             }
1392         }
1393     }
1394
1395     code += "if (y >= 0 && m >= 0 && d > 0 && h >= 0 && i >= 0 && s >= 0)\n"
1396         + "{v = new Date(y, m, d, h, i, s);}\n"
1397         + "else if (y >= 0 && m >= 0 && d > 0 && h >= 0 && i >= 0)\n"
1398         + "{v = new Date(y, m, d, h, i);}\n"
1399         + "else if (y >= 0 && m >= 0 && d > 0 && h >= 0)\n"
1400         + "{v = new Date(y, m, d, h);}\n"
1401         + "else if (y >= 0 && m >= 0 && d > 0)\n"
1402         + "{v = new Date(y, m, d);}\n"
1403         + "else if (y >= 0 && m >= 0)\n"
1404         + "{v = new Date(y, m);}\n"
1405         + "else if (y >= 0)\n"
1406         + "{v = new Date(y);}\n"
1407         + "}return (v && (z || o))?\n" // favour UTC offset over GMT offset
1408         + "    ((z)? v.add(Date.SECOND, (v.getTimezoneOffset() * 60) + (z*1)) :\n" // reset to UTC, then add offset
1409         + "        v.add(Date.HOUR, (v.getGMTOffset() / 100) + (o / -100))) : v\n" // reset to GMT, then add offset
1410         + ";}";
1411
1412     Date.parseRegexes[regexNum] = new RegExp("^" + regex + "$");
1413     /** eval:var:zzzzzzzzzzzzz */
1414     eval(code);
1415 };
1416
1417 // private
1418 Date.formatCodeToRegex = function(character, currentGroup) {
1419     switch (character) {
1420     case "D":
1421         return {g:0,
1422         c:null,
1423         s:"(?:Sun|Mon|Tue|Wed|Thu|Fri|Sat)"};
1424     case "j":
1425         return {g:1,
1426             c:"d = parseInt(results[" + currentGroup + "], 10);\n",
1427             s:"(\\d{1,2})"}; // day of month without leading zeroes
1428     case "d":
1429         return {g:1,
1430             c:"d = parseInt(results[" + currentGroup + "], 10);\n",
1431             s:"(\\d{2})"}; // day of month with leading zeroes
1432     case "l":
1433         return {g:0,
1434             c:null,
1435             s:"(?:" + Date.dayNames.join("|") + ")"};
1436     case "S":
1437         return {g:0,
1438             c:null,
1439             s:"(?:st|nd|rd|th)"};
1440     case "w":
1441         return {g:0,
1442             c:null,
1443             s:"\\d"};
1444     case "z":
1445         return {g:0,
1446             c:null,
1447             s:"(?:\\d{1,3})"};
1448     case "W":
1449         return {g:0,
1450             c:null,
1451             s:"(?:\\d{2})"};
1452     case "F":
1453         return {g:1,
1454             c:"m = parseInt(Date.monthNumbers[results[" + currentGroup + "].substring(0, 3)], 10);\n",
1455             s:"(" + Date.monthNames.join("|") + ")"};
1456     case "M":
1457         return {g:1,
1458             c:"m = parseInt(Date.monthNumbers[results[" + currentGroup + "]], 10);\n",
1459             s:"(Jan|Feb|Mar|Apr|May|Jun|Jul|Aug|Sep|Oct|Nov|Dec)"};
1460     case "n":
1461         return {g:1,
1462             c:"m = parseInt(results[" + currentGroup + "], 10) - 1;\n",
1463             s:"(\\d{1,2})"}; // Numeric representation of a month, without leading zeros
1464     case "m":
1465         return {g:1,
1466             c:"m = parseInt(results[" + currentGroup + "], 10) - 1;\n",
1467             s:"(\\d{2})"}; // Numeric representation of a month, with leading zeros
1468     case "t":
1469         return {g:0,
1470             c:null,
1471             s:"\\d{1,2}"};
1472     case "L":
1473         return {g:0,
1474             c:null,
1475             s:"(?:1|0)"};
1476     case "Y":
1477         return {g:1,
1478             c:"y = parseInt(results[" + currentGroup + "], 10);\n",
1479             s:"(\\d{4})"};
1480     case "y":
1481         return {g:1,
1482             c:"var ty = parseInt(results[" + currentGroup + "], 10);\n"
1483                 + "y = ty > Date.y2kYear ? 1900 + ty : 2000 + ty;\n",
1484             s:"(\\d{1,2})"};
1485     case "a":
1486         return {g:1,
1487             c:"if (results[" + currentGroup + "] == 'am') {\n"
1488                 + "if (h == 12) { h = 0; }\n"
1489                 + "} else { if (h < 12) { h += 12; }}",
1490             s:"(am|pm)"};
1491     case "A":
1492         return {g:1,
1493             c:"if (results[" + currentGroup + "] == 'AM') {\n"
1494                 + "if (h == 12) { h = 0; }\n"
1495                 + "} else { if (h < 12) { h += 12; }}",
1496             s:"(AM|PM)"};
1497     case "g":
1498     case "G":
1499         return {g:1,
1500             c:"h = parseInt(results[" + currentGroup + "], 10);\n",
1501             s:"(\\d{1,2})"}; // 12/24-hr format  format of an hour without leading zeroes
1502     case "h":
1503     case "H":
1504         return {g:1,
1505             c:"h = parseInt(results[" + currentGroup + "], 10);\n",
1506             s:"(\\d{2})"}; //  12/24-hr format  format of an hour with leading zeroes
1507     case "i":
1508         return {g:1,
1509             c:"i = parseInt(results[" + currentGroup + "], 10);\n",
1510             s:"(\\d{2})"};
1511     case "s":
1512         return {g:1,
1513             c:"s = parseInt(results[" + currentGroup + "], 10);\n",
1514             s:"(\\d{2})"};
1515     case "O":
1516         return {g:1,
1517             c:[
1518                 "o = results[", currentGroup, "];\n",
1519                 "var sn = o.substring(0,1);\n", // get + / - sign
1520                 "var hr = o.substring(1,3)*1 + Math.floor(o.substring(3,5) / 60);\n", // get hours (performs minutes-to-hour conversion also)
1521                 "var mn = o.substring(3,5) % 60;\n", // get minutes
1522                 "o = ((-12 <= (hr*60 + mn)/60) && ((hr*60 + mn)/60 <= 14))?\n", // -12hrs <= GMT offset <= 14hrs
1523                 "    (sn + String.leftPad(hr, 2, 0) + String.leftPad(mn, 2, 0)) : null;\n"
1524             ].join(""),
1525             s:"([+\-]\\d{2,4})"};
1526     
1527     
1528     case "P":
1529         return {g:1,
1530                 c:[
1531                    "o = results[", currentGroup, "];\n",
1532                    "var sn = o.substring(0,1);\n",
1533                    "var hr = o.substring(1,3)*1 + Math.floor(o.substring(4,6) / 60);\n",
1534                    "var mn = o.substring(4,6) % 60;\n",
1535                    "o = ((-12 <= (hr*60 + mn)/60) && ((hr*60 + mn)/60 <= 14))?\n",
1536                         "    (sn + String.leftPad(hr, 2, 0) + String.leftPad(mn, 2, 0)) : null;\n"
1537             ].join(""),
1538             s:"([+\-]\\d{4})"};
1539     case "T":
1540         return {g:0,
1541             c:null,
1542             s:"[A-Z]{1,4}"}; // timezone abbrev. may be between 1 - 4 chars
1543     case "Z":
1544         return {g:1,
1545             c:"z = results[" + currentGroup + "];\n" // -43200 <= UTC offset <= 50400
1546                   + "z = (-43200 <= z*1 && z*1 <= 50400)? z : null;\n",
1547             s:"([+\-]?\\d{1,5})"}; // leading '+' sign is optional for UTC offset
1548     default:
1549         return {g:0,
1550             c:null,
1551             s:String.escape(character)};
1552     }
1553 };
1554
1555 /**
1556  * Get the timezone abbreviation of the current date (equivalent to the format specifier 'T').
1557  * @return {String} The abbreviated timezone name (e.g. 'CST')
1558  */
1559 Date.prototype.getTimezone = function() {
1560     return this.toString().replace(/^.*? ([A-Z]{1,4})[\-+][0-9]{4} .*$/, "$1");
1561 };
1562
1563 /**
1564  * Get the offset from GMT of the current date (equivalent to the format specifier 'O').
1565  * @return {String} The 4-character offset string prefixed with + or - (e.g. '-0600')
1566  */
1567 Date.prototype.getGMTOffset = function() {
1568     return (this.getTimezoneOffset() > 0 ? "-" : "+")
1569         + String.leftPad(Math.abs(Math.floor(this.getTimezoneOffset() / 60)), 2, "0")
1570         + String.leftPad(this.getTimezoneOffset() % 60, 2, "0");
1571 };
1572
1573 /**
1574  * Get the offset from GMT of the current date (equivalent to the format specifier 'P').
1575  * @return {String} 2-characters representing hours and 2-characters representing minutes
1576  * seperated by a colon and prefixed with + or - (e.g. '-06:00')
1577  */
1578 Date.prototype.getGMTColonOffset = function() {
1579         return (this.getTimezoneOffset() > 0 ? "-" : "+")
1580                 + String.leftPad(Math.abs(Math.floor(this.getTimezoneOffset() / 60)), 2, "0")
1581                 + ":"
1582                 + String.leftPad(this.getTimezoneOffset() %60, 2, "0");
1583 }
1584
1585 /**
1586  * Get the numeric day number of the year, adjusted for leap year.
1587  * @return {Number} 0 through 364 (365 in leap years)
1588  */
1589 Date.prototype.getDayOfYear = function() {
1590     var num = 0;
1591     Date.daysInMonth[1] = this.isLeapYear() ? 29 : 28;
1592     for (var i = 0; i < this.getMonth(); ++i) {
1593         num += Date.daysInMonth[i];
1594     }
1595     return num + this.getDate() - 1;
1596 };
1597
1598 /**
1599  * Get the string representation of the numeric week number of the year
1600  * (equivalent to the format specifier 'W').
1601  * @return {String} '00' through '52'
1602  */
1603 Date.prototype.getWeekOfYear = function() {
1604     // Skip to Thursday of this week
1605     var now = this.getDayOfYear() + (4 - this.getDay());
1606     // Find the first Thursday of the year
1607     var jan1 = new Date(this.getFullYear(), 0, 1);
1608     var then = (7 - jan1.getDay() + 4);
1609     return String.leftPad(((now - then) / 7) + 1, 2, "0");
1610 };
1611
1612 /**
1613  * Whether or not the current date is in a leap year.
1614  * @return {Boolean} True if the current date is in a leap year, else false
1615  */
1616 Date.prototype.isLeapYear = function() {
1617     var year = this.getFullYear();
1618     return ((year & 3) == 0 && (year % 100 || (year % 400 == 0 && year)));
1619 };
1620
1621 /**
1622  * Get the first day of the current month, adjusted for leap year.  The returned value
1623  * is the numeric day index within the week (0-6) which can be used in conjunction with
1624  * the {@link #monthNames} array to retrieve the textual day name.
1625  * Example:
1626  *<pre><code>
1627 var dt = new Date('1/10/2007');
1628 document.write(Date.dayNames[dt.getFirstDayOfMonth()]); //output: 'Monday'
1629 </code></pre>
1630  * @return {Number} The day number (0-6)
1631  */
1632 Date.prototype.getFirstDayOfMonth = function() {
1633     var day = (this.getDay() - (this.getDate() - 1)) % 7;
1634     return (day < 0) ? (day + 7) : day;
1635 };
1636
1637 /**
1638  * Get the last day of the current month, adjusted for leap year.  The returned value
1639  * is the numeric day index within the week (0-6) which can be used in conjunction with
1640  * the {@link #monthNames} array to retrieve the textual day name.
1641  * Example:
1642  *<pre><code>
1643 var dt = new Date('1/10/2007');
1644 document.write(Date.dayNames[dt.getLastDayOfMonth()]); //output: 'Wednesday'
1645 </code></pre>
1646  * @return {Number} The day number (0-6)
1647  */
1648 Date.prototype.getLastDayOfMonth = function() {
1649     var day = (this.getDay() + (Date.daysInMonth[this.getMonth()] - this.getDate())) % 7;
1650     return (day < 0) ? (day + 7) : day;
1651 };
1652
1653
1654 /**
1655  * Get the first date of this date's month
1656  * @return {Date}
1657  */
1658 Date.prototype.getFirstDateOfMonth = function() {
1659     return new Date(this.getFullYear(), this.getMonth(), 1);
1660 };
1661
1662 /**
1663  * Get the last date of this date's month
1664  * @return {Date}
1665  */
1666 Date.prototype.getLastDateOfMonth = function() {
1667     return new Date(this.getFullYear(), this.getMonth(), this.getDaysInMonth());
1668 };
1669 /**
1670  * Get the number of days in the current month, adjusted for leap year.
1671  * @return {Number} The number of days in the month
1672  */
1673 Date.prototype.getDaysInMonth = function() {
1674     Date.daysInMonth[1] = this.isLeapYear() ? 29 : 28;
1675     return Date.daysInMonth[this.getMonth()];
1676 };
1677
1678 /**
1679  * Get the English ordinal suffix of the current day (equivalent to the format specifier 'S').
1680  * @return {String} 'st, 'nd', 'rd' or 'th'
1681  */
1682 Date.prototype.getSuffix = function() {
1683     switch (this.getDate()) {
1684         case 1:
1685         case 21:
1686         case 31:
1687             return "st";
1688         case 2:
1689         case 22:
1690             return "nd";
1691         case 3:
1692         case 23:
1693             return "rd";
1694         default:
1695             return "th";
1696     }
1697 };
1698
1699 // private
1700 Date.daysInMonth = [31,28,31,30,31,30,31,31,30,31,30,31];
1701
1702 /**
1703  * An array of textual month names.
1704  * Override these values for international dates, for example...
1705  * Date.monthNames = ['JanInYourLang', 'FebInYourLang', ...];
1706  * @type Array
1707  * @static
1708  */
1709 Date.monthNames =
1710    ["January",
1711     "February",
1712     "March",
1713     "April",
1714     "May",
1715     "June",
1716     "July",
1717     "August",
1718     "September",
1719     "October",
1720     "November",
1721     "December"];
1722
1723 /**
1724  * An array of textual day names.
1725  * Override these values for international dates, for example...
1726  * Date.dayNames = ['SundayInYourLang', 'MondayInYourLang', ...];
1727  * @type Array
1728  * @static
1729  */
1730 Date.dayNames =
1731    ["Sunday",
1732     "Monday",
1733     "Tuesday",
1734     "Wednesday",
1735     "Thursday",
1736     "Friday",
1737     "Saturday"];
1738
1739 // private
1740 Date.y2kYear = 50;
1741 // private
1742 Date.monthNumbers = {
1743     Jan:0,
1744     Feb:1,
1745     Mar:2,
1746     Apr:3,
1747     May:4,
1748     Jun:5,
1749     Jul:6,
1750     Aug:7,
1751     Sep:8,
1752     Oct:9,
1753     Nov:10,
1754     Dec:11};
1755
1756 /**
1757  * Creates and returns a new Date instance with the exact same date value as the called instance.
1758  * Dates are copied and passed by reference, so if a copied date variable is modified later, the original
1759  * variable will also be changed.  When the intention is to create a new variable that will not
1760  * modify the original instance, you should create a clone.
1761  *
1762  * Example of correctly cloning a date:
1763  * <pre><code>
1764 //wrong way:
1765 var orig = new Date('10/1/2006');
1766 var copy = orig;
1767 copy.setDate(5);
1768 document.write(orig);  //returns 'Thu Oct 05 2006'!
1769
1770 //correct way:
1771 var orig = new Date('10/1/2006');
1772 var copy = orig.clone();
1773 copy.setDate(5);
1774 document.write(orig);  //returns 'Thu Oct 01 2006'
1775 </code></pre>
1776  * @return {Date} The new Date instance
1777  */
1778 Date.prototype.clone = function() {
1779         return new Date(this.getTime());
1780 };
1781
1782 /**
1783  * Clears any time information from this date
1784  @param {Boolean} clone true to create a clone of this date, clear the time and return it
1785  @return {Date} this or the clone
1786  */
1787 Date.prototype.clearTime = function(clone){
1788     if(clone){
1789         return this.clone().clearTime();
1790     }
1791     this.setHours(0);
1792     this.setMinutes(0);
1793     this.setSeconds(0);
1794     this.setMilliseconds(0);
1795     return this;
1796 };
1797
1798 // private
1799 // safari setMonth is broken -- check that this is only donw once...
1800 if(Roo.isSafari && typeof(Date.brokenSetMonth) == 'undefined'){
1801     Date.brokenSetMonth = Date.prototype.setMonth;
1802         Date.prototype.setMonth = function(num){
1803                 if(num <= -1){
1804                         var n = Math.ceil(-num);
1805                         var back_year = Math.ceil(n/12);
1806                         var month = (n % 12) ? 12 - n % 12 : 0 ;
1807                         this.setFullYear(this.getFullYear() - back_year);
1808                         return Date.brokenSetMonth.call(this, month);
1809                 } else {
1810                         return Date.brokenSetMonth.apply(this, arguments);
1811                 }
1812         };
1813 }
1814
1815 /** Date interval constant 
1816 * @static 
1817 * @type String */
1818 Date.MILLI = "ms";
1819 /** Date interval constant 
1820 * @static 
1821 * @type String */
1822 Date.SECOND = "s";
1823 /** Date interval constant 
1824 * @static 
1825 * @type String */
1826 Date.MINUTE = "mi";
1827 /** Date interval constant 
1828 * @static 
1829 * @type String */
1830 Date.HOUR = "h";
1831 /** Date interval constant 
1832 * @static 
1833 * @type String */
1834 Date.DAY = "d";
1835 /** Date interval constant 
1836 * @static 
1837 * @type String */
1838 Date.MONTH = "mo";
1839 /** Date interval constant 
1840 * @static 
1841 * @type String */
1842 Date.YEAR = "y";
1843
1844 /**
1845  * Provides a convenient method of performing basic date arithmetic.  This method
1846  * does not modify the Date instance being called - it creates and returns
1847  * a new Date instance containing the resulting date value.
1848  *
1849  * Examples:
1850  * <pre><code>
1851 //Basic usage:
1852 var dt = new Date('10/29/2006').add(Date.DAY, 5);
1853 document.write(dt); //returns 'Fri Oct 06 2006 00:00:00'
1854
1855 //Negative values will subtract correctly:
1856 var dt2 = new Date('10/1/2006').add(Date.DAY, -5);
1857 document.write(dt2); //returns 'Tue Sep 26 2006 00:00:00'
1858
1859 //You can even chain several calls together in one line!
1860 var dt3 = new Date('10/1/2006').add(Date.DAY, 5).add(Date.HOUR, 8).add(Date.MINUTE, -30);
1861 document.write(dt3); //returns 'Fri Oct 06 2006 07:30:00'
1862  </code></pre>
1863  *
1864  * @param {String} interval   A valid date interval enum value
1865  * @param {Number} value      The amount to add to the current date
1866  * @return {Date} The new Date instance
1867  */
1868 Date.prototype.add = function(interval, value){
1869   var d = this.clone();
1870   if (!interval || value === 0) { return d; }
1871   switch(interval.toLowerCase()){
1872     case Date.MILLI:
1873       d.setMilliseconds(this.getMilliseconds() + value);
1874       break;
1875     case Date.SECOND:
1876       d.setSeconds(this.getSeconds() + value);
1877       break;
1878     case Date.MINUTE:
1879       d.setMinutes(this.getMinutes() + value);
1880       break;
1881     case Date.HOUR:
1882       d.setHours(this.getHours() + value);
1883       break;
1884     case Date.DAY:
1885       d.setDate(this.getDate() + value);
1886       break;
1887     case Date.MONTH:
1888       var day = this.getDate();
1889       if(day > 28){
1890           day = Math.min(day, this.getFirstDateOfMonth().add('mo', value).getLastDateOfMonth().getDate());
1891       }
1892       d.setDate(day);
1893       d.setMonth(this.getMonth() + value);
1894       break;
1895     case Date.YEAR:
1896       d.setFullYear(this.getFullYear() + value);
1897       break;
1898   }
1899   return d;
1900 };
1901 /**
1902  * @class Roo.lib.Dom
1903  * @licence LGPL
1904  * @static
1905  * 
1906  * Dom utils (from YIU afaik)
1907  *
1908  * 
1909  **/
1910 Roo.lib.Dom = {
1911     /**
1912      * Get the view width
1913      * @param {Boolean} full True will get the full document, otherwise it's the view width
1914      * @return {Number} The width
1915      */
1916      
1917     getViewWidth : function(full) {
1918         return full ? this.getDocumentWidth() : this.getViewportWidth();
1919     },
1920     /**
1921      * Get the view height
1922      * @param {Boolean} full True will get the full document, otherwise it's the view height
1923      * @return {Number} The height
1924      */
1925     getViewHeight : function(full) {
1926         return full ? this.getDocumentHeight() : this.getViewportHeight();
1927     },
1928     /**
1929      * Get the Full Document height 
1930      * @return {Number} The height
1931      */
1932     getDocumentHeight: function() {
1933         var scrollHeight = (document.compatMode != "CSS1Compat") ? document.body.scrollHeight : document.documentElement.scrollHeight;
1934         return Math.max(scrollHeight, this.getViewportHeight());
1935     },
1936     /**
1937      * Get the Full Document width
1938      * @return {Number} The width
1939      */
1940     getDocumentWidth: function() {
1941         var scrollWidth = (document.compatMode != "CSS1Compat") ? document.body.scrollWidth : document.documentElement.scrollWidth;
1942         return Math.max(scrollWidth, this.getViewportWidth());
1943     },
1944     /**
1945      * Get the Window Viewport height
1946      * @return {Number} The height
1947      */
1948     getViewportHeight: function() {
1949         var height = self.innerHeight;
1950         var mode = document.compatMode;
1951
1952         if ((mode || Roo.isIE) && !Roo.isOpera) {
1953             height = (mode == "CSS1Compat") ?
1954                      document.documentElement.clientHeight :
1955                      document.body.clientHeight;
1956         }
1957
1958         return height;
1959     },
1960     /**
1961      * Get the Window Viewport width
1962      * @return {Number} The width
1963      */
1964     getViewportWidth: function() {
1965         var width = self.innerWidth;
1966         var mode = document.compatMode;
1967
1968         if (mode || Roo.isIE) {
1969             width = (mode == "CSS1Compat") ?
1970                     document.documentElement.clientWidth :
1971                     document.body.clientWidth;
1972         }
1973         return width;
1974     },
1975
1976     isAncestor : function(p, c) {
1977         p = Roo.getDom(p);
1978         c = Roo.getDom(c);
1979         if (!p || !c) {
1980             return false;
1981         }
1982
1983         if (p.contains && !Roo.isSafari) {
1984             return p.contains(c);
1985         } else if (p.compareDocumentPosition) {
1986             return !!(p.compareDocumentPosition(c) & 16);
1987         } else {
1988             var parent = c.parentNode;
1989             while (parent) {
1990                 if (parent == p) {
1991                     return true;
1992                 }
1993                 else if (!parent.tagName || parent.tagName.toUpperCase() == "HTML") {
1994                     return false;
1995                 }
1996                 parent = parent.parentNode;
1997             }
1998             return false;
1999         }
2000     },
2001
2002     getRegion : function(el) {
2003         return Roo.lib.Region.getRegion(el);
2004     },
2005
2006     getY : function(el) {
2007         return this.getXY(el)[1];
2008     },
2009
2010     getX : function(el) {
2011         return this.getXY(el)[0];
2012     },
2013
2014     getXY : function(el) {
2015         var p, pe, b, scroll, bd = document.body;
2016         el = Roo.getDom(el);
2017         var fly = Roo.lib.AnimBase.fly;
2018         if (el.getBoundingClientRect) {
2019             b = el.getBoundingClientRect();
2020             scroll = fly(document).getScroll();
2021             return [b.left + scroll.left, b.top + scroll.top];
2022         }
2023         var x = 0, y = 0;
2024
2025         p = el;
2026
2027         var hasAbsolute = fly(el).getStyle("position") == "absolute";
2028
2029         while (p) {
2030
2031             x += p.offsetLeft;
2032             y += p.offsetTop;
2033
2034             if (!hasAbsolute && fly(p).getStyle("position") == "absolute") {
2035                 hasAbsolute = true;
2036             }
2037
2038             if (Roo.isGecko) {
2039                 pe = fly(p);
2040
2041                 var bt = parseInt(pe.getStyle("borderTopWidth"), 10) || 0;
2042                 var bl = parseInt(pe.getStyle("borderLeftWidth"), 10) || 0;
2043
2044
2045                 x += bl;
2046                 y += bt;
2047
2048
2049                 if (p != el && pe.getStyle('overflow') != 'visible') {
2050                     x += bl;
2051                     y += bt;
2052                 }
2053             }
2054             p = p.offsetParent;
2055         }
2056
2057         if (Roo.isSafari && hasAbsolute) {
2058             x -= bd.offsetLeft;
2059             y -= bd.offsetTop;
2060         }
2061
2062         if (Roo.isGecko && !hasAbsolute) {
2063             var dbd = fly(bd);
2064             x += parseInt(dbd.getStyle("borderLeftWidth"), 10) || 0;
2065             y += parseInt(dbd.getStyle("borderTopWidth"), 10) || 0;
2066         }
2067
2068         p = el.parentNode;
2069         while (p && p != bd) {
2070             if (!Roo.isOpera || (p.tagName != 'TR' && fly(p).getStyle("display") != "inline")) {
2071                 x -= p.scrollLeft;
2072                 y -= p.scrollTop;
2073             }
2074             p = p.parentNode;
2075         }
2076         return [x, y];
2077     },
2078  
2079   
2080
2081
2082     setXY : function(el, xy) {
2083         el = Roo.fly(el, '_setXY');
2084         el.position();
2085         var pts = el.translatePoints(xy);
2086         if (xy[0] !== false) {
2087             el.dom.style.left = pts.left + "px";
2088         }
2089         if (xy[1] !== false) {
2090             el.dom.style.top = pts.top + "px";
2091         }
2092     },
2093
2094     setX : function(el, x) {
2095         this.setXY(el, [x, false]);
2096     },
2097
2098     setY : function(el, y) {
2099         this.setXY(el, [false, y]);
2100     }
2101 };
2102 /*
2103  * Portions of this file are based on pieces of Yahoo User Interface Library
2104  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
2105  * YUI licensed under the BSD License:
2106  * http://developer.yahoo.net/yui/license.txt
2107  * <script type="text/javascript">
2108  *
2109  */
2110
2111 Roo.lib.Event = function() {
2112     var loadComplete = false;
2113     var listeners = [];
2114     var unloadListeners = [];
2115     var retryCount = 0;
2116     var onAvailStack = [];
2117     var counter = 0;
2118     var lastError = null;
2119
2120     return {
2121         POLL_RETRYS: 200,
2122         POLL_INTERVAL: 20,
2123         EL: 0,
2124         TYPE: 1,
2125         FN: 2,
2126         WFN: 3,
2127         OBJ: 3,
2128         ADJ_SCOPE: 4,
2129         _interval: null,
2130
2131         startInterval: function() {
2132             if (!this._interval) {
2133                 var self = this;
2134                 var callback = function() {
2135                     self._tryPreloadAttach();
2136                 };
2137                 this._interval = setInterval(callback, this.POLL_INTERVAL);
2138
2139             }
2140         },
2141
2142         onAvailable: function(p_id, p_fn, p_obj, p_override) {
2143             onAvailStack.push({ id:         p_id,
2144                 fn:         p_fn,
2145                 obj:        p_obj,
2146                 override:   p_override,
2147                 checkReady: false    });
2148
2149             retryCount = this.POLL_RETRYS;
2150             this.startInterval();
2151         },
2152
2153
2154         addListener: function(el, eventName, fn) {
2155             el = Roo.getDom(el);
2156             if (!el || !fn) {
2157                 return false;
2158             }
2159
2160             if ("unload" == eventName) {
2161                 unloadListeners[unloadListeners.length] =
2162                 [el, eventName, fn];
2163                 return true;
2164             }
2165
2166             var wrappedFn = function(e) {
2167                 return fn(Roo.lib.Event.getEvent(e));
2168             };
2169
2170             var li = [el, eventName, fn, wrappedFn];
2171
2172             var index = listeners.length;
2173             listeners[index] = li;
2174
2175             this.doAdd(el, eventName, wrappedFn, false);
2176             return true;
2177
2178         },
2179
2180
2181         removeListener: function(el, eventName, fn) {
2182             var i, len;
2183
2184             el = Roo.getDom(el);
2185
2186             if(!fn) {
2187                 return this.purgeElement(el, false, eventName);
2188             }
2189
2190
2191             if ("unload" == eventName) {
2192
2193                 for (i = 0,len = unloadListeners.length; i < len; i++) {
2194                     var li = unloadListeners[i];
2195                     if (li &&
2196                         li[0] == el &&
2197                         li[1] == eventName &&
2198                         li[2] == fn) {
2199                         unloadListeners.splice(i, 1);
2200                         return true;
2201                     }
2202                 }
2203
2204                 return false;
2205             }
2206
2207             var cacheItem = null;
2208
2209
2210             var index = arguments[3];
2211
2212             if ("undefined" == typeof index) {
2213                 index = this._getCacheIndex(el, eventName, fn);
2214             }
2215
2216             if (index >= 0) {
2217                 cacheItem = listeners[index];
2218             }
2219
2220             if (!el || !cacheItem) {
2221                 return false;
2222             }
2223
2224             this.doRemove(el, eventName, cacheItem[this.WFN], false);
2225
2226             delete listeners[index][this.WFN];
2227             delete listeners[index][this.FN];
2228             listeners.splice(index, 1);
2229
2230             return true;
2231
2232         },
2233
2234
2235         getTarget: function(ev, resolveTextNode) {
2236             ev = ev.browserEvent || ev;
2237             ev = ev.touches ? (ev.touches[0] || ev.changedTouches[0] || ev )  : ev;
2238             var t = ev.target || ev.srcElement;
2239             return this.resolveTextNode(t);
2240         },
2241
2242
2243         resolveTextNode: function(node) {
2244             if (Roo.isSafari && node && 3 == node.nodeType) {
2245                 return node.parentNode;
2246             } else {
2247                 return node;
2248             }
2249         },
2250
2251
2252         getPageX: function(ev) {
2253             ev = ev.browserEvent || ev;
2254             ev = ev.touches ? (ev.touches[0] || ev.changedTouches[0] || ev )  : ev;
2255             var x = ev.pageX;
2256             if (!x && 0 !== x) {
2257                 x = ev.clientX || 0;
2258
2259                 if (Roo.isIE) {
2260                     x += this.getScroll()[1];
2261                 }
2262             }
2263
2264             return x;
2265         },
2266
2267
2268         getPageY: function(ev) {
2269             ev = ev.browserEvent || ev;
2270             ev = ev.touches ? (ev.touches[0] || ev.changedTouches[0] || ev )  : ev;
2271             var y = ev.pageY;
2272             if (!y && 0 !== y) {
2273                 y = ev.clientY || 0;
2274
2275                 if (Roo.isIE) {
2276                     y += this.getScroll()[0];
2277                 }
2278             }
2279
2280
2281             return y;
2282         },
2283
2284
2285         getXY: function(ev) {
2286             ev = ev.browserEvent || ev;
2287             ev = ev.touches ? (ev.touches[0] || ev.changedTouches[0] || ev )  : ev;
2288             return [this.getPageX(ev), this.getPageY(ev)];
2289         },
2290
2291
2292         getRelatedTarget: function(ev) {
2293             ev = ev.browserEvent || ev;
2294             ev = ev.touches ? (ev.touches[0] || ev.changedTouches[0] || ev )  : ev;
2295             var t = ev.relatedTarget;
2296             if (!t) {
2297                 if (ev.type == "mouseout") {
2298                     t = ev.toElement;
2299                 } else if (ev.type == "mouseover") {
2300                     t = ev.fromElement;
2301                 }
2302             }
2303
2304             return this.resolveTextNode(t);
2305         },
2306
2307
2308         getTime: function(ev) {
2309             ev = ev.browserEvent || ev;
2310             ev = ev.touches ? (ev.touches[0] || ev.changedTouches[0] || ev )  : ev;
2311             if (!ev.time) {
2312                 var t = new Date().getTime();
2313                 try {
2314                     ev.time = t;
2315                 } catch(ex) {
2316                     this.lastError = ex;
2317                     return t;
2318                 }
2319             }
2320
2321             return ev.time;
2322         },
2323
2324
2325         stopEvent: function(ev) {
2326             this.stopPropagation(ev);
2327             this.preventDefault(ev);
2328         },
2329
2330
2331         stopPropagation: function(ev) {
2332             ev = ev.browserEvent || ev;
2333             if (ev.stopPropagation) {
2334                 ev.stopPropagation();
2335             } else {
2336                 ev.cancelBubble = true;
2337             }
2338         },
2339
2340
2341         preventDefault: function(ev) {
2342             ev = ev.browserEvent || ev;
2343             if(ev.preventDefault) {
2344                 ev.preventDefault();
2345             } else {
2346                 ev.returnValue = false;
2347             }
2348         },
2349
2350
2351         getEvent: function(e) {
2352             var ev = e || window.event;
2353             if (!ev) {
2354                 var c = this.getEvent.caller;
2355                 while (c) {
2356                     ev = c.arguments[0];
2357                     if (ev && Event == ev.constructor) {
2358                         break;
2359                     }
2360                     c = c.caller;
2361                 }
2362             }
2363             return ev;
2364         },
2365
2366
2367         getCharCode: function(ev) {
2368             ev = ev.browserEvent || ev;
2369             return ev.charCode || ev.keyCode || 0;
2370         },
2371
2372
2373         _getCacheIndex: function(el, eventName, fn) {
2374             for (var i = 0,len = listeners.length; i < len; ++i) {
2375                 var li = listeners[i];
2376                 if (li &&
2377                     li[this.FN] == fn &&
2378                     li[this.EL] == el &&
2379                     li[this.TYPE] == eventName) {
2380                     return i;
2381                 }
2382             }
2383
2384             return -1;
2385         },
2386
2387
2388         elCache: {},
2389
2390
2391         getEl: function(id) {
2392             return document.getElementById(id);
2393         },
2394
2395
2396         clearCache: function() {
2397         },
2398
2399
2400         _load: function(e) {
2401             loadComplete = true;
2402             var EU = Roo.lib.Event;
2403
2404
2405             if (Roo.isIE) {
2406                 EU.doRemove(window, "load", EU._load);
2407             }
2408         },
2409
2410
2411         _tryPreloadAttach: function() {
2412
2413             if (this.locked) {
2414                 return false;
2415             }
2416
2417             this.locked = true;
2418
2419
2420             var tryAgain = !loadComplete;
2421             if (!tryAgain) {
2422                 tryAgain = (retryCount > 0);
2423             }
2424
2425
2426             var notAvail = [];
2427             for (var i = 0,len = onAvailStack.length; i < len; ++i) {
2428                 var item = onAvailStack[i];
2429                 if (item) {
2430                     var el = this.getEl(item.id);
2431
2432                     if (el) {
2433                         if (!item.checkReady ||
2434                             loadComplete ||
2435                             el.nextSibling ||
2436                             (document && document.body)) {
2437
2438                             var scope = el;
2439                             if (item.override) {
2440                                 if (item.override === true) {
2441                                     scope = item.obj;
2442                                 } else {
2443                                     scope = item.override;
2444                                 }
2445                             }
2446                             item.fn.call(scope, item.obj);
2447                             onAvailStack[i] = null;
2448                         }
2449                     } else {
2450                         notAvail.push(item);
2451                     }
2452                 }
2453             }
2454
2455             retryCount = (notAvail.length === 0) ? 0 : retryCount - 1;
2456
2457             if (tryAgain) {
2458
2459                 this.startInterval();
2460             } else {
2461                 clearInterval(this._interval);
2462                 this._interval = null;
2463             }
2464
2465             this.locked = false;
2466
2467             return true;
2468
2469         },
2470
2471
2472         purgeElement: function(el, recurse, eventName) {
2473             var elListeners = this.getListeners(el, eventName);
2474             if (elListeners) {
2475                 for (var i = 0,len = elListeners.length; i < len; ++i) {
2476                     var l = elListeners[i];
2477                     this.removeListener(el, l.type, l.fn);
2478                 }
2479             }
2480
2481             if (recurse && el && el.childNodes) {
2482                 for (i = 0,len = el.childNodes.length; i < len; ++i) {
2483                     this.purgeElement(el.childNodes[i], recurse, eventName);
2484                 }
2485             }
2486         },
2487
2488
2489         getListeners: function(el, eventName) {
2490             var results = [], searchLists;
2491             if (!eventName) {
2492                 searchLists = [listeners, unloadListeners];
2493             } else if (eventName == "unload") {
2494                 searchLists = [unloadListeners];
2495             } else {
2496                 searchLists = [listeners];
2497             }
2498
2499             for (var j = 0; j < searchLists.length; ++j) {
2500                 var searchList = searchLists[j];
2501                 if (searchList && searchList.length > 0) {
2502                     for (var i = 0,len = searchList.length; i < len; ++i) {
2503                         var l = searchList[i];
2504                         if (l && l[this.EL] === el &&
2505                             (!eventName || eventName === l[this.TYPE])) {
2506                             results.push({
2507                                 type:   l[this.TYPE],
2508                                 fn:     l[this.FN],
2509                                 obj:    l[this.OBJ],
2510                                 adjust: l[this.ADJ_SCOPE],
2511                                 index:  i
2512                             });
2513                         }
2514                     }
2515                 }
2516             }
2517
2518             return (results.length) ? results : null;
2519         },
2520
2521
2522         _unload: function(e) {
2523
2524             var EU = Roo.lib.Event, i, j, l, len, index;
2525
2526             for (i = 0,len = unloadListeners.length; i < len; ++i) {
2527                 l = unloadListeners[i];
2528                 if (l) {
2529                     var scope = window;
2530                     if (l[EU.ADJ_SCOPE]) {
2531                         if (l[EU.ADJ_SCOPE] === true) {
2532                             scope = l[EU.OBJ];
2533                         } else {
2534                             scope = l[EU.ADJ_SCOPE];
2535                         }
2536                     }
2537                     l[EU.FN].call(scope, EU.getEvent(e), l[EU.OBJ]);
2538                     unloadListeners[i] = null;
2539                     l = null;
2540                     scope = null;
2541                 }
2542             }
2543
2544             unloadListeners = null;
2545
2546             if (listeners && listeners.length > 0) {
2547                 j = listeners.length;
2548                 while (j) {
2549                     index = j - 1;
2550                     l = listeners[index];
2551                     if (l) {
2552                         EU.removeListener(l[EU.EL], l[EU.TYPE],
2553                                 l[EU.FN], index);
2554                     }
2555                     j = j - 1;
2556                 }
2557                 l = null;
2558
2559                 EU.clearCache();
2560             }
2561
2562             EU.doRemove(window, "unload", EU._unload);
2563
2564         },
2565
2566
2567         getScroll: function() {
2568             var dd = document.documentElement, db = document.body;
2569             if (dd && (dd.scrollTop || dd.scrollLeft)) {
2570                 return [dd.scrollTop, dd.scrollLeft];
2571             } else if (db) {
2572                 return [db.scrollTop, db.scrollLeft];
2573             } else {
2574                 return [0, 0];
2575             }
2576         },
2577
2578
2579         doAdd: function () {
2580             if (window.addEventListener) {
2581                 return function(el, eventName, fn, capture) {
2582                     el.addEventListener(eventName, fn, (capture));
2583                 };
2584             } else if (window.attachEvent) {
2585                 return function(el, eventName, fn, capture) {
2586                     el.attachEvent("on" + eventName, fn);
2587                 };
2588             } else {
2589                 return function() {
2590                 };
2591             }
2592         }(),
2593
2594
2595         doRemove: function() {
2596             if (window.removeEventListener) {
2597                 return function (el, eventName, fn, capture) {
2598                     el.removeEventListener(eventName, fn, (capture));
2599                 };
2600             } else if (window.detachEvent) {
2601                 return function (el, eventName, fn) {
2602                     el.detachEvent("on" + eventName, fn);
2603                 };
2604             } else {
2605                 return function() {
2606                 };
2607             }
2608         }()
2609     };
2610     
2611 }();
2612 (function() {     
2613    
2614     var E = Roo.lib.Event;
2615     E.on = E.addListener;
2616     E.un = E.removeListener;
2617
2618     if (document && document.body) {
2619         E._load();
2620     } else {
2621         E.doAdd(window, "load", E._load);
2622     }
2623     E.doAdd(window, "unload", E._unload);
2624     E._tryPreloadAttach();
2625 })();
2626
2627  
2628
2629 (function() {
2630     /**
2631      * @class Roo.lib.Ajax
2632      *
2633      * provide a simple Ajax request utility functions
2634      * 
2635      * Portions of this file are based on pieces of Yahoo User Interface Library
2636     * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
2637     * YUI licensed under the BSD License:
2638     * http://developer.yahoo.net/yui/license.txt
2639     * <script type="text/javascript">
2640     *
2641      *
2642      */
2643     Roo.lib.Ajax = {
2644         /**
2645          * @static 
2646          */
2647         request : function(method, uri, cb, data, options) {
2648             if(options){
2649                 var hs = options.headers;
2650                 if(hs){
2651                     for(var h in hs){
2652                         if(hs.hasOwnProperty(h)){
2653                             this.initHeader(h, hs[h], false);
2654                         }
2655                     }
2656                 }
2657                 if(options.xmlData){
2658                     this.initHeader('Content-Type', 'text/xml', false);
2659                     method = 'POST';
2660                     data = options.xmlData;
2661                 }
2662             }
2663
2664             return this.asyncRequest(method, uri, cb, data);
2665         },
2666         /**
2667          * serialize a form
2668          *
2669          * @static
2670          * @param {DomForm} form element
2671          * @return {String} urlencode form output.
2672          */
2673         serializeForm : function(form) {
2674             if(typeof form == 'string') {
2675                 form = (document.getElementById(form) || document.forms[form]);
2676             }
2677
2678             var el, name, val, disabled, data = '', hasSubmit = false;
2679             for (var i = 0; i < form.elements.length; i++) {
2680                 el = form.elements[i];
2681                 disabled = form.elements[i].disabled;
2682                 name = form.elements[i].name;
2683                 val = form.elements[i].value;
2684
2685                 if (!disabled && name){
2686                     switch (el.type)
2687                             {
2688                         case 'select-one':
2689                         case 'select-multiple':
2690                             for (var j = 0; j < el.options.length; j++) {
2691                                 if (el.options[j].selected) {
2692                                     if (Roo.isIE) {
2693                                         data += Roo.encodeURIComponent(name) + '=' + Roo.encodeURIComponent(el.options[j].attributes['value'].specified ? el.options[j].value : el.options[j].text) + '&';
2694                                     }
2695                                     else {
2696                                         data += Roo.encodeURIComponent(name) + '=' + Roo.encodeURIComponent(el.options[j].hasAttribute('value') ? el.options[j].value : el.options[j].text) + '&';
2697                                     }
2698                                 }
2699                             }
2700                             break;
2701                         case 'radio':
2702                         case 'checkbox':
2703                             if (el.checked) {
2704                                 data += Roo.encodeURIComponent(name) + '=' + Roo.encodeURIComponent(val) + '&';
2705                             }
2706                             break;
2707                         case 'file':
2708
2709                         case undefined:
2710
2711                         case 'reset':
2712
2713                         case 'button':
2714
2715                             break;
2716                         case 'submit':
2717                             if(hasSubmit == false) {
2718                                 data += Roo.encodeURIComponent(name) + '=' + Roo.encodeURIComponent(val) + '&';
2719                                 hasSubmit = true;
2720                             }
2721                             break;
2722                         default:
2723                             data += Roo.encodeURIComponent(name) + '=' + Roo.encodeURIComponent(val) + '&';
2724                             break;
2725                     }
2726                 }
2727             }
2728             data = data.substr(0, data.length - 1);
2729             return data;
2730         },
2731
2732         headers:{},
2733
2734         hasHeaders:false,
2735
2736         useDefaultHeader:true,
2737
2738         defaultPostHeader:'application/x-www-form-urlencoded',
2739
2740         useDefaultXhrHeader:true,
2741
2742         defaultXhrHeader:'XMLHttpRequest',
2743
2744         hasDefaultHeaders:true,
2745
2746         defaultHeaders:{},
2747
2748         poll:{},
2749
2750         timeout:{},
2751
2752         pollInterval:50,
2753
2754         transactionId:0,
2755
2756         setProgId:function(id)
2757         {
2758             this.activeX.unshift(id);
2759         },
2760
2761         setDefaultPostHeader:function(b)
2762         {
2763             this.useDefaultHeader = b;
2764         },
2765
2766         setDefaultXhrHeader:function(b)
2767         {
2768             this.useDefaultXhrHeader = b;
2769         },
2770
2771         setPollingInterval:function(i)
2772         {
2773             if (typeof i == 'number' && isFinite(i)) {
2774                 this.pollInterval = i;
2775             }
2776         },
2777
2778         createXhrObject:function(transactionId)
2779         {
2780             var obj,http;
2781             try
2782             {
2783
2784                 http = new XMLHttpRequest();
2785
2786                 obj = { conn:http, tId:transactionId };
2787             }
2788             catch(e)
2789             {
2790                 for (var i = 0; i < this.activeX.length; ++i) {
2791                     try
2792                     {
2793
2794                         http = new ActiveXObject(this.activeX[i]);
2795
2796                         obj = { conn:http, tId:transactionId };
2797                         break;
2798                     }
2799                     catch(e) {
2800                     }
2801                 }
2802             }
2803             finally
2804             {
2805                 return obj;
2806             }
2807         },
2808
2809         getConnectionObject:function()
2810         {
2811             var o;
2812             var tId = this.transactionId;
2813
2814             try
2815             {
2816                 o = this.createXhrObject(tId);
2817                 if (o) {
2818                     this.transactionId++;
2819                 }
2820             }
2821             catch(e) {
2822             }
2823             finally
2824             {
2825                 return o;
2826             }
2827         },
2828
2829         asyncRequest:function(method, uri, callback, postData)
2830         {
2831             var o = this.getConnectionObject();
2832
2833             if (!o) {
2834                 return null;
2835             }
2836             else {
2837                 o.conn.open(method, uri, true);
2838
2839                 if (this.useDefaultXhrHeader) {
2840                     if (!this.defaultHeaders['X-Requested-With']) {
2841                         this.initHeader('X-Requested-With', this.defaultXhrHeader, true);
2842                     }
2843                 }
2844
2845                 if(postData && this.useDefaultHeader){
2846                     this.initHeader('Content-Type', this.defaultPostHeader);
2847                 }
2848
2849                  if (this.hasDefaultHeaders || this.hasHeaders) {
2850                     this.setHeader(o);
2851                 }
2852
2853                 this.handleReadyState(o, callback);
2854                 o.conn.send(postData || null);
2855
2856                 return o;
2857             }
2858         },
2859
2860         handleReadyState:function(o, callback)
2861         {
2862             var oConn = this;
2863
2864             if (callback && callback.timeout) {
2865                 
2866                 this.timeout[o.tId] = window.setTimeout(function() {
2867                     oConn.abort(o, callback, true);
2868                 }, callback.timeout);
2869             }
2870
2871             this.poll[o.tId] = window.setInterval(
2872                     function() {
2873                         if (o.conn && o.conn.readyState == 4) {
2874                             window.clearInterval(oConn.poll[o.tId]);
2875                             delete oConn.poll[o.tId];
2876
2877                             if(callback && callback.timeout) {
2878                                 window.clearTimeout(oConn.timeout[o.tId]);
2879                                 delete oConn.timeout[o.tId];
2880                             }
2881
2882                             oConn.handleTransactionResponse(o, callback);
2883                         }
2884                     }
2885                     , this.pollInterval);
2886         },
2887
2888         handleTransactionResponse:function(o, callback, isAbort)
2889         {
2890
2891             if (!callback) {
2892                 this.releaseObject(o);
2893                 return;
2894             }
2895
2896             var httpStatus, responseObject;
2897
2898             try
2899             {
2900                 if (o.conn.status !== undefined && o.conn.status != 0) {
2901                     httpStatus = o.conn.status;
2902                 }
2903                 else {
2904                     httpStatus = 13030;
2905                 }
2906             }
2907             catch(e) {
2908
2909
2910                 httpStatus = 13030;
2911             }
2912
2913             if (httpStatus >= 200 && httpStatus < 300) {
2914                 responseObject = this.createResponseObject(o, callback.argument);
2915                 if (callback.success) {
2916                     if (!callback.scope) {
2917                         callback.success(responseObject);
2918                     }
2919                     else {
2920
2921
2922                         callback.success.apply(callback.scope, [responseObject]);
2923                     }
2924                 }
2925             }
2926             else {
2927                 switch (httpStatus) {
2928
2929                     case 12002:
2930                     case 12029:
2931                     case 12030:
2932                     case 12031:
2933                     case 12152:
2934                     case 13030:
2935                         responseObject = this.createExceptionObject(o.tId, callback.argument, (isAbort ? isAbort : false));
2936                         if (callback.failure) {
2937                             if (!callback.scope) {
2938                                 callback.failure(responseObject);
2939                             }
2940                             else {
2941                                 callback.failure.apply(callback.scope, [responseObject]);
2942                             }
2943                         }
2944                         break;
2945                     default:
2946                         responseObject = this.createResponseObject(o, callback.argument);
2947                         if (callback.failure) {
2948                             if (!callback.scope) {
2949                                 callback.failure(responseObject);
2950                             }
2951                             else {
2952                                 callback.failure.apply(callback.scope, [responseObject]);
2953                             }
2954                         }
2955                 }
2956             }
2957
2958             this.releaseObject(o);
2959             responseObject = null;
2960         },
2961
2962         createResponseObject:function(o, callbackArg)
2963         {
2964             var obj = {};
2965             var headerObj = {};
2966
2967             try
2968             {
2969                 var headerStr = o.conn.getAllResponseHeaders();
2970                 var header = headerStr.split('\n');
2971                 for (var i = 0; i < header.length; i++) {
2972                     var delimitPos = header[i].indexOf(':');
2973                     if (delimitPos != -1) {
2974                         headerObj[header[i].substring(0, delimitPos)] = header[i].substring(delimitPos + 2);
2975                     }
2976                 }
2977             }
2978             catch(e) {
2979             }
2980
2981             obj.tId = o.tId;
2982             obj.status = o.conn.status;
2983             obj.statusText = o.conn.statusText;
2984             obj.getResponseHeader = headerObj;
2985             obj.getAllResponseHeaders = headerStr;
2986             obj.responseText = o.conn.responseText;
2987             obj.responseXML = o.conn.responseXML;
2988
2989             if (typeof callbackArg !== undefined) {
2990                 obj.argument = callbackArg;
2991             }
2992
2993             return obj;
2994         },
2995
2996         createExceptionObject:function(tId, callbackArg, isAbort)
2997         {
2998             var COMM_CODE = 0;
2999             var COMM_ERROR = 'communication failure';
3000             var ABORT_CODE = -1;
3001             var ABORT_ERROR = 'transaction aborted';
3002
3003             var obj = {};
3004
3005             obj.tId = tId;
3006             if (isAbort) {
3007                 obj.status = ABORT_CODE;
3008                 obj.statusText = ABORT_ERROR;
3009             }
3010             else {
3011                 obj.status = COMM_CODE;
3012                 obj.statusText = COMM_ERROR;
3013             }
3014
3015             if (callbackArg) {
3016                 obj.argument = callbackArg;
3017             }
3018
3019             return obj;
3020         },
3021
3022         initHeader:function(label, value, isDefault)
3023         {
3024             var headerObj = (isDefault) ? this.defaultHeaders : this.headers;
3025
3026             if (headerObj[label] === undefined) {
3027                 headerObj[label] = value;
3028             }
3029             else {
3030
3031
3032                 headerObj[label] = value + "," + headerObj[label];
3033             }
3034
3035             if (isDefault) {
3036                 this.hasDefaultHeaders = true;
3037             }
3038             else {
3039                 this.hasHeaders = true;
3040             }
3041         },
3042
3043
3044         setHeader:function(o)
3045         {
3046             if (this.hasDefaultHeaders) {
3047                 for (var prop in this.defaultHeaders) {
3048                     if (this.defaultHeaders.hasOwnProperty(prop)) {
3049                         o.conn.setRequestHeader(prop, this.defaultHeaders[prop]);
3050                     }
3051                 }
3052             }
3053
3054             if (this.hasHeaders) {
3055                 for (var prop in this.headers) {
3056                     if (this.headers.hasOwnProperty(prop)) {
3057                         o.conn.setRequestHeader(prop, this.headers[prop]);
3058                     }
3059                 }
3060                 this.headers = {};
3061                 this.hasHeaders = false;
3062             }
3063         },
3064
3065         resetDefaultHeaders:function() {
3066             delete this.defaultHeaders;
3067             this.defaultHeaders = {};
3068             this.hasDefaultHeaders = false;
3069         },
3070
3071         abort:function(o, callback, isTimeout)
3072         {
3073             if(this.isCallInProgress(o)) {
3074                 o.conn.abort();
3075                 window.clearInterval(this.poll[o.tId]);
3076                 delete this.poll[o.tId];
3077                 if (isTimeout) {
3078                     delete this.timeout[o.tId];
3079                 }
3080
3081                 this.handleTransactionResponse(o, callback, true);
3082
3083                 return true;
3084             }
3085             else {
3086                 return false;
3087             }
3088         },
3089
3090
3091         isCallInProgress:function(o)
3092         {
3093             if (o && o.conn) {
3094                 return o.conn.readyState != 4 && o.conn.readyState != 0;
3095             }
3096             else {
3097
3098                 return false;
3099             }
3100         },
3101
3102
3103         releaseObject:function(o)
3104         {
3105
3106             o.conn = null;
3107
3108             o = null;
3109         },
3110
3111         activeX:[
3112         'MSXML2.XMLHTTP.3.0',
3113         'MSXML2.XMLHTTP',
3114         'Microsoft.XMLHTTP'
3115         ]
3116
3117
3118     };
3119 })();/*
3120  * Portions of this file are based on pieces of Yahoo User Interface Library
3121  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3122  * YUI licensed under the BSD License:
3123  * http://developer.yahoo.net/yui/license.txt
3124  * <script type="text/javascript">
3125  *
3126  */
3127
3128 Roo.lib.Region = function(t, r, b, l) {
3129     this.top = t;
3130     this[1] = t;
3131     this.right = r;
3132     this.bottom = b;
3133     this.left = l;
3134     this[0] = l;
3135 };
3136
3137
3138 Roo.lib.Region.prototype = {
3139     contains : function(region) {
3140         return ( region.left >= this.left &&
3141                  region.right <= this.right &&
3142                  region.top >= this.top &&
3143                  region.bottom <= this.bottom    );
3144
3145     },
3146
3147     getArea : function() {
3148         return ( (this.bottom - this.top) * (this.right - this.left) );
3149     },
3150
3151     intersect : function(region) {
3152         var t = Math.max(this.top, region.top);
3153         var r = Math.min(this.right, region.right);
3154         var b = Math.min(this.bottom, region.bottom);
3155         var l = Math.max(this.left, region.left);
3156
3157         if (b >= t && r >= l) {
3158             return new Roo.lib.Region(t, r, b, l);
3159         } else {
3160             return null;
3161         }
3162     },
3163     union : function(region) {
3164         var t = Math.min(this.top, region.top);
3165         var r = Math.max(this.right, region.right);
3166         var b = Math.max(this.bottom, region.bottom);
3167         var l = Math.min(this.left, region.left);
3168
3169         return new Roo.lib.Region(t, r, b, l);
3170     },
3171
3172     adjust : function(t, l, b, r) {
3173         this.top += t;
3174         this.left += l;
3175         this.right += r;
3176         this.bottom += b;
3177         return this;
3178     }
3179 };
3180
3181 Roo.lib.Region.getRegion = function(el) {
3182     var p = Roo.lib.Dom.getXY(el);
3183
3184     var t = p[1];
3185     var r = p[0] + el.offsetWidth;
3186     var b = p[1] + el.offsetHeight;
3187     var l = p[0];
3188
3189     return new Roo.lib.Region(t, r, b, l);
3190 };
3191 /*
3192  * Portions of this file are based on pieces of Yahoo User Interface Library
3193  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3194  * YUI licensed under the BSD License:
3195  * http://developer.yahoo.net/yui/license.txt
3196  * <script type="text/javascript">
3197  *
3198  */
3199 //@@dep Roo.lib.Region
3200
3201
3202 Roo.lib.Point = function(x, y) {
3203     if (x instanceof Array) {
3204         y = x[1];
3205         x = x[0];
3206     }
3207     this.x = this.right = this.left = this[0] = x;
3208     this.y = this.top = this.bottom = this[1] = y;
3209 };
3210
3211 Roo.lib.Point.prototype = new Roo.lib.Region();
3212 /*
3213  * Portions of this file are based on pieces of Yahoo User Interface Library
3214  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3215  * YUI licensed under the BSD License:
3216  * http://developer.yahoo.net/yui/license.txt
3217  * <script type="text/javascript">
3218  *
3219  */
3220  
3221 (function() {   
3222
3223     Roo.lib.Anim = {
3224         scroll : function(el, args, duration, easing, cb, scope) {
3225             this.run(el, args, duration, easing, cb, scope, Roo.lib.Scroll);
3226         },
3227
3228         motion : function(el, args, duration, easing, cb, scope) {
3229             this.run(el, args, duration, easing, cb, scope, Roo.lib.Motion);
3230         },
3231
3232         color : function(el, args, duration, easing, cb, scope) {
3233             this.run(el, args, duration, easing, cb, scope, Roo.lib.ColorAnim);
3234         },
3235
3236         run : function(el, args, duration, easing, cb, scope, type) {
3237             type = type || Roo.lib.AnimBase;
3238             if (typeof easing == "string") {
3239                 easing = Roo.lib.Easing[easing];
3240             }
3241             var anim = new type(el, args, duration, easing);
3242             anim.animateX(function() {
3243                 Roo.callback(cb, scope);
3244             });
3245             return anim;
3246         }
3247     };
3248 })();/*
3249  * Portions of this file are based on pieces of Yahoo User Interface Library
3250  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3251  * YUI licensed under the BSD License:
3252  * http://developer.yahoo.net/yui/license.txt
3253  * <script type="text/javascript">
3254  *
3255  */
3256
3257 (function() {    
3258     var libFlyweight;
3259     
3260     function fly(el) {
3261         if (!libFlyweight) {
3262             libFlyweight = new Roo.Element.Flyweight();
3263         }
3264         libFlyweight.dom = el;
3265         return libFlyweight;
3266     }
3267
3268     // since this uses fly! - it cant be in DOM (which does not have fly yet..)
3269     
3270    
3271     
3272     Roo.lib.AnimBase = function(el, attributes, duration, method) {
3273         if (el) {
3274             this.init(el, attributes, duration, method);
3275         }
3276     };
3277
3278     Roo.lib.AnimBase.fly = fly;
3279     
3280     
3281     
3282     Roo.lib.AnimBase.prototype = {
3283
3284         toString: function() {
3285             var el = this.getEl();
3286             var id = el.id || el.tagName;
3287             return ("Anim " + id);
3288         },
3289
3290         patterns: {
3291             noNegatives:        /width|height|opacity|padding/i,
3292             offsetAttribute:  /^((width|height)|(top|left))$/,
3293             defaultUnit:        /width|height|top$|bottom$|left$|right$/i,
3294             offsetUnit:         /\d+(em|%|en|ex|pt|in|cm|mm|pc)$/i
3295         },
3296
3297
3298         doMethod: function(attr, start, end) {
3299             return this.method(this.currentFrame, start, end - start, this.totalFrames);
3300         },
3301
3302
3303         setAttribute: function(attr, val, unit) {
3304             if (this.patterns.noNegatives.test(attr)) {
3305                 val = (val > 0) ? val : 0;
3306             }
3307
3308             Roo.fly(this.getEl(), '_anim').setStyle(attr, val + unit);
3309         },
3310
3311
3312         getAttribute: function(attr) {
3313             var el = this.getEl();
3314             var val = fly(el).getStyle(attr);
3315
3316             if (val !== 'auto' && !this.patterns.offsetUnit.test(val)) {
3317                 return parseFloat(val);
3318             }
3319
3320             var a = this.patterns.offsetAttribute.exec(attr) || [];
3321             var pos = !!( a[3] );
3322             var box = !!( a[2] );
3323
3324
3325             if (box || (fly(el).getStyle('position') == 'absolute' && pos)) {
3326                 val = el['offset' + a[0].charAt(0).toUpperCase() + a[0].substr(1)];
3327             } else {
3328                 val = 0;
3329             }
3330
3331             return val;
3332         },
3333
3334
3335         getDefaultUnit: function(attr) {
3336             if (this.patterns.defaultUnit.test(attr)) {
3337                 return 'px';
3338             }
3339
3340             return '';
3341         },
3342
3343         animateX : function(callback, scope) {
3344             var f = function() {
3345                 this.onComplete.removeListener(f);
3346                 if (typeof callback == "function") {
3347                     callback.call(scope || this, this);
3348                 }
3349             };
3350             this.onComplete.addListener(f, this);
3351             this.animate();
3352         },
3353
3354
3355         setRuntimeAttribute: function(attr) {
3356             var start;
3357             var end;
3358             var attributes = this.attributes;
3359
3360             this.runtimeAttributes[attr] = {};
3361
3362             var isset = function(prop) {
3363                 return (typeof prop !== 'undefined');
3364             };
3365
3366             if (!isset(attributes[attr]['to']) && !isset(attributes[attr]['by'])) {
3367                 return false;
3368             }
3369
3370             start = ( isset(attributes[attr]['from']) ) ? attributes[attr]['from'] : this.getAttribute(attr);
3371
3372
3373             if (isset(attributes[attr]['to'])) {
3374                 end = attributes[attr]['to'];
3375             } else if (isset(attributes[attr]['by'])) {
3376                 if (start.constructor == Array) {
3377                     end = [];
3378                     for (var i = 0, len = start.length; i < len; ++i) {
3379                         end[i] = start[i] + attributes[attr]['by'][i];
3380                     }
3381                 } else {
3382                     end = start + attributes[attr]['by'];
3383                 }
3384             }
3385
3386             this.runtimeAttributes[attr].start = start;
3387             this.runtimeAttributes[attr].end = end;
3388
3389
3390             this.runtimeAttributes[attr].unit = ( isset(attributes[attr].unit) ) ? attributes[attr]['unit'] : this.getDefaultUnit(attr);
3391         },
3392
3393
3394         init: function(el, attributes, duration, method) {
3395
3396             var isAnimated = false;
3397
3398
3399             var startTime = null;
3400
3401
3402             var actualFrames = 0;
3403
3404
3405             el = Roo.getDom(el);
3406
3407
3408             this.attributes = attributes || {};
3409
3410
3411             this.duration = duration || 1;
3412
3413
3414             this.method = method || Roo.lib.Easing.easeNone;
3415
3416
3417             this.useSeconds = true;
3418
3419
3420             this.currentFrame = 0;
3421
3422
3423             this.totalFrames = Roo.lib.AnimMgr.fps;
3424
3425
3426             this.getEl = function() {
3427                 return el;
3428             };
3429
3430
3431             this.isAnimated = function() {
3432                 return isAnimated;
3433             };
3434
3435
3436             this.getStartTime = function() {
3437                 return startTime;
3438             };
3439
3440             this.runtimeAttributes = {};
3441
3442
3443             this.animate = function() {
3444                 if (this.isAnimated()) {
3445                     return false;
3446                 }
3447
3448                 this.currentFrame = 0;
3449
3450                 this.totalFrames = ( this.useSeconds ) ? Math.ceil(Roo.lib.AnimMgr.fps * this.duration) : this.duration;
3451
3452                 Roo.lib.AnimMgr.registerElement(this);
3453             };
3454
3455
3456             this.stop = function(finish) {
3457                 if (finish) {
3458                     this.currentFrame = this.totalFrames;
3459                     this._onTween.fire();
3460                 }
3461                 Roo.lib.AnimMgr.stop(this);
3462             };
3463
3464             var onStart = function() {
3465                 this.onStart.fire();
3466
3467                 this.runtimeAttributes = {};
3468                 for (var attr in this.attributes) {
3469                     this.setRuntimeAttribute(attr);
3470                 }
3471
3472                 isAnimated = true;
3473                 actualFrames = 0;
3474                 startTime = new Date();
3475             };
3476
3477
3478             var onTween = function() {
3479                 var data = {
3480                     duration: new Date() - this.getStartTime(),
3481                     currentFrame: this.currentFrame
3482                 };
3483
3484                 data.toString = function() {
3485                     return (
3486                             'duration: ' + data.duration +
3487                             ', currentFrame: ' + data.currentFrame
3488                             );
3489                 };
3490
3491                 this.onTween.fire(data);
3492
3493                 var runtimeAttributes = this.runtimeAttributes;
3494
3495                 for (var attr in runtimeAttributes) {
3496                     this.setAttribute(attr, this.doMethod(attr, runtimeAttributes[attr].start, runtimeAttributes[attr].end), runtimeAttributes[attr].unit);
3497                 }
3498
3499                 actualFrames += 1;
3500             };
3501
3502             var onComplete = function() {
3503                 var actual_duration = (new Date() - startTime) / 1000 ;
3504
3505                 var data = {
3506                     duration: actual_duration,
3507                     frames: actualFrames,
3508                     fps: actualFrames / actual_duration
3509                 };
3510
3511                 data.toString = function() {
3512                     return (
3513                             'duration: ' + data.duration +
3514                             ', frames: ' + data.frames +
3515                             ', fps: ' + data.fps
3516                             );
3517                 };
3518
3519                 isAnimated = false;
3520                 actualFrames = 0;
3521                 this.onComplete.fire(data);
3522             };
3523
3524
3525             this._onStart = new Roo.util.Event(this);
3526             this.onStart = new Roo.util.Event(this);
3527             this.onTween = new Roo.util.Event(this);
3528             this._onTween = new Roo.util.Event(this);
3529             this.onComplete = new Roo.util.Event(this);
3530             this._onComplete = new Roo.util.Event(this);
3531             this._onStart.addListener(onStart);
3532             this._onTween.addListener(onTween);
3533             this._onComplete.addListener(onComplete);
3534         }
3535     };
3536 })();
3537 /*
3538  * Portions of this file are based on pieces of Yahoo User Interface Library
3539  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3540  * YUI licensed under the BSD License:
3541  * http://developer.yahoo.net/yui/license.txt
3542  * <script type="text/javascript">
3543  *
3544  */
3545
3546 Roo.lib.AnimMgr = new function() {
3547
3548     var thread = null;
3549
3550
3551     var queue = [];
3552
3553
3554     var tweenCount = 0;
3555
3556
3557     this.fps = 1000;
3558
3559
3560     this.delay = 1;
3561
3562
3563     this.registerElement = function(tween) {
3564         queue[queue.length] = tween;
3565         tweenCount += 1;
3566         tween._onStart.fire();
3567         this.start();
3568     };
3569
3570
3571     this.unRegister = function(tween, index) {
3572         tween._onComplete.fire();
3573         index = index || getIndex(tween);
3574         if (index != -1) {
3575             queue.splice(index, 1);
3576         }
3577
3578         tweenCount -= 1;
3579         if (tweenCount <= 0) {
3580             this.stop();
3581         }
3582     };
3583
3584
3585     this.start = function() {
3586         if (thread === null) {
3587             thread = setInterval(this.run, this.delay);
3588         }
3589     };
3590
3591
3592     this.stop = function(tween) {
3593         if (!tween) {
3594             clearInterval(thread);
3595
3596             for (var i = 0, len = queue.length; i < len; ++i) {
3597                 if (queue[0].isAnimated()) {
3598                     this.unRegister(queue[0], 0);
3599                 }
3600             }
3601
3602             queue = [];
3603             thread = null;
3604             tweenCount = 0;
3605         }
3606         else {
3607             this.unRegister(tween);
3608         }
3609     };
3610
3611
3612     this.run = function() {
3613         for (var i = 0, len = queue.length; i < len; ++i) {
3614             var tween = queue[i];
3615             if (!tween || !tween.isAnimated()) {
3616                 continue;
3617             }
3618
3619             if (tween.currentFrame < tween.totalFrames || tween.totalFrames === null)
3620             {
3621                 tween.currentFrame += 1;
3622
3623                 if (tween.useSeconds) {
3624                     correctFrame(tween);
3625                 }
3626                 tween._onTween.fire();
3627             }
3628             else {
3629                 Roo.lib.AnimMgr.stop(tween, i);
3630             }
3631         }
3632     };
3633
3634     var getIndex = function(anim) {
3635         for (var i = 0, len = queue.length; i < len; ++i) {
3636             if (queue[i] == anim) {
3637                 return i;
3638             }
3639         }
3640         return -1;
3641     };
3642
3643
3644     var correctFrame = function(tween) {
3645         var frames = tween.totalFrames;
3646         var frame = tween.currentFrame;
3647         var expected = (tween.currentFrame * tween.duration * 1000 / tween.totalFrames);
3648         var elapsed = (new Date() - tween.getStartTime());
3649         var tweak = 0;
3650
3651         if (elapsed < tween.duration * 1000) {
3652             tweak = Math.round((elapsed / expected - 1) * tween.currentFrame);
3653         } else {
3654             tweak = frames - (frame + 1);
3655         }
3656         if (tweak > 0 && isFinite(tweak)) {
3657             if (tween.currentFrame + tweak >= frames) {
3658                 tweak = frames - (frame + 1);
3659             }
3660
3661             tween.currentFrame += tweak;
3662         }
3663     };
3664 };
3665
3666     /*
3667  * Portions of this file are based on pieces of Yahoo User Interface Library
3668  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3669  * YUI licensed under the BSD License:
3670  * http://developer.yahoo.net/yui/license.txt
3671  * <script type="text/javascript">
3672  *
3673  */
3674 Roo.lib.Bezier = new function() {
3675
3676         this.getPosition = function(points, t) {
3677             var n = points.length;
3678             var tmp = [];
3679
3680             for (var i = 0; i < n; ++i) {
3681                 tmp[i] = [points[i][0], points[i][1]];
3682             }
3683
3684             for (var j = 1; j < n; ++j) {
3685                 for (i = 0; i < n - j; ++i) {
3686                     tmp[i][0] = (1 - t) * tmp[i][0] + t * tmp[parseInt(i + 1, 10)][0];
3687                     tmp[i][1] = (1 - t) * tmp[i][1] + t * tmp[parseInt(i + 1, 10)][1];
3688                 }
3689             }
3690
3691             return [ tmp[0][0], tmp[0][1] ];
3692
3693         };
3694     }; 
3695
3696 /**
3697  * @class Roo.lib.Color
3698  * @constructor
3699  * An abstract Color implementation. Concrete Color implementations should use
3700  * an instance of this function as their prototype, and implement the getRGB and
3701  * getHSL functions. getRGB should return an object representing the RGB
3702  * components of this Color, with the red, green, and blue components in the
3703  * range [0,255] and the alpha component in the range [0,100]. getHSL should
3704  * return an object representing the HSL components of this Color, with the hue
3705  * component in the range [0,360), the saturation and lightness components in
3706  * the range [0,100], and the alpha component in the range [0,1].
3707  *
3708  *
3709  * Color.js
3710  *
3711  * Functions for Color handling and processing.
3712  *
3713  * http://www.safalra.com/web-design/javascript/Color-handling-and-processing/
3714  *
3715  * The author of this program, Safalra (Stephen Morley), irrevocably releases all
3716  * rights to this program, with the intention of it becoming part of the public
3717  * domain. Because this program is released into the public domain, it comes with
3718  * no warranty either expressed or implied, to the extent permitted by law.
3719  * 
3720  * For more free and public domain JavaScript code by the same author, visit:
3721  * http://www.safalra.com/web-design/javascript/
3722  * 
3723  */
3724 Roo.lib.Color = function() { }
3725
3726
3727 Roo.apply(Roo.lib.Color.prototype, {
3728   
3729   rgb : null,
3730   hsv : null,
3731   hsl : null,
3732   
3733   /**
3734    * getIntegerRGB
3735    * @return {Object} an object representing the RGBA components of this Color. The red,
3736    * green, and blue components are converted to integers in the range [0,255].
3737    * The alpha is a value in the range [0,1].
3738    */
3739   getIntegerRGB : function(){
3740
3741     // get the RGB components of this Color
3742     var rgb = this.getRGB();
3743
3744     // return the integer components
3745     return {
3746       'r' : Math.round(rgb.r),
3747       'g' : Math.round(rgb.g),
3748       'b' : Math.round(rgb.b),
3749       'a' : rgb.a
3750     };
3751
3752   },
3753
3754   /**
3755    * getPercentageRGB
3756    * @return {Object} an object representing the RGBA components of this Color. The red,
3757    * green, and blue components are converted to numbers in the range [0,100].
3758    * The alpha is a value in the range [0,1].
3759    */
3760   getPercentageRGB : function(){
3761
3762     // get the RGB components of this Color
3763     var rgb = this.getRGB();
3764
3765     // return the percentage components
3766     return {
3767       'r' : 100 * rgb.r / 255,
3768       'g' : 100 * rgb.g / 255,
3769       'b' : 100 * rgb.b / 255,
3770       'a' : rgb.a
3771     };
3772
3773   },
3774
3775   /**
3776    * getCSSHexadecimalRGB
3777    * @return {String} a string representing this Color as a CSS hexadecimal RGB Color
3778    * value - that is, a string of the form #RRGGBB where each of RR, GG, and BB
3779    * are two-digit hexadecimal numbers.
3780    */
3781   getCSSHexadecimalRGB : function()
3782   {
3783
3784     // get the integer RGB components
3785     var rgb = this.getIntegerRGB();
3786
3787     // determine the hexadecimal equivalents
3788     var r16 = rgb.r.toString(16);
3789     var g16 = rgb.g.toString(16);
3790     var b16 = rgb.b.toString(16);
3791
3792     // return the CSS RGB Color value
3793     return '#'
3794         + (r16.length == 2 ? r16 : '0' + r16)
3795         + (g16.length == 2 ? g16 : '0' + g16)
3796         + (b16.length == 2 ? b16 : '0' + b16);
3797
3798   },
3799
3800   /**
3801    * getCSSIntegerRGB
3802    * @return {String} a string representing this Color as a CSS integer RGB Color
3803    * value - that is, a string of the form rgb(r,g,b) where each of r, g, and b
3804    * are integers in the range [0,255].
3805    */
3806   getCSSIntegerRGB : function(){
3807
3808     // get the integer RGB components
3809     var rgb = this.getIntegerRGB();
3810
3811     // return the CSS RGB Color value
3812     return 'rgb(' + rgb.r + ',' + rgb.g + ',' + rgb.b + ')';
3813
3814   },
3815
3816   /**
3817    * getCSSIntegerRGBA
3818    * @return {String} Returns a string representing this Color as a CSS integer RGBA Color
3819    * value - that is, a string of the form rgba(r,g,b,a) where each of r, g, and
3820    * b are integers in the range [0,255] and a is in the range [0,1].
3821    */
3822   getCSSIntegerRGBA : function(){
3823
3824     // get the integer RGB components
3825     var rgb = this.getIntegerRGB();
3826
3827     // return the CSS integer RGBA Color value
3828     return 'rgb(' + rgb.r + ',' + rgb.g + ',' + rgb.b + ',' + rgb.a + ')';
3829
3830   },
3831
3832   /**
3833    * getCSSPercentageRGB
3834    * @return {String} a string representing this Color as a CSS percentage RGB Color
3835    * value - that is, a string of the form rgb(r%,g%,b%) where each of r, g, and
3836    * b are in the range [0,100].
3837    */
3838   getCSSPercentageRGB : function(){
3839
3840     // get the percentage RGB components
3841     var rgb = this.getPercentageRGB();
3842
3843     // return the CSS RGB Color value
3844     return 'rgb(' + rgb.r + '%,' + rgb.g + '%,' + rgb.b + '%)';
3845
3846   },
3847
3848   /**
3849    * getCSSPercentageRGBA
3850    * @return {String} a string representing this Color as a CSS percentage RGBA Color
3851    * value - that is, a string of the form rgba(r%,g%,b%,a) where each of r, g,
3852    * and b are in the range [0,100] and a is in the range [0,1].
3853    */
3854   getCSSPercentageRGBA : function(){
3855
3856     // get the percentage RGB components
3857     var rgb = this.getPercentageRGB();
3858
3859     // return the CSS percentage RGBA Color value
3860     return 'rgb(' + rgb.r + '%,' + rgb.g + '%,' + rgb.b + '%,' + rgb.a + ')';
3861
3862   },
3863
3864   /**
3865    * getCSSHSL
3866    * @return {String} a string representing this Color as a CSS HSL Color value - that
3867    * is, a string of the form hsl(h,s%,l%) where h is in the range [0,100] and
3868    * s and l are in the range [0,100].
3869    */
3870   getCSSHSL : function(){
3871
3872     // get the HSL components
3873     var hsl = this.getHSL();
3874
3875     // return the CSS HSL Color value
3876     return 'hsl(' + hsl.h + ',' + hsl.s + '%,' + hsl.l + '%)';
3877
3878   },
3879
3880   /**
3881    * getCSSHSLA
3882    * @return {String} a string representing this Color as a CSS HSLA Color value - that
3883    * is, a string of the form hsla(h,s%,l%,a) where h is in the range [0,100],
3884    * s and l are in the range [0,100], and a is in the range [0,1].
3885    */
3886   getCSSHSLA : function(){
3887
3888     // get the HSL components
3889     var hsl = this.getHSL();
3890
3891     // return the CSS HSL Color value
3892     return 'hsl(' + hsl.h + ',' + hsl.s + '%,' + hsl.l + '%,' + hsl.a + ')';
3893
3894   },
3895
3896   /**
3897    * Sets the Color of the specified node to this Color. This functions sets
3898    * the CSS 'color' property for the node. The parameter is:
3899    * 
3900    * @param {DomElement} node - the node whose Color should be set
3901    */
3902   setNodeColor : function(node){
3903
3904     // set the Color of the node
3905     node.style.color = this.getCSSHexadecimalRGB();
3906
3907   },
3908
3909   /**
3910    * Sets the background Color of the specified node to this Color. This
3911    * functions sets the CSS 'background-color' property for the node. The
3912    * parameter is:
3913    *
3914    * @param {DomElement} node - the node whose background Color should be set
3915    */
3916   setNodeBackgroundColor : function(node){
3917
3918     // set the background Color of the node
3919     node.style.backgroundColor = this.getCSSHexadecimalRGB();
3920
3921   },
3922   // convert between formats..
3923   toRGB: function()
3924   {
3925     var r = this.getIntegerRGB();
3926     return new Roo.lib.RGBColor(r.r,r.g,r.b,r.a);
3927     
3928   },
3929   toHSL : function()
3930   {
3931      var hsl = this.getHSL();
3932   // return the CSS HSL Color value
3933     return new Roo.lib.HSLColor(hsl.h,  hsl.s, hsl.l ,  hsl.a );
3934     
3935   },
3936   
3937   toHSV : function()
3938   {
3939     var rgb = this.toRGB();
3940     var hsv = rgb.getHSV();
3941    // return the CSS HSL Color value
3942     return new Roo.lib.HSVColor(hsv.h,  hsv.s, hsv.v ,  hsv.a );
3943     
3944   },
3945   
3946   // modify  v = 0 ... 1 (eg. 0.5)
3947   saturate : function(v)
3948   {
3949       var rgb = this.toRGB();
3950       var hsv = rgb.getHSV();
3951       return new Roo.lib.HSVColor(hsv.h,  hsv.s * v, hsv.v ,  hsv.a );
3952       
3953   
3954   },
3955   
3956    
3957   /**
3958    * getRGB
3959    * @return {Object} the RGB and alpha components of this Color as an object with r,
3960    * g, b, and a properties. r, g, and b are in the range [0,255] and a is in
3961    * the range [0,1].
3962    */
3963   getRGB: function(){
3964    
3965     // return the RGB components
3966     return {
3967       'r' : this.rgb.r,
3968       'g' : this.rgb.g,
3969       'b' : this.rgb.b,
3970       'a' : this.alpha
3971     };
3972
3973   },
3974
3975   /**
3976    * getHSV
3977    * @return {Object} the HSV and alpha components of this Color as an object with h,
3978    * s, v, and a properties. h is in the range [0,360), s and v are in the range
3979    * [0,100], and a is in the range [0,1].
3980    */
3981   getHSV : function()
3982   {
3983     
3984     // calculate the HSV components if necessary
3985     if (this.hsv == null) {
3986       this.calculateHSV();
3987     }
3988
3989     // return the HSV components
3990     return {
3991       'h' : this.hsv.h,
3992       's' : this.hsv.s,
3993       'v' : this.hsv.v,
3994       'a' : this.alpha
3995     };
3996
3997   },
3998
3999   /**
4000    * getHSL
4001    * @return {Object} the HSL and alpha components of this Color as an object with h,
4002    * s, l, and a properties. h is in the range [0,360), s and l are in the range
4003    * [0,100], and a is in the range [0,1].
4004    */
4005   getHSL : function(){
4006     
4007      
4008     // calculate the HSV components if necessary
4009     if (this.hsl == null) { this.calculateHSL(); }
4010
4011     // return the HSL components
4012     return {
4013       'h' : this.hsl.h,
4014       's' : this.hsl.s,
4015       'l' : this.hsl.l,
4016       'a' : this.alpha
4017     };
4018
4019   }
4020   
4021
4022 });
4023
4024
4025 /**
4026  * @class Roo.lib.RGBColor
4027  * @extends Roo.lib.Color
4028  * Creates a Color specified in the RGB Color space, with an optional alpha
4029  * component. The parameters are:
4030  * @constructor
4031  * 
4032
4033  * @param {Number} r - the red component, clipped to the range [0,255]
4034  * @param {Number} g - the green component, clipped to the range [0,255]
4035  * @param {Number} b - the blue component, clipped to the range [0,255]
4036  * @param {Number} a - the alpha component, clipped to the range [0,1] - this parameter is
4037  *     optional and defaults to 1
4038  */
4039 Roo.lib.RGBColor = function (r, g, b, a){
4040
4041   // store the alpha component after clipping it if necessary
4042   this.alpha = (a === undefined ? 1 : Math.max(0, Math.min(1, a)));
4043
4044   // store the RGB components after clipping them if necessary
4045   this.rgb =
4046       {
4047         'r' : Math.max(0, Math.min(255, r)),
4048         'g' : Math.max(0, Math.min(255, g)),
4049         'b' : Math.max(0, Math.min(255, b))
4050       };
4051
4052   // initialise the HSV and HSL components to null
4053   
4054
4055   /* 
4056    * //private returns the HSV or HSL hue component of this RGBColor. The hue is in the
4057    * range [0,360). The parameters are:
4058    *
4059    * maximum - the maximum of the RGB component values
4060    * range   - the range of the RGB component values
4061    */
4062    
4063
4064 }
4065 // this does an 'exteds'
4066 Roo.extend(Roo.lib.RGBColor, Roo.lib.Color, {
4067
4068   
4069     getHue  : function(maximum, range)
4070     {
4071       var rgb = this.rgb;
4072        
4073       // check whether the range is zero
4074       if (range == 0){
4075   
4076         // set the hue to zero (any hue is acceptable as the Color is grey)
4077         var hue = 0;
4078   
4079       }else{
4080   
4081         // determine which of the components has the highest value and set the hue
4082         switch (maximum){
4083   
4084           // red has the highest value
4085           case rgb.r:
4086             var hue = (rgb.g - rgb.b) / range * 60;
4087             if (hue < 0) { hue += 360; }
4088             break;
4089   
4090           // green has the highest value
4091           case rgb.g:
4092             var hue = (rgb.b - rgb.r) / range * 60 + 120;
4093             break;
4094   
4095           // blue has the highest value
4096           case rgb.b:
4097             var hue = (rgb.r - rgb.g) / range * 60 + 240;
4098             break;
4099   
4100         }
4101   
4102       }
4103   
4104       // return the hue
4105       return hue;
4106   
4107     },
4108
4109   /* //private Calculates and stores the HSV components of this RGBColor so that they can
4110    * be returned be the getHSV function.
4111    */
4112    calculateHSV : function(){
4113     var rgb = this.rgb;
4114     // get the maximum and range of the RGB component values
4115     var maximum = Math.max(rgb.r, rgb.g, rgb.b);
4116     var range   = maximum - Math.min(rgb.r, rgb.g, rgb.b);
4117
4118     // store the HSV components
4119     this.hsv =
4120         {
4121           'h' : this.getHue(maximum, range),
4122           's' : (maximum == 0 ? 0 : 100 * range / maximum),
4123           'v' : maximum / 2.55
4124         };
4125
4126   },
4127
4128   /* //private Calculates and stores the HSL components of this RGBColor so that they can
4129    * be returned be the getHSL function.
4130    */
4131    calculateHSL : function(){
4132     var rgb = this.rgb;
4133     // get the maximum and range of the RGB component values
4134     var maximum = Math.max(rgb.r, rgb.g, rgb.b);
4135     var range   = maximum - Math.min(rgb.r, rgb.g, rgb.b);
4136
4137     // determine the lightness in the range [0,1]
4138     var l = maximum / 255 - range / 510;
4139
4140     // store the HSL components
4141     this.hsl =
4142         {
4143           'h' : this.getHue(maximum, range),
4144           's' : (range == 0 ? 0 : range / 2.55 / (l < 0.5 ? l * 2 : 2 - l * 2)),
4145           'l' : 100 * l
4146         };
4147
4148   }
4149
4150 });
4151
4152 /**
4153  * @class Roo.lib.HSVColor
4154  * @extends Roo.lib.Color
4155  * Creates a Color specified in the HSV Color space, with an optional alpha
4156  * component. The parameters are:
4157  * @constructor
4158  *
4159  * @param {Number} h - the hue component, wrapped to the range [0,360)
4160  * @param {Number} s - the saturation component, clipped to the range [0,100]
4161  * @param {Number} v - the value component, clipped to the range [0,100]
4162  * @param {Number} a - the alpha component, clipped to the range [0,1] - this parameter is
4163  *     optional and defaults to 1
4164  */
4165 Roo.lib.HSVColor = function (h, s, v, a){
4166
4167   // store the alpha component after clipping it if necessary
4168   this.alpha = (a === undefined ? 1 : Math.max(0, Math.min(1, a)));
4169
4170   // store the HSV components after clipping or wrapping them if necessary
4171   this.hsv =
4172       {
4173         'h' : (h % 360 + 360) % 360,
4174         's' : Math.max(0, Math.min(100, s)),
4175         'v' : Math.max(0, Math.min(100, v))
4176       };
4177
4178   // initialise the RGB and HSL components to null
4179   this.rgb = null;
4180   this.hsl = null;
4181 }
4182
4183 Roo.extend(Roo.lib.HSVColor, Roo.lib.Color, {
4184   /* Calculates and stores the RGB components of this HSVColor so that they can
4185    * be returned be the getRGB function.
4186    */
4187   calculateRGB: function ()
4188   {
4189     var hsv = this.hsv;
4190     // check whether the saturation is zero
4191     if (hsv.s == 0){
4192
4193       // set the Color to the appropriate shade of grey
4194       var r = hsv.v;
4195       var g = hsv.v;
4196       var b = hsv.v;
4197
4198     }else{
4199
4200       // set some temporary values
4201       var f  = hsv.h / 60 - Math.floor(hsv.h / 60);
4202       var p  = hsv.v * (1 - hsv.s / 100);
4203       var q  = hsv.v * (1 - hsv.s / 100 * f);
4204       var t  = hsv.v * (1 - hsv.s / 100 * (1 - f));
4205
4206       // set the RGB Color components to their temporary values
4207       switch (Math.floor(hsv.h / 60)){
4208         case 0: var r = hsv.v; var g = t; var b = p; break;
4209         case 1: var r = q; var g = hsv.v; var b = p; break;
4210         case 2: var r = p; var g = hsv.v; var b = t; break;
4211         case 3: var r = p; var g = q; var b = hsv.v; break;
4212         case 4: var r = t; var g = p; var b = hsv.v; break;
4213         case 5: var r = hsv.v; var g = p; var b = q; break;
4214       }
4215
4216     }
4217
4218     // store the RGB components
4219     this.rgb =
4220         {
4221           'r' : r * 2.55,
4222           'g' : g * 2.55,
4223           'b' : b * 2.55
4224         };
4225
4226   },
4227
4228   /* Calculates and stores the HSL components of this HSVColor so that they can
4229    * be returned be the getHSL function.
4230    */
4231   calculateHSL : function (){
4232
4233     var hsv = this.hsv;
4234     // determine the lightness in the range [0,100]
4235     var l = (2 - hsv.s / 100) * hsv.v / 2;
4236
4237     // store the HSL components
4238     this.hsl =
4239         {
4240           'h' : hsv.h,
4241           's' : hsv.s * hsv.v / (l < 50 ? l * 2 : 200 - l * 2),
4242           'l' : l
4243         };
4244
4245     // correct a division-by-zero error
4246     if (isNaN(hsl.s)) { hsl.s = 0; }
4247
4248   } 
4249  
4250
4251 });
4252  
4253
4254 /**
4255  * @class Roo.lib.HSLColor
4256  * @extends Roo.lib.Color
4257  *
4258  * @constructor
4259  * Creates a Color specified in the HSL Color space, with an optional alpha
4260  * component. The parameters are:
4261  *
4262  * @param {Number} h - the hue component, wrapped to the range [0,360)
4263  * @param {Number} s - the saturation component, clipped to the range [0,100]
4264  * @param {Number} l - the lightness component, clipped to the range [0,100]
4265  * @param {Number} a - the alpha component, clipped to the range [0,1] - this parameter is
4266  *     optional and defaults to 1
4267  */
4268
4269 Roo.lib.HSLColor = function(h, s, l, a){
4270
4271   // store the alpha component after clipping it if necessary
4272   this.alpha = (a === undefined ? 1 : Math.max(0, Math.min(1, a)));
4273
4274   // store the HSL components after clipping or wrapping them if necessary
4275   this.hsl =
4276       {
4277         'h' : (h % 360 + 360) % 360,
4278         's' : Math.max(0, Math.min(100, s)),
4279         'l' : Math.max(0, Math.min(100, l))
4280       };
4281
4282   // initialise the RGB and HSV components to null
4283 }
4284
4285 Roo.extend(Roo.lib.HSLColor, Roo.lib.Color, {
4286
4287   /* Calculates and stores the RGB components of this HSLColor so that they can
4288    * be returned be the getRGB function.
4289    */
4290   calculateRGB: function (){
4291
4292     // check whether the saturation is zero
4293     if (this.hsl.s == 0){
4294
4295       // store the RGB components representing the appropriate shade of grey
4296       this.rgb =
4297           {
4298             'r' : this.hsl.l * 2.55,
4299             'g' : this.hsl.l * 2.55,
4300             'b' : this.hsl.l * 2.55
4301           };
4302
4303     }else{
4304
4305       // set some temporary values
4306       var p = this.hsl.l < 50
4307             ? this.hsl.l * (1 + hsl.s / 100)
4308             : this.hsl.l + hsl.s - hsl.l * hsl.s / 100;
4309       var q = 2 * hsl.l - p;
4310
4311       // initialise the RGB components
4312       this.rgb =
4313           {
4314             'r' : (h + 120) / 60 % 6,
4315             'g' : h / 60,
4316             'b' : (h + 240) / 60 % 6
4317           };
4318
4319       // loop over the RGB components
4320       for (var key in this.rgb){
4321
4322         // ensure that the property is not inherited from the root object
4323         if (this.rgb.hasOwnProperty(key)){
4324
4325           // set the component to its value in the range [0,100]
4326           if (this.rgb[key] < 1){
4327             this.rgb[key] = q + (p - q) * this.rgb[key];
4328           }else if (this.rgb[key] < 3){
4329             this.rgb[key] = p;
4330           }else if (this.rgb[key] < 4){
4331             this.rgb[key] = q + (p - q) * (4 - this.rgb[key]);
4332           }else{
4333             this.rgb[key] = q;
4334           }
4335
4336           // set the component to its value in the range [0,255]
4337           this.rgb[key] *= 2.55;
4338
4339         }
4340
4341       }
4342
4343     }
4344
4345   },
4346
4347   /* Calculates and stores the HSV components of this HSLColor so that they can
4348    * be returned be the getHSL function.
4349    */
4350    calculateHSV : function(){
4351
4352     // set a temporary value
4353     var t = this.hsl.s * (this.hsl.l < 50 ? this.hsl.l : 100 - this.hsl.l) / 100;
4354
4355     // store the HSV components
4356     this.hsv =
4357         {
4358           'h' : this.hsl.h,
4359           's' : 200 * t / (this.hsl.l + t),
4360           'v' : t + this.hsl.l
4361         };
4362
4363     // correct a division-by-zero error
4364     if (isNaN(this.hsv.s)) { this.hsv.s = 0; }
4365
4366   }
4367  
4368
4369 });
4370 /*
4371  * Portions of this file are based on pieces of Yahoo User Interface Library
4372  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
4373  * YUI licensed under the BSD License:
4374  * http://developer.yahoo.net/yui/license.txt
4375  * <script type="text/javascript">
4376  *
4377  */
4378 (function() {
4379
4380     Roo.lib.ColorAnim = function(el, attributes, duration, method) {
4381         Roo.lib.ColorAnim.superclass.constructor.call(this, el, attributes, duration, method);
4382     };
4383
4384     Roo.extend(Roo.lib.ColorAnim, Roo.lib.AnimBase);
4385
4386     var fly = Roo.lib.AnimBase.fly;
4387     var Y = Roo.lib;
4388     var superclass = Y.ColorAnim.superclass;
4389     var proto = Y.ColorAnim.prototype;
4390
4391     proto.toString = function() {
4392         var el = this.getEl();
4393         var id = el.id || el.tagName;
4394         return ("ColorAnim " + id);
4395     };
4396
4397     proto.patterns.color = /color$/i;
4398     proto.patterns.rgb = /^rgb\(([0-9]+)\s*,\s*([0-9]+)\s*,\s*([0-9]+)\)$/i;
4399     proto.patterns.hex = /^#?([0-9A-F]{2})([0-9A-F]{2})([0-9A-F]{2})$/i;
4400     proto.patterns.hex3 = /^#?([0-9A-F]{1})([0-9A-F]{1})([0-9A-F]{1})$/i;
4401     proto.patterns.transparent = /^transparent|rgba\(0, 0, 0, 0\)$/;
4402
4403
4404     proto.parseColor = function(s) {
4405         if (s.length == 3) {
4406             return s;
4407         }
4408
4409         var c = this.patterns.hex.exec(s);
4410         if (c && c.length == 4) {
4411             return [ parseInt(c[1], 16), parseInt(c[2], 16), parseInt(c[3], 16) ];
4412         }
4413
4414         c = this.patterns.rgb.exec(s);
4415         if (c && c.length == 4) {
4416             return [ parseInt(c[1], 10), parseInt(c[2], 10), parseInt(c[3], 10) ];
4417         }
4418
4419         c = this.patterns.hex3.exec(s);
4420         if (c && c.length == 4) {
4421             return [ parseInt(c[1] + c[1], 16), parseInt(c[2] + c[2], 16), parseInt(c[3] + c[3], 16) ];
4422         }
4423
4424         return null;
4425     };
4426     // since this uses fly! - it cant be in ColorAnim (which does not have fly yet..)
4427     proto.getAttribute = function(attr) {
4428         var el = this.getEl();
4429         if (this.patterns.color.test(attr)) {
4430             var val = fly(el).getStyle(attr);
4431
4432             if (this.patterns.transparent.test(val)) {
4433                 var parent = el.parentNode;
4434                 val = fly(parent).getStyle(attr);
4435
4436                 while (parent && this.patterns.transparent.test(val)) {
4437                     parent = parent.parentNode;
4438                     val = fly(parent).getStyle(attr);
4439                     if (parent.tagName.toUpperCase() == 'HTML') {
4440                         val = '#fff';
4441                     }
4442                 }
4443             }
4444         } else {
4445             val = superclass.getAttribute.call(this, attr);
4446         }
4447
4448         return val;
4449     };
4450     proto.getAttribute = function(attr) {
4451         var el = this.getEl();
4452         if (this.patterns.color.test(attr)) {
4453             var val = fly(el).getStyle(attr);
4454
4455             if (this.patterns.transparent.test(val)) {
4456                 var parent = el.parentNode;
4457                 val = fly(parent).getStyle(attr);
4458
4459                 while (parent && this.patterns.transparent.test(val)) {
4460                     parent = parent.parentNode;
4461                     val = fly(parent).getStyle(attr);
4462                     if (parent.tagName.toUpperCase() == 'HTML') {
4463                         val = '#fff';
4464                     }
4465                 }
4466             }
4467         } else {
4468             val = superclass.getAttribute.call(this, attr);
4469         }
4470
4471         return val;
4472     };
4473
4474     proto.doMethod = function(attr, start, end) {
4475         var val;
4476
4477         if (this.patterns.color.test(attr)) {
4478             val = [];
4479             for (var i = 0, len = start.length; i < len; ++i) {
4480                 val[i] = superclass.doMethod.call(this, attr, start[i], end[i]);
4481             }
4482
4483             val = 'rgb(' + Math.floor(val[0]) + ',' + Math.floor(val[1]) + ',' + Math.floor(val[2]) + ')';
4484         }
4485         else {
4486             val = superclass.doMethod.call(this, attr, start, end);
4487         }
4488
4489         return val;
4490     };
4491
4492     proto.setRuntimeAttribute = function(attr) {
4493         superclass.setRuntimeAttribute.call(this, attr);
4494
4495         if (this.patterns.color.test(attr)) {
4496             var attributes = this.attributes;
4497             var start = this.parseColor(this.runtimeAttributes[attr].start);
4498             var end = this.parseColor(this.runtimeAttributes[attr].end);
4499
4500             if (typeof attributes[attr]['to'] === 'undefined' && typeof attributes[attr]['by'] !== 'undefined') {
4501                 end = this.parseColor(attributes[attr].by);
4502
4503                 for (var i = 0, len = start.length; i < len; ++i) {
4504                     end[i] = start[i] + end[i];
4505                 }
4506             }
4507
4508             this.runtimeAttributes[attr].start = start;
4509             this.runtimeAttributes[attr].end = end;
4510         }
4511     };
4512 })();
4513
4514 /*
4515  * Portions of this file are based on pieces of Yahoo User Interface Library
4516  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
4517  * YUI licensed under the BSD License:
4518  * http://developer.yahoo.net/yui/license.txt
4519  * <script type="text/javascript">
4520  *
4521  */
4522 Roo.lib.Easing = {
4523
4524
4525     easeNone: function (t, b, c, d) {
4526         return c * t / d + b;
4527     },
4528
4529
4530     easeIn: function (t, b, c, d) {
4531         return c * (t /= d) * t + b;
4532     },
4533
4534
4535     easeOut: function (t, b, c, d) {
4536         return -c * (t /= d) * (t - 2) + b;
4537     },
4538
4539
4540     easeBoth: function (t, b, c, d) {
4541         if ((t /= d / 2) < 1) {
4542             return c / 2 * t * t + b;
4543         }
4544
4545         return -c / 2 * ((--t) * (t - 2) - 1) + b;
4546     },
4547
4548
4549     easeInStrong: function (t, b, c, d) {
4550         return c * (t /= d) * t * t * t + b;
4551     },
4552
4553
4554     easeOutStrong: function (t, b, c, d) {
4555         return -c * ((t = t / d - 1) * t * t * t - 1) + b;
4556     },
4557
4558
4559     easeBothStrong: function (t, b, c, d) {
4560         if ((t /= d / 2) < 1) {
4561             return c / 2 * t * t * t * t + b;
4562         }
4563
4564         return -c / 2 * ((t -= 2) * t * t * t - 2) + b;
4565     },
4566
4567
4568
4569     elasticIn: function (t, b, c, d, a, p) {
4570         if (t == 0) {
4571             return b;
4572         }
4573         if ((t /= d) == 1) {
4574             return b + c;
4575         }
4576         if (!p) {
4577             p = d * .3;
4578         }
4579
4580         if (!a || a < Math.abs(c)) {
4581             a = c;
4582             var s = p / 4;
4583         }
4584         else {
4585             var s = p / (2 * Math.PI) * Math.asin(c / a);
4586         }
4587
4588         return -(a * Math.pow(2, 10 * (t -= 1)) * Math.sin((t * d - s) * (2 * Math.PI) / p)) + b;
4589     },
4590
4591
4592     elasticOut: function (t, b, c, d, a, p) {
4593         if (t == 0) {
4594             return b;
4595         }
4596         if ((t /= d) == 1) {
4597             return b + c;
4598         }
4599         if (!p) {
4600             p = d * .3;
4601         }
4602
4603         if (!a || a < Math.abs(c)) {
4604             a = c;
4605             var s = p / 4;
4606         }
4607         else {
4608             var s = p / (2 * Math.PI) * Math.asin(c / a);
4609         }
4610
4611         return a * Math.pow(2, -10 * t) * Math.sin((t * d - s) * (2 * Math.PI) / p) + c + b;
4612     },
4613
4614
4615     elasticBoth: function (t, b, c, d, a, p) {
4616         if (t == 0) {
4617             return b;
4618         }
4619
4620         if ((t /= d / 2) == 2) {
4621             return b + c;
4622         }
4623
4624         if (!p) {
4625             p = d * (.3 * 1.5);
4626         }
4627
4628         if (!a || a < Math.abs(c)) {
4629             a = c;
4630             var s = p / 4;
4631         }
4632         else {
4633             var s = p / (2 * Math.PI) * Math.asin(c / a);
4634         }
4635
4636         if (t < 1) {
4637             return -.5 * (a * Math.pow(2, 10 * (t -= 1)) *
4638                           Math.sin((t * d - s) * (2 * Math.PI) / p)) + b;
4639         }
4640         return a * Math.pow(2, -10 * (t -= 1)) *
4641                Math.sin((t * d - s) * (2 * Math.PI) / p) * .5 + c + b;
4642     },
4643
4644
4645
4646     backIn: function (t, b, c, d, s) {
4647         if (typeof s == 'undefined') {
4648             s = 1.70158;
4649         }
4650         return c * (t /= d) * t * ((s + 1) * t - s) + b;
4651     },
4652
4653
4654     backOut: function (t, b, c, d, s) {
4655         if (typeof s == 'undefined') {
4656             s = 1.70158;
4657         }
4658         return c * ((t = t / d - 1) * t * ((s + 1) * t + s) + 1) + b;
4659     },
4660
4661
4662     backBoth: function (t, b, c, d, s) {
4663         if (typeof s == 'undefined') {
4664             s = 1.70158;
4665         }
4666
4667         if ((t /= d / 2 ) < 1) {
4668             return c / 2 * (t * t * (((s *= (1.525)) + 1) * t - s)) + b;
4669         }
4670         return c / 2 * ((t -= 2) * t * (((s *= (1.525)) + 1) * t + s) + 2) + b;
4671     },
4672
4673
4674     bounceIn: function (t, b, c, d) {
4675         return c - Roo.lib.Easing.bounceOut(d - t, 0, c, d) + b;
4676     },
4677
4678
4679     bounceOut: function (t, b, c, d) {
4680         if ((t /= d) < (1 / 2.75)) {
4681             return c * (7.5625 * t * t) + b;
4682         } else if (t < (2 / 2.75)) {
4683             return c * (7.5625 * (t -= (1.5 / 2.75)) * t + .75) + b;
4684         } else if (t < (2.5 / 2.75)) {
4685             return c * (7.5625 * (t -= (2.25 / 2.75)) * t + .9375) + b;
4686         }
4687         return c * (7.5625 * (t -= (2.625 / 2.75)) * t + .984375) + b;
4688     },
4689
4690
4691     bounceBoth: function (t, b, c, d) {
4692         if (t < d / 2) {
4693             return Roo.lib.Easing.bounceIn(t * 2, 0, c, d) * .5 + b;
4694         }
4695         return Roo.lib.Easing.bounceOut(t * 2 - d, 0, c, d) * .5 + c * .5 + b;
4696     }
4697 };/*
4698  * Portions of this file are based on pieces of Yahoo User Interface Library
4699  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
4700  * YUI licensed under the BSD License:
4701  * http://developer.yahoo.net/yui/license.txt
4702  * <script type="text/javascript">
4703  *
4704  */
4705     (function() {
4706         Roo.lib.Motion = function(el, attributes, duration, method) {
4707             if (el) {
4708                 Roo.lib.Motion.superclass.constructor.call(this, el, attributes, duration, method);
4709             }
4710         };
4711
4712         Roo.extend(Roo.lib.Motion, Roo.lib.ColorAnim);
4713
4714
4715         var Y = Roo.lib;
4716         var superclass = Y.Motion.superclass;
4717         var proto = Y.Motion.prototype;
4718
4719         proto.toString = function() {
4720             var el = this.getEl();
4721             var id = el.id || el.tagName;
4722             return ("Motion " + id);
4723         };
4724
4725         proto.patterns.points = /^points$/i;
4726
4727         proto.setAttribute = function(attr, val, unit) {
4728             if (this.patterns.points.test(attr)) {
4729                 unit = unit || 'px';
4730                 superclass.setAttribute.call(this, 'left', val[0], unit);
4731                 superclass.setAttribute.call(this, 'top', val[1], unit);
4732             } else {
4733                 superclass.setAttribute.call(this, attr, val, unit);
4734             }
4735         };
4736
4737         proto.getAttribute = function(attr) {
4738             if (this.patterns.points.test(attr)) {
4739                 var val = [
4740                         superclass.getAttribute.call(this, 'left'),
4741                         superclass.getAttribute.call(this, 'top')
4742                         ];
4743             } else {
4744                 val = superclass.getAttribute.call(this, attr);
4745             }
4746
4747             return val;
4748         };
4749
4750         proto.doMethod = function(attr, start, end) {
4751             var val = null;
4752
4753             if (this.patterns.points.test(attr)) {
4754                 var t = this.method(this.currentFrame, 0, 100, this.totalFrames) / 100;
4755                 val = Y.Bezier.getPosition(this.runtimeAttributes[attr], t);
4756             } else {
4757                 val = superclass.doMethod.call(this, attr, start, end);
4758             }
4759             return val;
4760         };
4761
4762         proto.setRuntimeAttribute = function(attr) {
4763             if (this.patterns.points.test(attr)) {
4764                 var el = this.getEl();
4765                 var attributes = this.attributes;
4766                 var start;
4767                 var control = attributes['points']['control'] || [];
4768                 var end;
4769                 var i, len;
4770
4771                 if (control.length > 0 && !(control[0] instanceof Array)) {
4772                     control = [control];
4773                 } else {
4774                     var tmp = [];
4775                     for (i = 0,len = control.length; i < len; ++i) {
4776                         tmp[i] = control[i];
4777                     }
4778                     control = tmp;
4779                 }
4780
4781                 Roo.fly(el).position();
4782
4783                 if (isset(attributes['points']['from'])) {
4784                     Roo.lib.Dom.setXY(el, attributes['points']['from']);
4785                 }
4786                 else {
4787                     Roo.lib.Dom.setXY(el, Roo.lib.Dom.getXY(el));
4788                 }
4789
4790                 start = this.getAttribute('points');
4791
4792
4793                 if (isset(attributes['points']['to'])) {
4794                     end = translateValues.call(this, attributes['points']['to'], start);
4795
4796                     var pageXY = Roo.lib.Dom.getXY(this.getEl());
4797                     for (i = 0,len = control.length; i < len; ++i) {
4798                         control[i] = translateValues.call(this, control[i], start);
4799                     }
4800
4801
4802                 } else if (isset(attributes['points']['by'])) {
4803                     end = [ start[0] + attributes['points']['by'][0], start[1] + attributes['points']['by'][1] ];
4804
4805                     for (i = 0,len = control.length; i < len; ++i) {
4806                         control[i] = [ start[0] + control[i][0], start[1] + control[i][1] ];
4807                     }
4808                 }
4809
4810                 this.runtimeAttributes[attr] = [start];
4811
4812                 if (control.length > 0) {
4813                     this.runtimeAttributes[attr] = this.runtimeAttributes[attr].concat(control);
4814                 }
4815
4816                 this.runtimeAttributes[attr][this.runtimeAttributes[attr].length] = end;
4817             }
4818             else {
4819                 superclass.setRuntimeAttribute.call(this, attr);
4820             }
4821         };
4822
4823         var translateValues = function(val, start) {
4824             var pageXY = Roo.lib.Dom.getXY(this.getEl());
4825             val = [ val[0] - pageXY[0] + start[0], val[1] - pageXY[1] + start[1] ];
4826
4827             return val;
4828         };
4829
4830         var isset = function(prop) {
4831             return (typeof prop !== 'undefined');
4832         };
4833     })();
4834 /*
4835  * Portions of this file are based on pieces of Yahoo User Interface Library
4836  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
4837  * YUI licensed under the BSD License:
4838  * http://developer.yahoo.net/yui/license.txt
4839  * <script type="text/javascript">
4840  *
4841  */
4842     (function() {
4843         Roo.lib.Scroll = function(el, attributes, duration, method) {
4844             if (el) {
4845                 Roo.lib.Scroll.superclass.constructor.call(this, el, attributes, duration, method);
4846             }
4847         };
4848
4849         Roo.extend(Roo.lib.Scroll, Roo.lib.ColorAnim);
4850
4851
4852         var Y = Roo.lib;
4853         var superclass = Y.Scroll.superclass;
4854         var proto = Y.Scroll.prototype;
4855
4856         proto.toString = function() {
4857             var el = this.getEl();
4858             var id = el.id || el.tagName;
4859             return ("Scroll " + id);
4860         };
4861
4862         proto.doMethod = function(attr, start, end) {
4863             var val = null;
4864
4865             if (attr == 'scroll') {
4866                 val = [
4867                         this.method(this.currentFrame, start[0], end[0] - start[0], this.totalFrames),
4868                         this.method(this.currentFrame, start[1], end[1] - start[1], this.totalFrames)
4869                         ];
4870
4871             } else {
4872                 val = superclass.doMethod.call(this, attr, start, end);
4873             }
4874             return val;
4875         };
4876
4877         proto.getAttribute = function(attr) {
4878             var val = null;
4879             var el = this.getEl();
4880
4881             if (attr == 'scroll') {
4882                 val = [ el.scrollLeft, el.scrollTop ];
4883             } else {
4884                 val = superclass.getAttribute.call(this, attr);
4885             }
4886
4887             return val;
4888         };
4889
4890         proto.setAttribute = function(attr, val, unit) {
4891             var el = this.getEl();
4892
4893             if (attr == 'scroll') {
4894                 el.scrollLeft = val[0];
4895                 el.scrollTop = val[1];
4896             } else {
4897                 superclass.setAttribute.call(this, attr, val, unit);
4898             }
4899         };
4900     })();
4901 /*
4902  * Based on:
4903  * Ext JS Library 1.1.1
4904  * Copyright(c) 2006-2007, Ext JS, LLC.
4905  *
4906  * Originally Released Under LGPL - original licence link has changed is not relivant.
4907  *
4908  * Fork - LGPL
4909  * <script type="text/javascript">
4910  */
4911
4912
4913 // nasty IE9 hack - what a pile of crap that is..
4914
4915  if (typeof Range != "undefined" && typeof Range.prototype.createContextualFragment == "undefined") {
4916     Range.prototype.createContextualFragment = function (html) {
4917         var doc = window.document;
4918         var container = doc.createElement("div");
4919         container.innerHTML = html;
4920         var frag = doc.createDocumentFragment(), n;
4921         while ((n = container.firstChild)) {
4922             frag.appendChild(n);
4923         }
4924         return frag;
4925     };
4926 }
4927
4928 /**
4929  * @class Roo.DomHelper
4930  * Utility class for working with DOM and/or Templates. It transparently supports using HTML fragments or DOM.
4931  * 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>.
4932  * @static
4933  */
4934 Roo.DomHelper = function(){
4935     var tempTableEl = null;
4936     var emptyTags = /^(?:br|frame|hr|img|input|link|meta|range|spacer|wbr|area|param|col)$/i;
4937     var tableRe = /^table|tbody|tr|td$/i;
4938     var xmlns = {};
4939     // build as innerHTML where available
4940     /** @ignore */
4941     var createHtml = function(o){
4942         if(typeof o == 'string'){
4943             return o;
4944         }
4945         var b = "";
4946         if(!o.tag){
4947             o.tag = "div";
4948         }
4949         b += "<" + o.tag;
4950         for(var attr in o){
4951             if(attr == "tag" || attr == "children" || attr == "cn" || attr == "html" || typeof o[attr] == "function") { continue; }
4952             if(attr == "style"){
4953                 var s = o["style"];
4954                 if(typeof s == "function"){
4955                     s = s.call();
4956                 }
4957                 if(typeof s == "string"){
4958                     b += ' style="' + s + '"';
4959                 }else if(typeof s == "object"){
4960                     b += ' style="';
4961                     for(var key in s){
4962                         if(typeof s[key] != "function"){
4963                             b += key + ":" + s[key] + ";";
4964                         }
4965                     }
4966                     b += '"';
4967                 }
4968             }else{
4969                 if(attr == "cls"){
4970                     b += ' class="' + o["cls"] + '"';
4971                 }else if(attr == "htmlFor"){
4972                     b += ' for="' + o["htmlFor"] + '"';
4973                 }else{
4974                     b += " " + attr + '="' + o[attr] + '"';
4975                 }
4976             }
4977         }
4978         if(emptyTags.test(o.tag)){
4979             b += "/>";
4980         }else{
4981             b += ">";
4982             var cn = o.children || o.cn;
4983             if(cn){
4984                 //http://bugs.kde.org/show_bug.cgi?id=71506
4985                 if((cn instanceof Array) || (Roo.isSafari && typeof(cn.join) == "function")){
4986                     for(var i = 0, len = cn.length; i < len; i++) {
4987                         b += createHtml(cn[i], b);
4988                     }
4989                 }else{
4990                     b += createHtml(cn, b);
4991                 }
4992             }
4993             if(o.html){
4994                 b += o.html;
4995             }
4996             b += "</" + o.tag + ">";
4997         }
4998         return b;
4999     };
5000
5001     // build as dom
5002     /** @ignore */
5003     var createDom = function(o, parentNode){
5004          
5005         // defininition craeted..
5006         var ns = false;
5007         if (o.ns && o.ns != 'html') {
5008                
5009             if (o.xmlns && typeof(xmlns[o.ns]) == 'undefined') {
5010                 xmlns[o.ns] = o.xmlns;
5011                 ns = o.xmlns;
5012             }
5013             if (typeof(xmlns[o.ns]) == 'undefined') {
5014                 console.log("Trying to create namespace element " + o.ns + ", however no xmlns was sent to builder previously");
5015             }
5016             ns = xmlns[o.ns];
5017         }
5018         
5019         
5020         if (typeof(o) == 'string') {
5021             return parentNode.appendChild(document.createTextNode(o));
5022         }
5023         o.tag = o.tag || div;
5024         if (o.ns && Roo.isIE) {
5025             ns = false;
5026             o.tag = o.ns + ':' + o.tag;
5027             
5028         }
5029         var el = ns ? document.createElementNS( ns, o.tag||'div') :  document.createElement(o.tag||'div');
5030         var useSet = el.setAttribute ? true : false; // In IE some elements don't have setAttribute
5031         for(var attr in o){
5032             
5033             if(attr == "tag" || attr == "ns" ||attr == "xmlns" ||attr == "children" || attr == "cn" || attr == "html" || 
5034                     attr == "style" || typeof o[attr] == "function") { continue; }
5035                     
5036             if(attr=="cls" && Roo.isIE){
5037                 el.className = o["cls"];
5038             }else{
5039                 if(useSet) { el.setAttribute(attr=="cls" ? 'class' : attr, o[attr]);}
5040                 else { 
5041                     el[attr] = o[attr];
5042                 }
5043             }
5044         }
5045         Roo.DomHelper.applyStyles(el, o.style);
5046         var cn = o.children || o.cn;
5047         if(cn){
5048             //http://bugs.kde.org/show_bug.cgi?id=71506
5049              if((cn instanceof Array) || (Roo.isSafari && typeof(cn.join) == "function")){
5050                 for(var i = 0, len = cn.length; i < len; i++) {
5051                     createDom(cn[i], el);
5052                 }
5053             }else{
5054                 createDom(cn, el);
5055             }
5056         }
5057         if(o.html){
5058             el.innerHTML = o.html;
5059         }
5060         if(parentNode){
5061            parentNode.appendChild(el);
5062         }
5063         return el;
5064     };
5065
5066     var ieTable = function(depth, s, h, e){
5067         tempTableEl.innerHTML = [s, h, e].join('');
5068         var i = -1, el = tempTableEl;
5069         while(++i < depth && el.firstChild){
5070             el = el.firstChild;
5071         }
5072         return el;
5073     };
5074
5075     // kill repeat to save bytes
5076     var ts = '<table>',
5077         te = '</table>',
5078         tbs = ts+'<tbody>',
5079         tbe = '</tbody>'+te,
5080         trs = tbs + '<tr>',
5081         tre = '</tr>'+tbe;
5082
5083     /**
5084      * @ignore
5085      * Nasty code for IE's broken table implementation
5086      */
5087     var insertIntoTable = function(tag, where, el, html){
5088         if(!tempTableEl){
5089             tempTableEl = document.createElement('div');
5090         }
5091         var node;
5092         var before = null;
5093         if(tag == 'td'){
5094             if(where == 'afterbegin' || where == 'beforeend'){ // INTO a TD
5095                 return;
5096             }
5097             if(where == 'beforebegin'){
5098                 before = el;
5099                 el = el.parentNode;
5100             } else{
5101                 before = el.nextSibling;
5102                 el = el.parentNode;
5103             }
5104             node = ieTable(4, trs, html, tre);
5105         }
5106         else if(tag == 'tr'){
5107             if(where == 'beforebegin'){
5108                 before = el;
5109                 el = el.parentNode;
5110                 node = ieTable(3, tbs, html, tbe);
5111             } else if(where == 'afterend'){
5112                 before = el.nextSibling;
5113                 el = el.parentNode;
5114                 node = ieTable(3, tbs, html, tbe);
5115             } else{ // INTO a TR
5116                 if(where == 'afterbegin'){
5117                     before = el.firstChild;
5118                 }
5119                 node = ieTable(4, trs, html, tre);
5120             }
5121         } else if(tag == 'tbody'){
5122             if(where == 'beforebegin'){
5123                 before = el;
5124                 el = el.parentNode;
5125                 node = ieTable(2, ts, html, te);
5126             } else if(where == 'afterend'){
5127                 before = el.nextSibling;
5128                 el = el.parentNode;
5129                 node = ieTable(2, ts, html, te);
5130             } else{
5131                 if(where == 'afterbegin'){
5132                     before = el.firstChild;
5133                 }
5134                 node = ieTable(3, tbs, html, tbe);
5135             }
5136         } else{ // TABLE
5137             if(where == 'beforebegin' || where == 'afterend'){ // OUTSIDE the table
5138                 return;
5139             }
5140             if(where == 'afterbegin'){
5141                 before = el.firstChild;
5142             }
5143             node = ieTable(2, ts, html, te);
5144         }
5145         el.insertBefore(node, before);
5146         return node;
5147     };
5148     
5149     // this is a bit like the react update code...
5150     // 
5151     
5152     var updateNode = function(from, to)
5153     {
5154         // should we handle non-standard elements?
5155         
5156         if (from.nodeType != to.nodeType) {
5157             from.parentNode.replaceChild(to, from);
5158         }
5159         
5160         if (from.nodeType == 3) {
5161             // assume it's text?!
5162             if (from.data == to.data) {
5163                 return;
5164             }
5165             from.data = to.data;
5166             return;
5167         }
5168         
5169         // assume 'to' doesnt have '1/3 nodetypes!
5170         if (from.nodeType !=1 || from.tagName != to.tagName) {
5171             from.parentNode.replaceChild(to, from);
5172             return;
5173         }
5174         // compare attributes
5175         var ar = Array.from(from.attributes);
5176         for(var i = 0; i< ar.length;i++) {
5177             if (to.hasAttribute(ar[i].name)) {
5178                 continue;
5179             }
5180             from.removeAttribute(ar[i].name);
5181         }
5182         ar = to.attributes;
5183         for(var i = 0; i< ar.length;i++) {
5184             if (from.getAttribute(ar[i].name) == to.getAttribute(ar[i].name)) {
5185                 continue;
5186             }
5187             from.setAttribute(ar[i].name, to.getAttribute(ar[i].name));
5188         }
5189         // children
5190         var far = Array.from(from.childNodes);
5191         var tar = Array.from(to.childNodes);
5192         // if the lengths are different.. then it's probably a editable content change, rather than
5193         // a change of the block definition..
5194         if (from.innerHTML == to.innerHTML) {
5195             return;
5196         }
5197         if (far.length != tar.length) {
5198             from.innerHTML = to.innerHTML;
5199             return;
5200         }
5201         
5202         for(var i = 0; i < far.length; i++) {
5203             updateNode(far[i], tar[i]);
5204         }
5205         
5206         
5207     };
5208     
5209     
5210
5211     return {
5212         /** True to force the use of DOM instead of html fragments @type Boolean */
5213         useDom : false,
5214     
5215         /**
5216          * Returns the markup for the passed Element(s) config
5217          * @param {Object} o The Dom object spec (and children)
5218          * @return {String}
5219          */
5220         markup : function(o){
5221             return createHtml(o);
5222         },
5223     
5224         /**
5225          * Applies a style specification to an element
5226          * @param {String/HTMLElement} el The element to apply styles to
5227          * @param {String/Object/Function} styles A style specification string eg "width:100px", or object in the form {width:"100px"}, or
5228          * a function which returns such a specification.
5229          */
5230         applyStyles : function(el, styles){
5231             if(styles){
5232                el = Roo.fly(el);
5233                if(typeof styles == "string"){
5234                    var re = /\s?([a-z\-]*)\:\s?([^;]*);?/gi;
5235                    var matches;
5236                    while ((matches = re.exec(styles)) != null){
5237                        el.setStyle(matches[1], matches[2]);
5238                    }
5239                }else if (typeof styles == "object"){
5240                    for (var style in styles){
5241                       el.setStyle(style, styles[style]);
5242                    }
5243                }else if (typeof styles == "function"){
5244                     Roo.DomHelper.applyStyles(el, styles.call());
5245                }
5246             }
5247         },
5248     
5249         /**
5250          * Inserts an HTML fragment into the Dom
5251          * @param {String} where Where to insert the html in relation to el - beforeBegin, afterBegin, beforeEnd, afterEnd.
5252          * @param {HTMLElement} el The context element
5253          * @param {String} html The HTML fragmenet
5254          * @return {HTMLElement} The new node
5255          */
5256         insertHtml : function(where, el, html){
5257             where = where.toLowerCase();
5258             if(el.insertAdjacentHTML){
5259                 if(tableRe.test(el.tagName)){
5260                     var rs;
5261                     if(rs = insertIntoTable(el.tagName.toLowerCase(), where, el, html)){
5262                         return rs;
5263                     }
5264                 }
5265                 switch(where){
5266                     case "beforebegin":
5267                         el.insertAdjacentHTML('BeforeBegin', html);
5268                         return el.previousSibling;
5269                     case "afterbegin":
5270                         el.insertAdjacentHTML('AfterBegin', html);
5271                         return el.firstChild;
5272                     case "beforeend":
5273                         el.insertAdjacentHTML('BeforeEnd', html);
5274                         return el.lastChild;
5275                     case "afterend":
5276                         el.insertAdjacentHTML('AfterEnd', html);
5277                         return el.nextSibling;
5278                 }
5279                 throw 'Illegal insertion point -> "' + where + '"';
5280             }
5281             var range = el.ownerDocument.createRange();
5282             var frag;
5283             switch(where){
5284                  case "beforebegin":
5285                     range.setStartBefore(el);
5286                     frag = range.createContextualFragment(html);
5287                     el.parentNode.insertBefore(frag, el);
5288                     return el.previousSibling;
5289                  case "afterbegin":
5290                     if(el.firstChild){
5291                         range.setStartBefore(el.firstChild);
5292                         frag = range.createContextualFragment(html);
5293                         el.insertBefore(frag, el.firstChild);
5294                         return el.firstChild;
5295                     }else{
5296                         el.innerHTML = html;
5297                         return el.firstChild;
5298                     }
5299                 case "beforeend":
5300                     if(el.lastChild){
5301                         range.setStartAfter(el.lastChild);
5302                         frag = range.createContextualFragment(html);
5303                         el.appendChild(frag);
5304                         return el.lastChild;
5305                     }else{
5306                         el.innerHTML = html;
5307                         return el.lastChild;
5308                     }
5309                 case "afterend":
5310                     range.setStartAfter(el);
5311                     frag = range.createContextualFragment(html);
5312                     el.parentNode.insertBefore(frag, el.nextSibling);
5313                     return el.nextSibling;
5314                 }
5315                 throw 'Illegal insertion point -> "' + where + '"';
5316         },
5317     
5318         /**
5319          * Creates new Dom element(s) and inserts them before el
5320          * @param {String/HTMLElement/Element} el The context element
5321          * @param {Object/String} o The Dom object spec (and children) or raw HTML blob
5322          * @param {Boolean} returnElement (optional) true to return a Roo.Element
5323          * @return {HTMLElement/Roo.Element} The new node
5324          */
5325         insertBefore : function(el, o, returnElement){
5326             return this.doInsert(el, o, returnElement, "beforeBegin");
5327         },
5328     
5329         /**
5330          * Creates new Dom element(s) and inserts them after el
5331          * @param {String/HTMLElement/Element} el The context element
5332          * @param {Object} o The Dom object spec (and children)
5333          * @param {Boolean} returnElement (optional) true to return a Roo.Element
5334          * @return {HTMLElement/Roo.Element} The new node
5335          */
5336         insertAfter : function(el, o, returnElement){
5337             return this.doInsert(el, o, returnElement, "afterEnd", "nextSibling");
5338         },
5339     
5340         /**
5341          * Creates new Dom element(s) and inserts them as the first child of el
5342          * @param {String/HTMLElement/Element} el The context element
5343          * @param {Object/String} o The Dom object spec (and children) or raw HTML blob
5344          * @param {Boolean} returnElement (optional) true to return a Roo.Element
5345          * @return {HTMLElement/Roo.Element} The new node
5346          */
5347         insertFirst : function(el, o, returnElement){
5348             return this.doInsert(el, o, returnElement, "afterBegin");
5349         },
5350     
5351         // private
5352         doInsert : function(el, o, returnElement, pos, sibling){
5353             el = Roo.getDom(el);
5354             var newNode;
5355             if(this.useDom || o.ns){
5356                 newNode = createDom(o, null);
5357                 el.parentNode.insertBefore(newNode, sibling ? el[sibling] : el);
5358             }else{
5359                 var html = createHtml(o);
5360                 newNode = this.insertHtml(pos, el, html);
5361             }
5362             return returnElement ? Roo.get(newNode, true) : newNode;
5363         },
5364     
5365         /**
5366          * Creates new Dom element(s) and appends them to el
5367          * @param {String/HTMLElement/Element} el The context element
5368          * @param {Object/String} o The Dom object spec (and children) or raw HTML blob
5369          * @param {Boolean} returnElement (optional) true to return a Roo.Element
5370          * @return {HTMLElement/Roo.Element} The new node
5371          */
5372         append : function(el, o, returnElement){
5373             el = Roo.getDom(el);
5374             var newNode;
5375             if(this.useDom || o.ns){
5376                 newNode = createDom(o, null);
5377                 el.appendChild(newNode);
5378             }else{
5379                 var html = createHtml(o);
5380                 newNode = this.insertHtml("beforeEnd", el, html);
5381             }
5382             return returnElement ? Roo.get(newNode, true) : newNode;
5383         },
5384     
5385         /**
5386          * Creates new Dom element(s) and overwrites the contents of el with them
5387          * @param {String/HTMLElement/Element} el The context element
5388          * @param {Object/String} o The Dom object spec (and children) or raw HTML blob
5389          * @param {Boolean} returnElement (optional) true to return a Roo.Element
5390          * @return {HTMLElement/Roo.Element} The new node
5391          */
5392         overwrite : function(el, o, returnElement)
5393         {
5394             el = Roo.getDom(el);
5395             if (o.ns) {
5396               
5397                 while (el.childNodes.length) {
5398                     el.removeChild(el.firstChild);
5399                 }
5400                 createDom(o, el);
5401             } else {
5402                 el.innerHTML = createHtml(o);   
5403             }
5404             
5405             return returnElement ? Roo.get(el.firstChild, true) : el.firstChild;
5406         },
5407     
5408         /**
5409          * Creates a new Roo.DomHelper.Template from the Dom object spec
5410          * @param {Object} o The Dom object spec (and children)
5411          * @return {Roo.DomHelper.Template} The new template
5412          */
5413         createTemplate : function(o){
5414             var html = createHtml(o);
5415             return new Roo.Template(html);
5416         },
5417          /**
5418          * Updates the first element with the spec from the o (replacing if necessary)
5419          * This iterates through the children, and updates attributes / children etc..
5420          * @param {String/HTMLElement/Element} el The context element
5421          * @param {Object/String} o The Dom object spec (and children) or raw HTML blob
5422          */
5423         
5424         update : function(el, o)
5425         {
5426             updateNode(Roo.getDom(el), createDom(o));
5427             
5428         }
5429         
5430         
5431     };
5432 }();
5433 /*
5434  * Based on:
5435  * Ext JS Library 1.1.1
5436  * Copyright(c) 2006-2007, Ext JS, LLC.
5437  *
5438  * Originally Released Under LGPL - original licence link has changed is not relivant.
5439  *
5440  * Fork - LGPL
5441  * <script type="text/javascript">
5442  */
5443  
5444 /**
5445 * @class Roo.Template
5446 * Represents an HTML fragment template. Templates can be precompiled for greater performance.
5447 * For a list of available format functions, see {@link Roo.util.Format}.<br />
5448 * Usage:
5449 <pre><code>
5450 var t = new Roo.Template({
5451     html :  '&lt;div name="{id}"&gt;' + 
5452         '&lt;span class="{cls}"&gt;{name:trim} {someval:this.myformat}{value:ellipsis(10)}&lt;/span&gt;' +
5453         '&lt;/div&gt;',
5454     myformat: function (value, allValues) {
5455         return 'XX' + value;
5456     }
5457 });
5458 t.append('some-element', {id: 'myid', cls: 'myclass', name: 'foo', value: 'bar'});
5459 </code></pre>
5460 * For more information see this blog post with examples:
5461 *  <a href="http://www.cnitblog.com/seeyeah/archive/2011/12/30/38728.html/">DomHelper
5462      - Create Elements using DOM, HTML fragments and Templates</a>. 
5463 * @constructor
5464 * @param {Object} cfg - Configuration object.
5465 */
5466 Roo.Template = function(cfg){
5467     // BC!
5468     if(cfg instanceof Array){
5469         cfg = cfg.join("");
5470     }else if(arguments.length > 1){
5471         cfg = Array.prototype.join.call(arguments, "");
5472     }
5473     
5474     
5475     if (typeof(cfg) == 'object') {
5476         Roo.apply(this,cfg)
5477     } else {
5478         // bc
5479         this.html = cfg;
5480     }
5481     if (this.url) {
5482         this.load();
5483     }
5484     
5485 };
5486 Roo.Template.prototype = {
5487     
5488     /**
5489      * @cfg {Function} onLoad Called after the template has been loaded and complied (usually from a remove source)
5490      */
5491     onLoad : false,
5492     
5493     
5494     /**
5495      * @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..
5496      *                    it should be fixed so that template is observable...
5497      */
5498     url : false,
5499     /**
5500      * @cfg {String} html  The HTML fragment or an array of fragments to join("") or multiple arguments to join("")
5501      */
5502     html : '',
5503     
5504     
5505     compiled : false,
5506     loaded : false,
5507     /**
5508      * Returns an HTML fragment of this template with the specified values applied.
5509      * @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'})
5510      * @return {String} The HTML fragment
5511      */
5512     
5513    
5514     
5515     applyTemplate : function(values){
5516         //Roo.log(["applyTemplate", values]);
5517         try {
5518            
5519             if(this.compiled){
5520                 return this.compiled(values);
5521             }
5522             var useF = this.disableFormats !== true;
5523             var fm = Roo.util.Format, tpl = this;
5524             var fn = function(m, name, format, args){
5525                 if(format && useF){
5526                     if(format.substr(0, 5) == "this."){
5527                         return tpl.call(format.substr(5), values[name], values);
5528                     }else{
5529                         if(args){
5530                             // quoted values are required for strings in compiled templates, 
5531                             // but for non compiled we need to strip them
5532                             // quoted reversed for jsmin
5533                             var re = /^\s*['"](.*)["']\s*$/;
5534                             args = args.split(',');
5535                             for(var i = 0, len = args.length; i < len; i++){
5536                                 args[i] = args[i].replace(re, "$1");
5537                             }
5538                             args = [values[name]].concat(args);
5539                         }else{
5540                             args = [values[name]];
5541                         }
5542                         return fm[format].apply(fm, args);
5543                     }
5544                 }else{
5545                     return values[name] !== undefined ? values[name] : "";
5546                 }
5547             };
5548             return this.html.replace(this.re, fn);
5549         } catch (e) {
5550             Roo.log(e);
5551             throw e;
5552         }
5553          
5554     },
5555     
5556     loading : false,
5557       
5558     load : function ()
5559     {
5560          
5561         if (this.loading) {
5562             return;
5563         }
5564         var _t = this;
5565         
5566         this.loading = true;
5567         this.compiled = false;
5568         
5569         var cx = new Roo.data.Connection();
5570         cx.request({
5571             url : this.url,
5572             method : 'GET',
5573             success : function (response) {
5574                 _t.loading = false;
5575                 _t.url = false;
5576                 
5577                 _t.set(response.responseText,true);
5578                 _t.loaded = true;
5579                 if (_t.onLoad) {
5580                     _t.onLoad();
5581                 }
5582              },
5583             failure : function(response) {
5584                 Roo.log("Template failed to load from " + _t.url);
5585                 _t.loading = false;
5586             }
5587         });
5588     },
5589
5590     /**
5591      * Sets the HTML used as the template and optionally compiles it.
5592      * @param {String} html
5593      * @param {Boolean} compile (optional) True to compile the template (defaults to undefined)
5594      * @return {Roo.Template} this
5595      */
5596     set : function(html, compile){
5597         this.html = html;
5598         this.compiled = false;
5599         if(compile){
5600             this.compile();
5601         }
5602         return this;
5603     },
5604     
5605     /**
5606      * True to disable format functions (defaults to false)
5607      * @type Boolean
5608      */
5609     disableFormats : false,
5610     
5611     /**
5612     * The regular expression used to match template variables 
5613     * @type RegExp
5614     * @property 
5615     */
5616     re : /\{([\w-]+)(?:\:([\w\.]*)(?:\((.*?)?\))?)?\}/g,
5617     
5618     /**
5619      * Compiles the template into an internal function, eliminating the RegEx overhead.
5620      * @return {Roo.Template} this
5621      */
5622     compile : function(){
5623         var fm = Roo.util.Format;
5624         var useF = this.disableFormats !== true;
5625         var sep = Roo.isGecko ? "+" : ",";
5626         var fn = function(m, name, format, args){
5627             if(format && useF){
5628                 args = args ? ',' + args : "";
5629                 if(format.substr(0, 5) != "this."){
5630                     format = "fm." + format + '(';
5631                 }else{
5632                     format = 'this.call("'+ format.substr(5) + '", ';
5633                     args = ", values";
5634                 }
5635             }else{
5636                 args= ''; format = "(values['" + name + "'] == undefined ? '' : ";
5637             }
5638             return "'"+ sep + format + "values['" + name + "']" + args + ")"+sep+"'";
5639         };
5640         var body;
5641         // branched to use + in gecko and [].join() in others
5642         if(Roo.isGecko){
5643             body = "this.compiled = function(values){ return '" +
5644                    this.html.replace(/\\/g, '\\\\').replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn) +
5645                     "';};";
5646         }else{
5647             body = ["this.compiled = function(values){ return ['"];
5648             body.push(this.html.replace(/\\/g, '\\\\').replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn));
5649             body.push("'].join('');};");
5650             body = body.join('');
5651         }
5652         /**
5653          * eval:var:values
5654          * eval:var:fm
5655          */
5656         eval(body);
5657         return this;
5658     },
5659     
5660     // private function used to call members
5661     call : function(fnName, value, allValues){
5662         return this[fnName](value, allValues);
5663     },
5664     
5665     /**
5666      * Applies the supplied values to the template and inserts the new node(s) as the first child of el.
5667      * @param {String/HTMLElement/Roo.Element} el The context element
5668      * @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'})
5669      * @param {Boolean} returnElement (optional) true to return a Roo.Element (defaults to undefined)
5670      * @return {HTMLElement/Roo.Element} The new node or Element
5671      */
5672     insertFirst: function(el, values, returnElement){
5673         return this.doInsert('afterBegin', el, values, returnElement);
5674     },
5675
5676     /**
5677      * Applies the supplied values to the template and inserts the new node(s) before el.
5678      * @param {String/HTMLElement/Roo.Element} el The context element
5679      * @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'})
5680      * @param {Boolean} returnElement (optional) true to return a Roo.Element (defaults to undefined)
5681      * @return {HTMLElement/Roo.Element} The new node or Element
5682      */
5683     insertBefore: function(el, values, returnElement){
5684         return this.doInsert('beforeBegin', el, values, returnElement);
5685     },
5686
5687     /**
5688      * Applies the supplied values to the template and inserts the new node(s) after el.
5689      * @param {String/HTMLElement/Roo.Element} el The context element
5690      * @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'})
5691      * @param {Boolean} returnElement (optional) true to return a Roo.Element (defaults to undefined)
5692      * @return {HTMLElement/Roo.Element} The new node or Element
5693      */
5694     insertAfter : function(el, values, returnElement){
5695         return this.doInsert('afterEnd', el, values, returnElement);
5696     },
5697     
5698     /**
5699      * Applies the supplied values to the template and appends the new node(s) to el.
5700      * @param {String/HTMLElement/Roo.Element} el The context element
5701      * @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'})
5702      * @param {Boolean} returnElement (optional) true to return a Roo.Element (defaults to undefined)
5703      * @return {HTMLElement/Roo.Element} The new node or Element
5704      */
5705     append : function(el, values, returnElement){
5706         return this.doInsert('beforeEnd', el, values, returnElement);
5707     },
5708
5709     doInsert : function(where, el, values, returnEl){
5710         el = Roo.getDom(el);
5711         var newNode = Roo.DomHelper.insertHtml(where, el, this.applyTemplate(values));
5712         return returnEl ? Roo.get(newNode, true) : newNode;
5713     },
5714
5715     /**
5716      * Applies the supplied values to the template and overwrites the content of el with the new node(s).
5717      * @param {String/HTMLElement/Roo.Element} el The context element
5718      * @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'})
5719      * @param {Boolean} returnElement (optional) true to return a Roo.Element (defaults to undefined)
5720      * @return {HTMLElement/Roo.Element} The new node or Element
5721      */
5722     overwrite : function(el, values, returnElement){
5723         el = Roo.getDom(el);
5724         el.innerHTML = this.applyTemplate(values);
5725         return returnElement ? Roo.get(el.firstChild, true) : el.firstChild;
5726     }
5727 };
5728 /**
5729  * Alias for {@link #applyTemplate}
5730  * @method
5731  */
5732 Roo.Template.prototype.apply = Roo.Template.prototype.applyTemplate;
5733
5734 // backwards compat
5735 Roo.DomHelper.Template = Roo.Template;
5736
5737 /**
5738  * Creates a template from the passed element's value (<i>display:none</i> textarea, preferred) or innerHTML.
5739  * @param {String/HTMLElement} el A DOM element or its id
5740  * @returns {Roo.Template} The created template
5741  * @static
5742  */
5743 Roo.Template.from = function(el){
5744     el = Roo.getDom(el);
5745     return new Roo.Template(el.value || el.innerHTML);
5746 };/*
5747  * Based on:
5748  * Ext JS Library 1.1.1
5749  * Copyright(c) 2006-2007, Ext JS, LLC.
5750  *
5751  * Originally Released Under LGPL - original licence link has changed is not relivant.
5752  *
5753  * Fork - LGPL
5754  * <script type="text/javascript">
5755  */
5756  
5757
5758 /*
5759  * This is code is also distributed under MIT license for use
5760  * with jQuery and prototype JavaScript libraries.
5761  */
5762 /**
5763  * @class Roo.DomQuery
5764 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).
5765 <p>
5766 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>
5767
5768 <p>
5769 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.
5770 </p>
5771 <h4>Element Selectors:</h4>
5772 <ul class="list">
5773     <li> <b>*</b> any element</li>
5774     <li> <b>E</b> an element with the tag E</li>
5775     <li> <b>E F</b> All descendent elements of E that have the tag F</li>
5776     <li> <b>E > F</b> or <b>E/F</b> all direct children elements of E that have the tag F</li>
5777     <li> <b>E + F</b> all elements with the tag F that are immediately preceded by an element with the tag E</li>
5778     <li> <b>E ~ F</b> all elements with the tag F that are preceded by a sibling element with the tag E</li>
5779 </ul>
5780 <h4>Attribute Selectors:</h4>
5781 <p>The use of @ and quotes are optional. For example, div[@foo='bar'] is also a valid attribute selector.</p>
5782 <ul class="list">
5783     <li> <b>E[foo]</b> has an attribute "foo"</li>
5784     <li> <b>E[foo=bar]</b> has an attribute "foo" that equals "bar"</li>
5785     <li> <b>E[foo^=bar]</b> has an attribute "foo" that starts with "bar"</li>
5786     <li> <b>E[foo$=bar]</b> has an attribute "foo" that ends with "bar"</li>
5787     <li> <b>E[foo*=bar]</b> has an attribute "foo" that contains the substring "bar"</li>
5788     <li> <b>E[foo%=2]</b> has an attribute "foo" that is evenly divisible by 2</li>
5789     <li> <b>E[foo!=bar]</b> has an attribute "foo" that does not equal "bar"</li>
5790 </ul>
5791 <h4>Pseudo Classes:</h4>
5792 <ul class="list">
5793     <li> <b>E:first-child</b> E is the first child of its parent</li>
5794     <li> <b>E:last-child</b> E is the last child of its parent</li>
5795     <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>
5796     <li> <b>E:nth-child(odd)</b> E is an odd child of its parent</li>
5797     <li> <b>E:nth-child(even)</b> E is an even child of its parent</li>
5798     <li> <b>E:only-child</b> E is the only child of its parent</li>
5799     <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>
5800     <li> <b>E:first</b> the first E in the resultset</li>
5801     <li> <b>E:last</b> the last E in the resultset</li>
5802     <li> <b>E:nth(<i>n</i>)</b> the <i>n</i>th E in the resultset (1 based)</li>
5803     <li> <b>E:odd</b> shortcut for :nth-child(odd)</li>
5804     <li> <b>E:even</b> shortcut for :nth-child(even)</li>
5805     <li> <b>E:contains(foo)</b> E's innerHTML contains the substring "foo"</li>
5806     <li> <b>E:nodeValue(foo)</b> E contains a textNode with a nodeValue that equals "foo"</li>
5807     <li> <b>E:not(S)</b> an E element that does not match simple selector S</li>
5808     <li> <b>E:has(S)</b> an E element that has a descendent that matches simple selector S</li>
5809     <li> <b>E:next(S)</b> an E element whose next sibling matches simple selector S</li>
5810     <li> <b>E:prev(S)</b> an E element whose previous sibling matches simple selector S</li>
5811 </ul>
5812 <h4>CSS Value Selectors:</h4>
5813 <ul class="list">
5814     <li> <b>E{display=none}</b> css value "display" that equals "none"</li>
5815     <li> <b>E{display^=none}</b> css value "display" that starts with "none"</li>
5816     <li> <b>E{display$=none}</b> css value "display" that ends with "none"</li>
5817     <li> <b>E{display*=none}</b> css value "display" that contains the substring "none"</li>
5818     <li> <b>E{display%=2}</b> css value "display" that is evenly divisible by 2</li>
5819     <li> <b>E{display!=none}</b> css value "display" that does not equal "none"</li>
5820 </ul>
5821  * @static
5822  */
5823 Roo.DomQuery = function(){
5824     var cache = {}, simpleCache = {}, valueCache = {};
5825     var nonSpace = /\S/;
5826     var trimRe = /^\s+|\s+$/g;
5827     var tplRe = /\{(\d+)\}/g;
5828     var modeRe = /^(\s?[\/>+~]\s?|\s|$)/;
5829     var tagTokenRe = /^(#)?([\w-\*]+)/;
5830     var nthRe = /(\d*)n\+?(\d*)/, nthRe2 = /\D/;
5831
5832     function child(p, index){
5833         var i = 0;
5834         var n = p.firstChild;
5835         while(n){
5836             if(n.nodeType == 1){
5837                if(++i == index){
5838                    return n;
5839                }
5840             }
5841             n = n.nextSibling;
5842         }
5843         return null;
5844     };
5845
5846     function next(n){
5847         while((n = n.nextSibling) && n.nodeType != 1);
5848         return n;
5849     };
5850
5851     function prev(n){
5852         while((n = n.previousSibling) && n.nodeType != 1);
5853         return n;
5854     };
5855
5856     function children(d){
5857         var n = d.firstChild, ni = -1;
5858             while(n){
5859                 var nx = n.nextSibling;
5860                 if(n.nodeType == 3 && !nonSpace.test(n.nodeValue)){
5861                     d.removeChild(n);
5862                 }else{
5863                     n.nodeIndex = ++ni;
5864                 }
5865                 n = nx;
5866             }
5867             return this;
5868         };
5869
5870     function byClassName(c, a, v){
5871         if(!v){
5872             return c;
5873         }
5874         var r = [], ri = -1, cn;
5875         for(var i = 0, ci; ci = c[i]; i++){
5876             
5877             
5878             if((' '+
5879                 ( (ci instanceof SVGElement) ? ci.className.baseVal : ci.className)
5880                  +' ').indexOf(v) != -1){
5881                 r[++ri] = ci;
5882             }
5883         }
5884         return r;
5885     };
5886
5887     function attrValue(n, attr){
5888         if(!n.tagName && typeof n.length != "undefined"){
5889             n = n[0];
5890         }
5891         if(!n){
5892             return null;
5893         }
5894         if(attr == "for"){
5895             return n.htmlFor;
5896         }
5897         if(attr == "class" || attr == "className"){
5898             return (n instanceof SVGElement) ? n.className.baseVal : n.className;
5899         }
5900         return n.getAttribute(attr) || n[attr];
5901
5902     };
5903
5904     function getNodes(ns, mode, tagName){
5905         var result = [], ri = -1, cs;
5906         if(!ns){
5907             return result;
5908         }
5909         tagName = tagName || "*";
5910         if(typeof ns.getElementsByTagName != "undefined"){
5911             ns = [ns];
5912         }
5913         if(!mode){
5914             for(var i = 0, ni; ni = ns[i]; i++){
5915                 cs = ni.getElementsByTagName(tagName);
5916                 for(var j = 0, ci; ci = cs[j]; j++){
5917                     result[++ri] = ci;
5918                 }
5919             }
5920         }else if(mode == "/" || mode == ">"){
5921             var utag = tagName.toUpperCase();
5922             for(var i = 0, ni, cn; ni = ns[i]; i++){
5923                 cn = ni.children || ni.childNodes;
5924                 for(var j = 0, cj; cj = cn[j]; j++){
5925                     if(cj.nodeName == utag || cj.nodeName == tagName  || tagName == '*'){
5926                         result[++ri] = cj;
5927                     }
5928                 }
5929             }
5930         }else if(mode == "+"){
5931             var utag = tagName.toUpperCase();
5932             for(var i = 0, n; n = ns[i]; i++){
5933                 while((n = n.nextSibling) && n.nodeType != 1);
5934                 if(n && (n.nodeName == utag || n.nodeName == tagName || tagName == '*')){
5935                     result[++ri] = n;
5936                 }
5937             }
5938         }else if(mode == "~"){
5939             for(var i = 0, n; n = ns[i]; i++){
5940                 while((n = n.nextSibling) && (n.nodeType != 1 || (tagName == '*' || n.tagName.toLowerCase()!=tagName)));
5941                 if(n){
5942                     result[++ri] = n;
5943                 }
5944             }
5945         }
5946         return result;
5947     };
5948
5949     function concat(a, b){
5950         if(b.slice){
5951             return a.concat(b);
5952         }
5953         for(var i = 0, l = b.length; i < l; i++){
5954             a[a.length] = b[i];
5955         }
5956         return a;
5957     }
5958
5959     function byTag(cs, tagName){
5960         if(cs.tagName || cs == document){
5961             cs = [cs];
5962         }
5963         if(!tagName){
5964             return cs;
5965         }
5966         var r = [], ri = -1;
5967         tagName = tagName.toLowerCase();
5968         for(var i = 0, ci; ci = cs[i]; i++){
5969             if(ci.nodeType == 1 && ci.tagName.toLowerCase()==tagName){
5970                 r[++ri] = ci;
5971             }
5972         }
5973         return r;
5974     };
5975
5976     function byId(cs, attr, id){
5977         if(cs.tagName || cs == document){
5978             cs = [cs];
5979         }
5980         if(!id){
5981             return cs;
5982         }
5983         var r = [], ri = -1;
5984         for(var i = 0,ci; ci = cs[i]; i++){
5985             if(ci && ci.id == id){
5986                 r[++ri] = ci;
5987                 return r;
5988             }
5989         }
5990         return r;
5991     };
5992
5993     function byAttribute(cs, attr, value, op, custom){
5994         var r = [], ri = -1, st = custom=="{";
5995         var f = Roo.DomQuery.operators[op];
5996         for(var i = 0, ci; ci = cs[i]; i++){
5997             var a;
5998             if(st){
5999                 a = Roo.DomQuery.getStyle(ci, attr);
6000             }
6001             else if(attr == "class" || attr == "className"){
6002                 a = (ci instanceof SVGElement) ? ci.className.baseVal : ci.className;
6003             }else if(attr == "for"){
6004                 a = ci.htmlFor;
6005             }else if(attr == "href"){
6006                 a = ci.getAttribute("href", 2);
6007             }else{
6008                 a = ci.getAttribute(attr);
6009             }
6010             if((f && f(a, value)) || (!f && a)){
6011                 r[++ri] = ci;
6012             }
6013         }
6014         return r;
6015     };
6016
6017     function byPseudo(cs, name, value){
6018         return Roo.DomQuery.pseudos[name](cs, value);
6019     };
6020
6021     // This is for IE MSXML which does not support expandos.
6022     // IE runs the same speed using setAttribute, however FF slows way down
6023     // and Safari completely fails so they need to continue to use expandos.
6024     var isIE = window.ActiveXObject ? true : false;
6025
6026     // this eval is stop the compressor from
6027     // renaming the variable to something shorter
6028     
6029     /** eval:var:batch */
6030     var batch = 30803; 
6031
6032     var key = 30803;
6033
6034     function nodupIEXml(cs){
6035         var d = ++key;
6036         cs[0].setAttribute("_nodup", d);
6037         var r = [cs[0]];
6038         for(var i = 1, len = cs.length; i < len; i++){
6039             var c = cs[i];
6040             if(!c.getAttribute("_nodup") != d){
6041                 c.setAttribute("_nodup", d);
6042                 r[r.length] = c;
6043             }
6044         }
6045         for(var i = 0, len = cs.length; i < len; i++){
6046             cs[i].removeAttribute("_nodup");
6047         }
6048         return r;
6049     }
6050
6051     function nodup(cs){
6052         if(!cs){
6053             return [];
6054         }
6055         var len = cs.length, c, i, r = cs, cj, ri = -1;
6056         if(!len || typeof cs.nodeType != "undefined" || len == 1){
6057             return cs;
6058         }
6059         if(isIE && typeof cs[0].selectSingleNode != "undefined"){
6060             return nodupIEXml(cs);
6061         }
6062         var d = ++key;
6063         cs[0]._nodup = d;
6064         for(i = 1; c = cs[i]; i++){
6065             if(c._nodup != d){
6066                 c._nodup = d;
6067             }else{
6068                 r = [];
6069                 for(var j = 0; j < i; j++){
6070                     r[++ri] = cs[j];
6071                 }
6072                 for(j = i+1; cj = cs[j]; j++){
6073                     if(cj._nodup != d){
6074                         cj._nodup = d;
6075                         r[++ri] = cj;
6076                     }
6077                 }
6078                 return r;
6079             }
6080         }
6081         return r;
6082     }
6083
6084     function quickDiffIEXml(c1, c2){
6085         var d = ++key;
6086         for(var i = 0, len = c1.length; i < len; i++){
6087             c1[i].setAttribute("_qdiff", d);
6088         }
6089         var r = [];
6090         for(var i = 0, len = c2.length; i < len; i++){
6091             if(c2[i].getAttribute("_qdiff") != d){
6092                 r[r.length] = c2[i];
6093             }
6094         }
6095         for(var i = 0, len = c1.length; i < len; i++){
6096            c1[i].removeAttribute("_qdiff");
6097         }
6098         return r;
6099     }
6100
6101     function quickDiff(c1, c2){
6102         var len1 = c1.length;
6103         if(!len1){
6104             return c2;
6105         }
6106         if(isIE && c1[0].selectSingleNode){
6107             return quickDiffIEXml(c1, c2);
6108         }
6109         var d = ++key;
6110         for(var i = 0; i < len1; i++){
6111             c1[i]._qdiff = d;
6112         }
6113         var r = [];
6114         for(var i = 0, len = c2.length; i < len; i++){
6115             if(c2[i]._qdiff != d){
6116                 r[r.length] = c2[i];
6117             }
6118         }
6119         return r;
6120     }
6121
6122     function quickId(ns, mode, root, id){
6123         if(ns == root){
6124            var d = root.ownerDocument || root;
6125            return d.getElementById(id);
6126         }
6127         ns = getNodes(ns, mode, "*");
6128         return byId(ns, null, id);
6129     }
6130
6131     return {
6132         getStyle : function(el, name){
6133             return Roo.fly(el).getStyle(name);
6134         },
6135         /**
6136          * Compiles a selector/xpath query into a reusable function. The returned function
6137          * takes one parameter "root" (optional), which is the context node from where the query should start.
6138          * @param {String} selector The selector/xpath query
6139          * @param {String} type (optional) Either "select" (the default) or "simple" for a simple selector match
6140          * @return {Function}
6141          */
6142         compile : function(path, type){
6143             type = type || "select";
6144             
6145             var fn = ["var f = function(root){\n var mode; ++batch; var n = root || document;\n"];
6146             var q = path, mode, lq;
6147             var tk = Roo.DomQuery.matchers;
6148             var tklen = tk.length;
6149             var mm;
6150
6151             // accept leading mode switch
6152             var lmode = q.match(modeRe);
6153             if(lmode && lmode[1]){
6154                 fn[fn.length] = 'mode="'+lmode[1].replace(trimRe, "")+'";';
6155                 q = q.replace(lmode[1], "");
6156             }
6157             // strip leading slashes
6158             while(path.substr(0, 1)=="/"){
6159                 path = path.substr(1);
6160             }
6161
6162             while(q && lq != q){
6163                 lq = q;
6164                 var tm = q.match(tagTokenRe);
6165                 if(type == "select"){
6166                     if(tm){
6167                         if(tm[1] == "#"){
6168                             fn[fn.length] = 'n = quickId(n, mode, root, "'+tm[2]+'");';
6169                         }else{
6170                             fn[fn.length] = 'n = getNodes(n, mode, "'+tm[2]+'");';
6171                         }
6172                         q = q.replace(tm[0], "");
6173                     }else if(q.substr(0, 1) != '@'){
6174                         fn[fn.length] = 'n = getNodes(n, mode, "*");';
6175                     }
6176                 }else{
6177                     if(tm){
6178                         if(tm[1] == "#"){
6179                             fn[fn.length] = 'n = byId(n, null, "'+tm[2]+'");';
6180                         }else{
6181                             fn[fn.length] = 'n = byTag(n, "'+tm[2]+'");';
6182                         }
6183                         q = q.replace(tm[0], "");
6184                     }
6185                 }
6186                 while(!(mm = q.match(modeRe))){
6187                     var matched = false;
6188                     for(var j = 0; j < tklen; j++){
6189                         var t = tk[j];
6190                         var m = q.match(t.re);
6191                         if(m){
6192                             fn[fn.length] = t.select.replace(tplRe, function(x, i){
6193                                                     return m[i];
6194                                                 });
6195                             q = q.replace(m[0], "");
6196                             matched = true;
6197                             break;
6198                         }
6199                     }
6200                     // prevent infinite loop on bad selector
6201                     if(!matched){
6202                         throw 'Error parsing selector, parsing failed at "' + q + '"';
6203                     }
6204                 }
6205                 if(mm[1]){
6206                     fn[fn.length] = 'mode="'+mm[1].replace(trimRe, "")+'";';
6207                     q = q.replace(mm[1], "");
6208                 }
6209             }
6210             fn[fn.length] = "return nodup(n);\n}";
6211             
6212              /** 
6213               * list of variables that need from compression as they are used by eval.
6214              *  eval:var:batch 
6215              *  eval:var:nodup
6216              *  eval:var:byTag
6217              *  eval:var:ById
6218              *  eval:var:getNodes
6219              *  eval:var:quickId
6220              *  eval:var:mode
6221              *  eval:var:root
6222              *  eval:var:n
6223              *  eval:var:byClassName
6224              *  eval:var:byPseudo
6225              *  eval:var:byAttribute
6226              *  eval:var:attrValue
6227              * 
6228              **/ 
6229             eval(fn.join(""));
6230             return f;
6231         },
6232
6233         /**
6234          * Selects a group of elements.
6235          * @param {String} selector The selector/xpath query (can be a comma separated list of selectors)
6236          * @param {Node} root (optional) The start of the query (defaults to document).
6237          * @return {Array}
6238          */
6239         select : function(path, root, type){
6240             if(!root || root == document){
6241                 root = document;
6242             }
6243             if(typeof root == "string"){
6244                 root = document.getElementById(root);
6245             }
6246             var paths = path.split(",");
6247             var results = [];
6248             for(var i = 0, len = paths.length; i < len; i++){
6249                 var p = paths[i].replace(trimRe, "");
6250                 if(!cache[p]){
6251                     cache[p] = Roo.DomQuery.compile(p);
6252                     if(!cache[p]){
6253                         throw p + " is not a valid selector";
6254                     }
6255                 }
6256                 var result = cache[p](root);
6257                 if(result && result != document){
6258                     results = results.concat(result);
6259                 }
6260             }
6261             if(paths.length > 1){
6262                 return nodup(results);
6263             }
6264             return results;
6265         },
6266
6267         /**
6268          * Selects a single element.
6269          * @param {String} selector The selector/xpath query
6270          * @param {Node} root (optional) The start of the query (defaults to document).
6271          * @return {Element}
6272          */
6273         selectNode : function(path, root){
6274             return Roo.DomQuery.select(path, root)[0];
6275         },
6276
6277         /**
6278          * Selects the value of a node, optionally replacing null with the defaultValue.
6279          * @param {String} selector The selector/xpath query
6280          * @param {Node} root (optional) The start of the query (defaults to document).
6281          * @param {String} defaultValue
6282          */
6283         selectValue : function(path, root, defaultValue){
6284             path = path.replace(trimRe, "");
6285             if(!valueCache[path]){
6286                 valueCache[path] = Roo.DomQuery.compile(path, "select");
6287             }
6288             var n = valueCache[path](root);
6289             n = n[0] ? n[0] : n;
6290             var v = (n && n.firstChild ? n.firstChild.nodeValue : null);
6291             return ((v === null||v === undefined||v==='') ? defaultValue : v);
6292         },
6293
6294         /**
6295          * Selects the value of a node, parsing integers and floats.
6296          * @param {String} selector The selector/xpath query
6297          * @param {Node} root (optional) The start of the query (defaults to document).
6298          * @param {Number} defaultValue
6299          * @return {Number}
6300          */
6301         selectNumber : function(path, root, defaultValue){
6302             var v = Roo.DomQuery.selectValue(path, root, defaultValue || 0);
6303             return parseFloat(v);
6304         },
6305
6306         /**
6307          * Returns true if the passed element(s) match the passed simple selector (e.g. div.some-class or span:first-child)
6308          * @param {String/HTMLElement/Array} el An element id, element or array of elements
6309          * @param {String} selector The simple selector to test
6310          * @return {Boolean}
6311          */
6312         is : function(el, ss){
6313             if(typeof el == "string"){
6314                 el = document.getElementById(el);
6315             }
6316             var isArray = (el instanceof Array);
6317             var result = Roo.DomQuery.filter(isArray ? el : [el], ss);
6318             return isArray ? (result.length == el.length) : (result.length > 0);
6319         },
6320
6321         /**
6322          * Filters an array of elements to only include matches of a simple selector (e.g. div.some-class or span:first-child)
6323          * @param {Array} el An array of elements to filter
6324          * @param {String} selector The simple selector to test
6325          * @param {Boolean} nonMatches If true, it returns the elements that DON'T match
6326          * the selector instead of the ones that match
6327          * @return {Array}
6328          */
6329         filter : function(els, ss, nonMatches){
6330             ss = ss.replace(trimRe, "");
6331             if(!simpleCache[ss]){
6332                 simpleCache[ss] = Roo.DomQuery.compile(ss, "simple");
6333             }
6334             var result = simpleCache[ss](els);
6335             return nonMatches ? quickDiff(result, els) : result;
6336         },
6337
6338         /**
6339          * Collection of matching regular expressions and code snippets.
6340          */
6341         matchers : [{
6342                 re: /^\.([\w-]+)/,
6343                 select: 'n = byClassName(n, null, " {1} ");'
6344             }, {
6345                 re: /^\:([\w-]+)(?:\(((?:[^\s>\/]*|.*?))\))?/,
6346                 select: 'n = byPseudo(n, "{1}", "{2}");'
6347             },{
6348                 re: /^(?:([\[\{])(?:@)?([\w-]+)\s?(?:(=|.=)\s?['"]?(.*?)["']?)?[\]\}])/,
6349                 select: 'n = byAttribute(n, "{2}", "{4}", "{3}", "{1}");'
6350             }, {
6351                 re: /^#([\w-]+)/,
6352                 select: 'n = byId(n, null, "{1}");'
6353             },{
6354                 re: /^@([\w-]+)/,
6355                 select: 'return {firstChild:{nodeValue:attrValue(n, "{1}")}};'
6356             }
6357         ],
6358
6359         /**
6360          * Collection of operator comparison functions. The default operators are =, !=, ^=, $=, *=, %=, |= and ~=.
6361          * 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;.
6362          */
6363         operators : {
6364             "=" : function(a, v){
6365                 return a == v;
6366             },
6367             "!=" : function(a, v){
6368                 return a != v;
6369             },
6370             "^=" : function(a, v){
6371                 return a && a.substr(0, v.length) == v;
6372             },
6373             "$=" : function(a, v){
6374                 return a && a.substr(a.length-v.length) == v;
6375             },
6376             "*=" : function(a, v){
6377                 return a && a.indexOf(v) !== -1;
6378             },
6379             "%=" : function(a, v){
6380                 return (a % v) == 0;
6381             },
6382             "|=" : function(a, v){
6383                 return a && (a == v || a.substr(0, v.length+1) == v+'-');
6384             },
6385             "~=" : function(a, v){
6386                 return a && (' '+a+' ').indexOf(' '+v+' ') != -1;
6387             }
6388         },
6389
6390         /**
6391          * Collection of "pseudo class" processors. Each processor is passed the current nodeset (array)
6392          * and the argument (if any) supplied in the selector.
6393          */
6394         pseudos : {
6395             "first-child" : function(c){
6396                 var r = [], ri = -1, n;
6397                 for(var i = 0, ci; ci = n = c[i]; i++){
6398                     while((n = n.previousSibling) && n.nodeType != 1);
6399                     if(!n){
6400                         r[++ri] = ci;
6401                     }
6402                 }
6403                 return r;
6404             },
6405
6406             "last-child" : function(c){
6407                 var r = [], ri = -1, n;
6408                 for(var i = 0, ci; ci = n = c[i]; i++){
6409                     while((n = n.nextSibling) && n.nodeType != 1);
6410                     if(!n){
6411                         r[++ri] = ci;
6412                     }
6413                 }
6414                 return r;
6415             },
6416
6417             "nth-child" : function(c, a) {
6418                 var r = [], ri = -1;
6419                 var m = nthRe.exec(a == "even" && "2n" || a == "odd" && "2n+1" || !nthRe2.test(a) && "n+" + a || a);
6420                 var f = (m[1] || 1) - 0, l = m[2] - 0;
6421                 for(var i = 0, n; n = c[i]; i++){
6422                     var pn = n.parentNode;
6423                     if (batch != pn._batch) {
6424                         var j = 0;
6425                         for(var cn = pn.firstChild; cn; cn = cn.nextSibling){
6426                             if(cn.nodeType == 1){
6427                                cn.nodeIndex = ++j;
6428                             }
6429                         }
6430                         pn._batch = batch;
6431                     }
6432                     if (f == 1) {
6433                         if (l == 0 || n.nodeIndex == l){
6434                             r[++ri] = n;
6435                         }
6436                     } else if ((n.nodeIndex + l) % f == 0){
6437                         r[++ri] = n;
6438                     }
6439                 }
6440
6441                 return r;
6442             },
6443
6444             "only-child" : function(c){
6445                 var r = [], ri = -1;;
6446                 for(var i = 0, ci; ci = c[i]; i++){
6447                     if(!prev(ci) && !next(ci)){
6448                         r[++ri] = ci;
6449                     }
6450                 }
6451                 return r;
6452             },
6453
6454             "empty" : function(c){
6455                 var r = [], ri = -1;
6456                 for(var i = 0, ci; ci = c[i]; i++){
6457                     var cns = ci.childNodes, j = 0, cn, empty = true;
6458                     while(cn = cns[j]){
6459                         ++j;
6460                         if(cn.nodeType == 1 || cn.nodeType == 3){
6461                             empty = false;
6462                             break;
6463                         }
6464                     }
6465                     if(empty){
6466                         r[++ri] = ci;
6467                     }
6468                 }
6469                 return r;
6470             },
6471
6472             "contains" : function(c, v){
6473                 var r = [], ri = -1;
6474                 for(var i = 0, ci; ci = c[i]; i++){
6475                     if((ci.textContent||ci.innerText||'').indexOf(v) != -1){
6476                         r[++ri] = ci;
6477                     }
6478                 }
6479                 return r;
6480             },
6481
6482             "nodeValue" : function(c, v){
6483                 var r = [], ri = -1;
6484                 for(var i = 0, ci; ci = c[i]; i++){
6485                     if(ci.firstChild && ci.firstChild.nodeValue == v){
6486                         r[++ri] = ci;
6487                     }
6488                 }
6489                 return r;
6490             },
6491
6492             "checked" : function(c){
6493                 var r = [], ri = -1;
6494                 for(var i = 0, ci; ci = c[i]; i++){
6495                     if(ci.checked == true){
6496                         r[++ri] = ci;
6497                     }
6498                 }
6499                 return r;
6500             },
6501
6502             "not" : function(c, ss){
6503                 return Roo.DomQuery.filter(c, ss, true);
6504             },
6505
6506             "odd" : function(c){
6507                 return this["nth-child"](c, "odd");
6508             },
6509
6510             "even" : function(c){
6511                 return this["nth-child"](c, "even");
6512             },
6513
6514             "nth" : function(c, a){
6515                 return c[a-1] || [];
6516             },
6517
6518             "first" : function(c){
6519                 return c[0] || [];
6520             },
6521
6522             "last" : function(c){
6523                 return c[c.length-1] || [];
6524             },
6525
6526             "has" : function(c, ss){
6527                 var s = Roo.DomQuery.select;
6528                 var r = [], ri = -1;
6529                 for(var i = 0, ci; ci = c[i]; i++){
6530                     if(s(ss, ci).length > 0){
6531                         r[++ri] = ci;
6532                     }
6533                 }
6534                 return r;
6535             },
6536
6537             "next" : function(c, ss){
6538                 var is = Roo.DomQuery.is;
6539                 var r = [], ri = -1;
6540                 for(var i = 0, ci; ci = c[i]; i++){
6541                     var n = next(ci);
6542                     if(n && is(n, ss)){
6543                         r[++ri] = ci;
6544                     }
6545                 }
6546                 return r;
6547             },
6548
6549             "prev" : function(c, ss){
6550                 var is = Roo.DomQuery.is;
6551                 var r = [], ri = -1;
6552                 for(var i = 0, ci; ci = c[i]; i++){
6553                     var n = prev(ci);
6554                     if(n && is(n, ss)){
6555                         r[++ri] = ci;
6556                     }
6557                 }
6558                 return r;
6559             }
6560         }
6561     };
6562 }();
6563
6564 /**
6565  * Selects an array of DOM nodes by CSS/XPath selector. Shorthand of {@link Roo.DomQuery#select}
6566  * @param {String} path The selector/xpath query
6567  * @param {Node} root (optional) The start of the query (defaults to document).
6568  * @return {Array}
6569  * @member Roo
6570  * @method query
6571  */
6572 Roo.query = Roo.DomQuery.select;
6573 /*
6574  * Based on:
6575  * Ext JS Library 1.1.1
6576  * Copyright(c) 2006-2007, Ext JS, LLC.
6577  *
6578  * Originally Released Under LGPL - original licence link has changed is not relivant.
6579  *
6580  * Fork - LGPL
6581  * <script type="text/javascript">
6582  */
6583
6584 /**
6585  * @class Roo.util.Observable
6586  * Base class that provides a common interface for publishing events. Subclasses are expected to
6587  * to have a property "events" with all the events defined.<br>
6588  * For example:
6589  * <pre><code>
6590  Employee = function(name){
6591     this.name = name;
6592     this.addEvents({
6593         "fired" : true,
6594         "quit" : true
6595     });
6596  }
6597  Roo.extend(Employee, Roo.util.Observable);
6598 </code></pre>
6599  * @param {Object} config properties to use (incuding events / listeners)
6600  */
6601
6602 Roo.util.Observable = function(cfg){
6603     
6604     cfg = cfg|| {};
6605     this.addEvents(cfg.events || {});
6606     if (cfg.events) {
6607         delete cfg.events; // make sure
6608     }
6609      
6610     Roo.apply(this, cfg);
6611     
6612     if(this.listeners){
6613         this.on(this.listeners);
6614         delete this.listeners;
6615     }
6616 };
6617 Roo.util.Observable.prototype = {
6618     /** 
6619  * @cfg {Object} listeners  list of events and functions to call for this object, 
6620  * For example :
6621  * <pre><code>
6622     listeners :  { 
6623        'click' : function(e) {
6624            ..... 
6625         } ,
6626         .... 
6627     } 
6628   </code></pre>
6629  */
6630     
6631     
6632     /**
6633      * Fires the specified event with the passed parameters (minus the event name).
6634      * @param {String} eventName
6635      * @param {Object...} args Variable number of parameters are passed to handlers
6636      * @return {Boolean} returns false if any of the handlers return false otherwise it returns true
6637      */
6638     fireEvent : function(){
6639         var ce = this.events[arguments[0].toLowerCase()];
6640         if(typeof ce == "object"){
6641             return ce.fire.apply(ce, Array.prototype.slice.call(arguments, 1));
6642         }else{
6643             return true;
6644         }
6645     },
6646
6647     // private
6648     filterOptRe : /^(?:scope|delay|buffer|single)$/,
6649
6650     /**
6651      * Appends an event handler to this component
6652      * @param {String}   eventName The type of event to listen for
6653      * @param {Function} handler The method the event invokes
6654      * @param {Object}   scope (optional) The scope in which to execute the handler
6655      * function. The handler function's "this" context.
6656      * @param {Object}   options (optional) An object containing handler configuration
6657      * properties. This may contain any of the following properties:<ul>
6658      * <li>scope {Object} The scope in which to execute the handler function. The handler function's "this" context.</li>
6659      * <li>delay {Number} The number of milliseconds to delay the invocation of the handler after te event fires.</li>
6660      * <li>single {Boolean} True to add a handler to handle just the next firing of the event, and then remove itself.</li>
6661      * <li>buffer {Number} Causes the handler to be scheduled to run in an {@link Roo.util.DelayedTask} delayed
6662      * by the specified number of milliseconds. If the event fires again within that time, the original
6663      * handler is <em>not</em> invoked, but the new handler is scheduled in its place.</li>
6664      * </ul><br>
6665      * <p>
6666      * <b>Combining Options</b><br>
6667      * Using the options argument, it is possible to combine different types of listeners:<br>
6668      * <br>
6669      * A normalized, delayed, one-time listener that auto stops the event and passes a custom argument (forumId)
6670                 <pre><code>
6671                 el.on('click', this.onClick, this, {
6672                         single: true,
6673                 delay: 100,
6674                 forumId: 4
6675                 });
6676                 </code></pre>
6677      * <p>
6678      * <b>Attaching multiple handlers in 1 call</b><br>
6679      * The method also allows for a single argument to be passed which is a config object containing properties
6680      * which specify multiple handlers.
6681      * <pre><code>
6682                 el.on({
6683                         'click': {
6684                         fn: this.onClick,
6685                         scope: this,
6686                         delay: 100
6687                 }, 
6688                 'mouseover': {
6689                         fn: this.onMouseOver,
6690                         scope: this
6691                 },
6692                 'mouseout': {
6693                         fn: this.onMouseOut,
6694                         scope: this
6695                 }
6696                 });
6697                 </code></pre>
6698      * <p>
6699      * Or a shorthand syntax which passes the same scope object to all handlers:
6700         <pre><code>
6701                 el.on({
6702                         'click': this.onClick,
6703                 'mouseover': this.onMouseOver,
6704                 'mouseout': this.onMouseOut,
6705                 scope: this
6706                 });
6707                 </code></pre>
6708      */
6709     addListener : function(eventName, fn, scope, o){
6710         if(typeof eventName == "object"){
6711             o = eventName;
6712             for(var e in o){
6713                 if(this.filterOptRe.test(e)){
6714                     continue;
6715                 }
6716                 if(typeof o[e] == "function"){
6717                     // shared options
6718                     this.addListener(e, o[e], o.scope,  o);
6719                 }else{
6720                     // individual options
6721                     this.addListener(e, o[e].fn, o[e].scope, o[e]);
6722                 }
6723             }
6724             return;
6725         }
6726         o = (!o || typeof o == "boolean") ? {} : o;
6727         eventName = eventName.toLowerCase();
6728         var ce = this.events[eventName] || true;
6729         if(typeof ce == "boolean"){
6730             ce = new Roo.util.Event(this, eventName);
6731             this.events[eventName] = ce;
6732         }
6733         ce.addListener(fn, scope, o);
6734     },
6735
6736     /**
6737      * Removes a listener
6738      * @param {String}   eventName     The type of event to listen for
6739      * @param {Function} handler        The handler to remove
6740      * @param {Object}   scope  (optional) The scope (this object) for the handler
6741      */
6742     removeListener : function(eventName, fn, scope){
6743         var ce = this.events[eventName.toLowerCase()];
6744         if(typeof ce == "object"){
6745             ce.removeListener(fn, scope);
6746         }
6747     },
6748
6749     /**
6750      * Removes all listeners for this object
6751      */
6752     purgeListeners : function(){
6753         for(var evt in this.events){
6754             if(typeof this.events[evt] == "object"){
6755                  this.events[evt].clearListeners();
6756             }
6757         }
6758     },
6759
6760     relayEvents : function(o, events){
6761         var createHandler = function(ename){
6762             return function(){
6763                  
6764                 return this.fireEvent.apply(this, Roo.combine(ename, Array.prototype.slice.call(arguments, 0)));
6765             };
6766         };
6767         for(var i = 0, len = events.length; i < len; i++){
6768             var ename = events[i];
6769             if(!this.events[ename]){
6770                 this.events[ename] = true;
6771             };
6772             o.on(ename, createHandler(ename), this);
6773         }
6774     },
6775
6776     /**
6777      * Used to define events on this Observable
6778      * @param {Object} object The object with the events defined
6779      */
6780     addEvents : function(o){
6781         if(!this.events){
6782             this.events = {};
6783         }
6784         Roo.applyIf(this.events, o);
6785     },
6786
6787     /**
6788      * Checks to see if this object has any listeners for a specified event
6789      * @param {String} eventName The name of the event to check for
6790      * @return {Boolean} True if the event is being listened for, else false
6791      */
6792     hasListener : function(eventName){
6793         var e = this.events[eventName];
6794         return typeof e == "object" && e.listeners.length > 0;
6795     }
6796 };
6797 /**
6798  * Appends an event handler to this element (shorthand for addListener)
6799  * @param {String}   eventName     The type of event to listen for
6800  * @param {Function} handler        The method the event invokes
6801  * @param {Object}   scope (optional) The scope in which to execute the handler
6802  * function. The handler function's "this" context.
6803  * @param {Object}   options  (optional)
6804  * @method
6805  */
6806 Roo.util.Observable.prototype.on = Roo.util.Observable.prototype.addListener;
6807 /**
6808  * Removes a listener (shorthand for removeListener)
6809  * @param {String}   eventName     The type of event to listen for
6810  * @param {Function} handler        The handler to remove
6811  * @param {Object}   scope  (optional) The scope (this object) for the handler
6812  * @method
6813  */
6814 Roo.util.Observable.prototype.un = Roo.util.Observable.prototype.removeListener;
6815
6816 /**
6817  * Starts capture on the specified Observable. All events will be passed
6818  * to the supplied function with the event name + standard signature of the event
6819  * <b>before</b> the event is fired. If the supplied function returns false,
6820  * the event will not fire.
6821  * @param {Observable} o The Observable to capture
6822  * @param {Function} fn The function to call
6823  * @param {Object} scope (optional) The scope (this object) for the fn
6824  * @static
6825  */
6826 Roo.util.Observable.capture = function(o, fn, scope){
6827     o.fireEvent = o.fireEvent.createInterceptor(fn, scope);
6828 };
6829
6830 /**
6831  * Removes <b>all</b> added captures from the Observable.
6832  * @param {Observable} o The Observable to release
6833  * @static
6834  */
6835 Roo.util.Observable.releaseCapture = function(o){
6836     o.fireEvent = Roo.util.Observable.prototype.fireEvent;
6837 };
6838
6839 (function(){
6840
6841     var createBuffered = function(h, o, scope){
6842         var task = new Roo.util.DelayedTask();
6843         return function(){
6844             task.delay(o.buffer, h, scope, Array.prototype.slice.call(arguments, 0));
6845         };
6846     };
6847
6848     var createSingle = function(h, e, fn, scope){
6849         return function(){
6850             e.removeListener(fn, scope);
6851             return h.apply(scope, arguments);
6852         };
6853     };
6854
6855     var createDelayed = function(h, o, scope){
6856         return function(){
6857             var args = Array.prototype.slice.call(arguments, 0);
6858             setTimeout(function(){
6859                 h.apply(scope, args);
6860             }, o.delay || 10);
6861         };
6862     };
6863
6864     Roo.util.Event = function(obj, name){
6865         this.name = name;
6866         this.obj = obj;
6867         this.listeners = [];
6868     };
6869
6870     Roo.util.Event.prototype = {
6871         addListener : function(fn, scope, options){
6872             var o = options || {};
6873             scope = scope || this.obj;
6874             if(!this.isListening(fn, scope)){
6875                 var l = {fn: fn, scope: scope, options: o};
6876                 var h = fn;
6877                 if(o.delay){
6878                     h = createDelayed(h, o, scope);
6879                 }
6880                 if(o.single){
6881                     h = createSingle(h, this, fn, scope);
6882                 }
6883                 if(o.buffer){
6884                     h = createBuffered(h, o, scope);
6885                 }
6886                 l.fireFn = h;
6887                 if(!this.firing){ // if we are currently firing this event, don't disturb the listener loop
6888                     this.listeners.push(l);
6889                 }else{
6890                     this.listeners = this.listeners.slice(0);
6891                     this.listeners.push(l);
6892                 }
6893             }
6894         },
6895
6896         findListener : function(fn, scope){
6897             scope = scope || this.obj;
6898             var ls = this.listeners;
6899             for(var i = 0, len = ls.length; i < len; i++){
6900                 var l = ls[i];
6901                 if(l.fn == fn && l.scope == scope){
6902                     return i;
6903                 }
6904             }
6905             return -1;
6906         },
6907
6908         isListening : function(fn, scope){
6909             return this.findListener(fn, scope) != -1;
6910         },
6911
6912         removeListener : function(fn, scope){
6913             var index;
6914             if((index = this.findListener(fn, scope)) != -1){
6915                 if(!this.firing){
6916                     this.listeners.splice(index, 1);
6917                 }else{
6918                     this.listeners = this.listeners.slice(0);
6919                     this.listeners.splice(index, 1);
6920                 }
6921                 return true;
6922             }
6923             return false;
6924         },
6925
6926         clearListeners : function(){
6927             this.listeners = [];
6928         },
6929
6930         fire : function(){
6931             var ls = this.listeners, scope, len = ls.length;
6932             if(len > 0){
6933                 this.firing = true;
6934                 var args = Array.prototype.slice.call(arguments, 0);                
6935                 for(var i = 0; i < len; i++){
6936                     var l = ls[i];
6937                     if(l.fireFn.apply(l.scope||this.obj||window, args) === false){
6938                         this.firing = false;
6939                         return false;
6940                     }
6941                 }
6942                 this.firing = false;
6943             }
6944             return true;
6945         }
6946     };
6947 })();/*
6948  * RooJS Library 
6949  * Copyright(c) 2007-2017, Roo J Solutions Ltd
6950  *
6951  * Licence LGPL 
6952  *
6953  */
6954  
6955 /**
6956  * @class Roo.Document
6957  * @extends Roo.util.Observable
6958  * This is a convience class to wrap up the main document loading code.. , rather than adding Roo.onReady(......)
6959  * 
6960  * @param {Object} config the methods and properties of the 'base' class for the application.
6961  * 
6962  *  Generic Page handler - implement this to start your app..
6963  * 
6964  * eg.
6965  *  MyProject = new Roo.Document({
6966         events : {
6967             'load' : true // your events..
6968         },
6969         listeners : {
6970             'ready' : function() {
6971                 // fired on Roo.onReady()
6972             }
6973         }
6974  * 
6975  */
6976 Roo.Document = function(cfg) {
6977      
6978     this.addEvents({ 
6979         'ready' : true
6980     });
6981     Roo.util.Observable.call(this,cfg);
6982     
6983     var _this = this;
6984     
6985     Roo.onReady(function() {
6986         _this.fireEvent('ready');
6987     },null,false);
6988     
6989     
6990 }
6991
6992 Roo.extend(Roo.Document, Roo.util.Observable, {});/*
6993  * Based on:
6994  * Ext JS Library 1.1.1
6995  * Copyright(c) 2006-2007, Ext JS, LLC.
6996  *
6997  * Originally Released Under LGPL - original licence link has changed is not relivant.
6998  *
6999  * Fork - LGPL
7000  * <script type="text/javascript">
7001  */
7002
7003 /**
7004  * @class Roo.EventManager
7005  * Registers event handlers that want to receive a normalized EventObject instead of the standard browser event and provides 
7006  * several useful events directly.
7007  * See {@link Roo.EventObject} for more details on normalized event objects.
7008  * @static
7009  */
7010 Roo.EventManager = function(){
7011     var docReadyEvent, docReadyProcId, docReadyState = false;
7012     var resizeEvent, resizeTask, textEvent, textSize;
7013     var E = Roo.lib.Event;
7014     var D = Roo.lib.Dom;
7015
7016     
7017     
7018
7019     var fireDocReady = function(){
7020         if(!docReadyState){
7021             docReadyState = true;
7022             Roo.isReady = true;
7023             if(docReadyProcId){
7024                 clearInterval(docReadyProcId);
7025             }
7026             if(Roo.isGecko || Roo.isOpera) {
7027                 document.removeEventListener("DOMContentLoaded", fireDocReady, false);
7028             }
7029             if(Roo.isIE){
7030                 var defer = document.getElementById("ie-deferred-loader");
7031                 if(defer){
7032                     defer.onreadystatechange = null;
7033                     defer.parentNode.removeChild(defer);
7034                 }
7035             }
7036             if(docReadyEvent){
7037                 docReadyEvent.fire();
7038                 docReadyEvent.clearListeners();
7039             }
7040         }
7041     };
7042     
7043     var initDocReady = function(){
7044         docReadyEvent = new Roo.util.Event();
7045         if(Roo.isGecko || Roo.isOpera) {
7046             document.addEventListener("DOMContentLoaded", fireDocReady, false);
7047         }else if(Roo.isIE){
7048             document.write("<s"+'cript id="ie-deferred-loader" defer="defer" src="/'+'/:"></s'+"cript>");
7049             var defer = document.getElementById("ie-deferred-loader");
7050             defer.onreadystatechange = function(){
7051                 if(this.readyState == "complete"){
7052                     fireDocReady();
7053                 }
7054             };
7055         }else if(Roo.isSafari){ 
7056             docReadyProcId = setInterval(function(){
7057                 var rs = document.readyState;
7058                 if(rs == "complete") {
7059                     fireDocReady();     
7060                  }
7061             }, 10);
7062         }
7063         // no matter what, make sure it fires on load
7064         E.on(window, "load", fireDocReady);
7065     };
7066
7067     var createBuffered = function(h, o){
7068         var task = new Roo.util.DelayedTask(h);
7069         return function(e){
7070             // create new event object impl so new events don't wipe out properties
7071             e = new Roo.EventObjectImpl(e);
7072             task.delay(o.buffer, h, null, [e]);
7073         };
7074     };
7075
7076     var createSingle = function(h, el, ename, fn){
7077         return function(e){
7078             Roo.EventManager.removeListener(el, ename, fn);
7079             h(e);
7080         };
7081     };
7082
7083     var createDelayed = function(h, o){
7084         return function(e){
7085             // create new event object impl so new events don't wipe out properties
7086             e = new Roo.EventObjectImpl(e);
7087             setTimeout(function(){
7088                 h(e);
7089             }, o.delay || 10);
7090         };
7091     };
7092     var transitionEndVal = false;
7093     
7094     var transitionEnd = function()
7095     {
7096         if (transitionEndVal) {
7097             return transitionEndVal;
7098         }
7099         var el = document.createElement('div');
7100
7101         var transEndEventNames = {
7102             WebkitTransition : 'webkitTransitionEnd',
7103             MozTransition    : 'transitionend',
7104             OTransition      : 'oTransitionEnd otransitionend',
7105             transition       : 'transitionend'
7106         };
7107     
7108         for (var name in transEndEventNames) {
7109             if (el.style[name] !== undefined) {
7110                 transitionEndVal = transEndEventNames[name];
7111                 return  transitionEndVal ;
7112             }
7113         }
7114     }
7115     
7116   
7117
7118     var listen = function(element, ename, opt, fn, scope)
7119     {
7120         var o = (!opt || typeof opt == "boolean") ? {} : opt;
7121         fn = fn || o.fn; scope = scope || o.scope;
7122         var el = Roo.getDom(element);
7123         
7124         
7125         if(!el){
7126             throw "Error listening for \"" + ename + '\". Element "' + element + '" doesn\'t exist.';
7127         }
7128         
7129         if (ename == 'transitionend') {
7130             ename = transitionEnd();
7131         }
7132         var h = function(e){
7133             e = Roo.EventObject.setEvent(e);
7134             var t;
7135             if(o.delegate){
7136                 t = e.getTarget(o.delegate, el);
7137                 if(!t){
7138                     return;
7139                 }
7140             }else{
7141                 t = e.target;
7142             }
7143             if(o.stopEvent === true){
7144                 e.stopEvent();
7145             }
7146             if(o.preventDefault === true){
7147                e.preventDefault();
7148             }
7149             if(o.stopPropagation === true){
7150                 e.stopPropagation();
7151             }
7152
7153             if(o.normalized === false){
7154                 e = e.browserEvent;
7155             }
7156
7157             fn.call(scope || el, e, t, o);
7158         };
7159         if(o.delay){
7160             h = createDelayed(h, o);
7161         }
7162         if(o.single){
7163             h = createSingle(h, el, ename, fn);
7164         }
7165         if(o.buffer){
7166             h = createBuffered(h, o);
7167         }
7168         
7169         fn._handlers = fn._handlers || [];
7170         
7171         
7172         fn._handlers.push([Roo.id(el), ename, h]);
7173         
7174         
7175          
7176         E.on(el, ename, h); // this adds the actuall listener to the object..
7177         
7178         
7179         if(ename == "mousewheel" && el.addEventListener){ // workaround for jQuery
7180             el.addEventListener("DOMMouseScroll", h, false);
7181             E.on(window, 'unload', function(){
7182                 el.removeEventListener("DOMMouseScroll", h, false);
7183             });
7184         }
7185         if(ename == "mousedown" && el == document){ // fix stopped mousedowns on the document
7186             Roo.EventManager.stoppedMouseDownEvent.addListener(h);
7187         }
7188         return h;
7189     };
7190
7191     var stopListening = function(el, ename, fn){
7192         var id = Roo.id(el), hds = fn._handlers, hd = fn;
7193         if(hds){
7194             for(var i = 0, len = hds.length; i < len; i++){
7195                 var h = hds[i];
7196                 if(h[0] == id && h[1] == ename){
7197                     hd = h[2];
7198                     hds.splice(i, 1);
7199                     break;
7200                 }
7201             }
7202         }
7203         E.un(el, ename, hd);
7204         el = Roo.getDom(el);
7205         if(ename == "mousewheel" && el.addEventListener){
7206             el.removeEventListener("DOMMouseScroll", hd, false);
7207         }
7208         if(ename == "mousedown" && el == document){ // fix stopped mousedowns on the document
7209             Roo.EventManager.stoppedMouseDownEvent.removeListener(hd);
7210         }
7211     };
7212
7213     var propRe = /^(?:scope|delay|buffer|single|stopEvent|preventDefault|stopPropagation|normalized|args|delegate)$/;
7214     
7215     var pub = {
7216         
7217         
7218         /** 
7219          * Fix for doc tools
7220          * @scope Roo.EventManager
7221          */
7222         
7223         
7224         /** 
7225          * This is no longer needed and is deprecated. Places a simple wrapper around an event handler to override the browser event
7226          * object with a Roo.EventObject
7227          * @param {Function} fn        The method the event invokes
7228          * @param {Object}   scope    An object that becomes the scope of the handler
7229          * @param {boolean}  override If true, the obj passed in becomes
7230          *                             the execution scope of the listener
7231          * @return {Function} The wrapped function
7232          * @deprecated
7233          */
7234         wrap : function(fn, scope, override){
7235             return function(e){
7236                 Roo.EventObject.setEvent(e);
7237                 fn.call(override ? scope || window : window, Roo.EventObject, scope);
7238             };
7239         },
7240         
7241         /**
7242      * Appends an event handler to an element (shorthand for addListener)
7243      * @param {String/HTMLElement}   element        The html element or id to assign the
7244      * @param {String}   eventName The type of event to listen for
7245      * @param {Function} handler The method the event invokes
7246      * @param {Object}   scope (optional) The scope in which to execute the handler
7247      * function. The handler function's "this" context.
7248      * @param {Object}   options (optional) An object containing handler configuration
7249      * properties. This may contain any of the following properties:<ul>
7250      * <li>scope {Object} The scope in which to execute the handler function. The handler function's "this" context.</li>
7251      * <li>delegate {String} A simple selector to filter the target or look for a descendant of the target</li>
7252      * <li>stopEvent {Boolean} True to stop the event. That is stop propagation, and prevent the default action.</li>
7253      * <li>preventDefault {Boolean} True to prevent the default action</li>
7254      * <li>stopPropagation {Boolean} True to prevent event propagation</li>
7255      * <li>normalized {Boolean} False to pass a browser event to the handler function instead of an Roo.EventObject</li>
7256      * <li>delay {Number} The number of milliseconds to delay the invocation of the handler after te event fires.</li>
7257      * <li>single {Boolean} True to add a handler to handle just the next firing of the event, and then remove itself.</li>
7258      * <li>buffer {Number} Causes the handler to be scheduled to run in an {@link Roo.util.DelayedTask} delayed
7259      * by the specified number of milliseconds. If the event fires again within that time, the original
7260      * handler is <em>not</em> invoked, but the new handler is scheduled in its place.</li>
7261      * </ul><br>
7262      * <p>
7263      * <b>Combining Options</b><br>
7264      * Using the options argument, it is possible to combine different types of listeners:<br>
7265      * <br>
7266      * A normalized, delayed, one-time listener that auto stops the event and passes a custom argument (forumId)<div style="margin: 5px 20px 20px;">
7267      * Code:<pre><code>
7268 el.on('click', this.onClick, this, {
7269     single: true,
7270     delay: 100,
7271     stopEvent : true,
7272     forumId: 4
7273 });</code></pre>
7274      * <p>
7275      * <b>Attaching multiple handlers in 1 call</b><br>
7276       * The method also allows for a single argument to be passed which is a config object containing properties
7277      * which specify multiple handlers.
7278      * <p>
7279      * Code:<pre><code>
7280 el.on({
7281     'click' : {
7282         fn: this.onClick
7283         scope: this,
7284         delay: 100
7285     },
7286     'mouseover' : {
7287         fn: this.onMouseOver
7288         scope: this
7289     },
7290     'mouseout' : {
7291         fn: this.onMouseOut
7292         scope: this
7293     }
7294 });</code></pre>
7295      * <p>
7296      * Or a shorthand syntax:<br>
7297      * Code:<pre><code>
7298 el.on({
7299     'click' : this.onClick,
7300     'mouseover' : this.onMouseOver,
7301     'mouseout' : this.onMouseOut
7302     scope: this
7303 });</code></pre>
7304      */
7305         addListener : function(element, eventName, fn, scope, options){
7306             if(typeof eventName == "object"){
7307                 var o = eventName;
7308                 for(var e in o){
7309                     if(propRe.test(e)){
7310                         continue;
7311                     }
7312                     if(typeof o[e] == "function"){
7313                         // shared options
7314                         listen(element, e, o, o[e], o.scope);
7315                     }else{
7316                         // individual options
7317                         listen(element, e, o[e]);
7318                     }
7319                 }
7320                 return;
7321             }
7322             return listen(element, eventName, options, fn, scope);
7323         },
7324         
7325         /**
7326          * Removes an event handler
7327          *
7328          * @param {String/HTMLElement}   element        The id or html element to remove the 
7329          *                             event from
7330          * @param {String}   eventName     The type of event
7331          * @param {Function} fn
7332          * @return {Boolean} True if a listener was actually removed
7333          */
7334         removeListener : function(element, eventName, fn){
7335             return stopListening(element, eventName, fn);
7336         },
7337         
7338         /**
7339          * Fires when the document is ready (before onload and before images are loaded). Can be 
7340          * accessed shorthanded Roo.onReady().
7341          * @param {Function} fn        The method the event invokes
7342          * @param {Object}   scope    An  object that becomes the scope of the handler
7343          * @param {boolean}  options
7344          */
7345         onDocumentReady : function(fn, scope, options){
7346             if(docReadyState){ // if it already fired
7347                 docReadyEvent.addListener(fn, scope, options);
7348                 docReadyEvent.fire();
7349                 docReadyEvent.clearListeners();
7350                 return;
7351             }
7352             if(!docReadyEvent){
7353                 initDocReady();
7354             }
7355             docReadyEvent.addListener(fn, scope, options);
7356         },
7357         
7358         /**
7359          * Fires when the window is resized and provides resize event buffering (50 milliseconds), passes new viewport width and height to handlers.
7360          * @param {Function} fn        The method the event invokes
7361          * @param {Object}   scope    An object that becomes the scope of the handler
7362          * @param {boolean}  options
7363          */
7364         onWindowResize : function(fn, scope, options)
7365         {
7366             if(!resizeEvent){
7367                 resizeEvent = new Roo.util.Event();
7368                 resizeTask = new Roo.util.DelayedTask(function(){
7369                     resizeEvent.fire(D.getViewWidth(), D.getViewHeight());
7370                 });
7371                 E.on(window, "resize", function()
7372                 {
7373                     if (Roo.isIE) {
7374                         resizeTask.delay(50);
7375                     } else {
7376                         resizeEvent.fire(D.getViewWidth(), D.getViewHeight());
7377                     }
7378                 });
7379             }
7380             resizeEvent.addListener(fn, scope, options);
7381         },
7382
7383         /**
7384          * Fires when the user changes the active text size. Handler gets called with 2 params, the old size and the new size.
7385          * @param {Function} fn        The method the event invokes
7386          * @param {Object}   scope    An object that becomes the scope of the handler
7387          * @param {boolean}  options
7388          */
7389         onTextResize : function(fn, scope, options){
7390             if(!textEvent){
7391                 textEvent = new Roo.util.Event();
7392                 var textEl = new Roo.Element(document.createElement('div'));
7393                 textEl.dom.className = 'x-text-resize';
7394                 textEl.dom.innerHTML = 'X';
7395                 textEl.appendTo(document.body);
7396                 textSize = textEl.dom.offsetHeight;
7397                 setInterval(function(){
7398                     if(textEl.dom.offsetHeight != textSize){
7399                         textEvent.fire(textSize, textSize = textEl.dom.offsetHeight);
7400                     }
7401                 }, this.textResizeInterval);
7402             }
7403             textEvent.addListener(fn, scope, options);
7404         },
7405
7406         /**
7407          * Removes the passed window resize listener.
7408          * @param {Function} fn        The method the event invokes
7409          * @param {Object}   scope    The scope of handler
7410          */
7411         removeResizeListener : function(fn, scope){
7412             if(resizeEvent){
7413                 resizeEvent.removeListener(fn, scope);
7414             }
7415         },
7416
7417         // private
7418         fireResize : function(){
7419             if(resizeEvent){
7420                 resizeEvent.fire(D.getViewWidth(), D.getViewHeight());
7421             }   
7422         },
7423         /**
7424          * Url used for onDocumentReady with using SSL (defaults to Roo.SSL_SECURE_URL)
7425          */
7426         ieDeferSrc : false,
7427         /**
7428          * The frequency, in milliseconds, to check for text resize events (defaults to 50)
7429          */
7430         textResizeInterval : 50
7431     };
7432     
7433     /**
7434      * Fix for doc tools
7435      * @scopeAlias pub=Roo.EventManager
7436      */
7437     
7438      /**
7439      * Appends an event handler to an element (shorthand for addListener)
7440      * @param {String/HTMLElement}   element        The html element or id to assign the
7441      * @param {String}   eventName The type of event to listen for
7442      * @param {Function} handler The method the event invokes
7443      * @param {Object}   scope (optional) The scope in which to execute the handler
7444      * function. The handler function's "this" context.
7445      * @param {Object}   options (optional) An object containing handler configuration
7446      * properties. This may contain any of the following properties:<ul>
7447      * <li>scope {Object} The scope in which to execute the handler function. The handler function's "this" context.</li>
7448      * <li>delegate {String} A simple selector to filter the target or look for a descendant of the target</li>
7449      * <li>stopEvent {Boolean} True to stop the event. That is stop propagation, and prevent the default action.</li>
7450      * <li>preventDefault {Boolean} True to prevent the default action</li>
7451      * <li>stopPropagation {Boolean} True to prevent event propagation</li>
7452      * <li>normalized {Boolean} False to pass a browser event to the handler function instead of an Roo.EventObject</li>
7453      * <li>delay {Number} The number of milliseconds to delay the invocation of the handler after te event fires.</li>
7454      * <li>single {Boolean} True to add a handler to handle just the next firing of the event, and then remove itself.</li>
7455      * <li>buffer {Number} Causes the handler to be scheduled to run in an {@link Roo.util.DelayedTask} delayed
7456      * by the specified number of milliseconds. If the event fires again within that time, the original
7457      * handler is <em>not</em> invoked, but the new handler is scheduled in its place.</li>
7458      * </ul><br>
7459      * <p>
7460      * <b>Combining Options</b><br>
7461      * Using the options argument, it is possible to combine different types of listeners:<br>
7462      * <br>
7463      * A normalized, delayed, one-time listener that auto stops the event and passes a custom argument (forumId)<div style="margin: 5px 20px 20px;">
7464      * Code:<pre><code>
7465 el.on('click', this.onClick, this, {
7466     single: true,
7467     delay: 100,
7468     stopEvent : true,
7469     forumId: 4
7470 });</code></pre>
7471      * <p>
7472      * <b>Attaching multiple handlers in 1 call</b><br>
7473       * The method also allows for a single argument to be passed which is a config object containing properties
7474      * which specify multiple handlers.
7475      * <p>
7476      * Code:<pre><code>
7477 el.on({
7478     'click' : {
7479         fn: this.onClick
7480         scope: this,
7481         delay: 100
7482     },
7483     'mouseover' : {
7484         fn: this.onMouseOver
7485         scope: this
7486     },
7487     'mouseout' : {
7488         fn: this.onMouseOut
7489         scope: this
7490     }
7491 });</code></pre>
7492      * <p>
7493      * Or a shorthand syntax:<br>
7494      * Code:<pre><code>
7495 el.on({
7496     'click' : this.onClick,
7497     'mouseover' : this.onMouseOver,
7498     'mouseout' : this.onMouseOut
7499     scope: this
7500 });</code></pre>
7501      */
7502     pub.on = pub.addListener;
7503     pub.un = pub.removeListener;
7504
7505     pub.stoppedMouseDownEvent = new Roo.util.Event();
7506     return pub;
7507 }();
7508 /**
7509   * Fires when the document is ready (before onload and before images are loaded).  Shorthand of {@link Roo.EventManager#onDocumentReady}.
7510   * @param {Function} fn        The method the event invokes
7511   * @param {Object}   scope    An  object that becomes the scope of the handler
7512   * @param {boolean}  override If true, the obj passed in becomes
7513   *                             the execution scope of the listener
7514   * @member Roo
7515   * @method onReady
7516  */
7517 Roo.onReady = Roo.EventManager.onDocumentReady;
7518
7519 Roo.onReady(function(){
7520     var bd = Roo.get(document.body);
7521     if(!bd){ return; }
7522
7523     var cls = [
7524             Roo.isIE ? "roo-ie"
7525             : Roo.isIE11 ? "roo-ie11"
7526             : Roo.isEdge ? "roo-edge"
7527             : Roo.isGecko ? "roo-gecko"
7528             : Roo.isOpera ? "roo-opera"
7529             : Roo.isSafari ? "roo-safari" : ""];
7530
7531     if(Roo.isMac){
7532         cls.push("roo-mac");
7533     }
7534     if(Roo.isLinux){
7535         cls.push("roo-linux");
7536     }
7537     if(Roo.isIOS){
7538         cls.push("roo-ios");
7539     }
7540     if(Roo.isTouch){
7541         cls.push("roo-touch");
7542     }
7543     if(Roo.isBorderBox){
7544         cls.push('roo-border-box');
7545     }
7546     if(Roo.isStrict){ // add to the parent to allow for selectors like ".ext-strict .ext-ie"
7547         var p = bd.dom.parentNode;
7548         if(p){
7549             p.className += ' roo-strict';
7550         }
7551     }
7552     bd.addClass(cls.join(' '));
7553 });
7554
7555 /**
7556  * @class Roo.EventObject
7557  * EventObject exposes the Yahoo! UI Event functionality directly on the object
7558  * passed to your event handler. It exists mostly for convenience. It also fixes the annoying null checks automatically to cleanup your code 
7559  * Example:
7560  * <pre><code>
7561  function handleClick(e){ // e is not a standard event object, it is a Roo.EventObject
7562     e.preventDefault();
7563     var target = e.getTarget();
7564     ...
7565  }
7566  var myDiv = Roo.get("myDiv");
7567  myDiv.on("click", handleClick);
7568  //or
7569  Roo.EventManager.on("myDiv", 'click', handleClick);
7570  Roo.EventManager.addListener("myDiv", 'click', handleClick);
7571  </code></pre>
7572  * @static
7573  */
7574 Roo.EventObject = function(){
7575     
7576     var E = Roo.lib.Event;
7577     
7578     // safari keypress events for special keys return bad keycodes
7579     var safariKeys = {
7580         63234 : 37, // left
7581         63235 : 39, // right
7582         63232 : 38, // up
7583         63233 : 40, // down
7584         63276 : 33, // page up
7585         63277 : 34, // page down
7586         63272 : 46, // delete
7587         63273 : 36, // home
7588         63275 : 35  // end
7589     };
7590
7591     // normalize button clicks
7592     var btnMap = Roo.isIE ? {1:0,4:1,2:2} :
7593                 (Roo.isSafari ? {1:0,2:1,3:2} : {0:0,1:1,2:2});
7594
7595     Roo.EventObjectImpl = function(e){
7596         if(e){
7597             this.setEvent(e.browserEvent || e);
7598         }
7599     };
7600     Roo.EventObjectImpl.prototype = {
7601         /**
7602          * Used to fix doc tools.
7603          * @scope Roo.EventObject.prototype
7604          */
7605             
7606
7607         
7608         
7609         /** The normal browser event */
7610         browserEvent : null,
7611         /** The button pressed in a mouse event */
7612         button : -1,
7613         /** True if the shift key was down during the event */
7614         shiftKey : false,
7615         /** True if the control key was down during the event */
7616         ctrlKey : false,
7617         /** True if the alt key was down during the event */
7618         altKey : false,
7619
7620         /** Key constant 
7621         * @type Number */
7622         BACKSPACE : 8,
7623         /** Key constant 
7624         * @type Number */
7625         TAB : 9,
7626         /** Key constant 
7627         * @type Number */
7628         RETURN : 13,
7629         /** Key constant 
7630         * @type Number */
7631         ENTER : 13,
7632         /** Key constant 
7633         * @type Number */
7634         SHIFT : 16,
7635         /** Key constant 
7636         * @type Number */
7637         CONTROL : 17,
7638         /** Key constant 
7639         * @type Number */
7640         ESC : 27,
7641         /** Key constant 
7642         * @type Number */
7643         SPACE : 32,
7644         /** Key constant 
7645         * @type Number */
7646         PAGEUP : 33,
7647         /** Key constant 
7648         * @type Number */
7649         PAGEDOWN : 34,
7650         /** Key constant 
7651         * @type Number */
7652         END : 35,
7653         /** Key constant 
7654         * @type Number */
7655         HOME : 36,
7656         /** Key constant 
7657         * @type Number */
7658         LEFT : 37,
7659         /** Key constant 
7660         * @type Number */
7661         UP : 38,
7662         /** Key constant 
7663         * @type Number */
7664         RIGHT : 39,
7665         /** Key constant 
7666         * @type Number */
7667         DOWN : 40,
7668         /** Key constant 
7669         * @type Number */
7670         DELETE : 46,
7671         /** Key constant 
7672         * @type Number */
7673         F5 : 116,
7674
7675            /** @private */
7676         setEvent : function(e){
7677             if(e == this || (e && e.browserEvent)){ // already wrapped
7678                 return e;
7679             }
7680             this.browserEvent = e;
7681             if(e){
7682                 // normalize buttons
7683                 this.button = e.button ? btnMap[e.button] : (e.which ? e.which-1 : -1);
7684                 if(e.type == 'click' && this.button == -1){
7685                     this.button = 0;
7686                 }
7687                 this.type = e.type;
7688                 this.shiftKey = e.shiftKey;
7689                 // mac metaKey behaves like ctrlKey
7690                 this.ctrlKey = e.ctrlKey || e.metaKey;
7691                 this.altKey = e.altKey;
7692                 // in getKey these will be normalized for the mac
7693                 this.keyCode = e.keyCode;
7694                 // keyup warnings on firefox.
7695                 this.charCode = (e.type == 'keyup' || e.type == 'keydown') ? 0 : e.charCode;
7696                 // cache the target for the delayed and or buffered events
7697                 this.target = E.getTarget(e);
7698                 // same for XY
7699                 this.xy = E.getXY(e);
7700             }else{
7701                 this.button = -1;
7702                 this.shiftKey = false;
7703                 this.ctrlKey = false;
7704                 this.altKey = false;
7705                 this.keyCode = 0;
7706                 this.charCode =0;
7707                 this.target = null;
7708                 this.xy = [0, 0];
7709             }
7710             return this;
7711         },
7712
7713         /**
7714          * Stop the event (preventDefault and stopPropagation)
7715          */
7716         stopEvent : function(){
7717             if(this.browserEvent){
7718                 if(this.browserEvent.type == 'mousedown'){
7719                     Roo.EventManager.stoppedMouseDownEvent.fire(this);
7720                 }
7721                 E.stopEvent(this.browserEvent);
7722             }
7723         },
7724
7725         /**
7726          * Prevents the browsers default handling of the event.
7727          */
7728         preventDefault : function(){
7729             if(this.browserEvent){
7730                 E.preventDefault(this.browserEvent);
7731             }
7732         },
7733
7734         /** @private */
7735         isNavKeyPress : function(){
7736             var k = this.keyCode;
7737             k = Roo.isSafari ? (safariKeys[k] || k) : k;
7738             return (k >= 33 && k <= 40) || k == this.RETURN || k == this.TAB || k == this.ESC;
7739         },
7740
7741         isSpecialKey : function(){
7742             var k = this.keyCode;
7743             return (this.type == 'keypress' && this.ctrlKey) || k == 9 || k == 13  || k == 40 || k == 27 ||
7744             (k == 16) || (k == 17) ||
7745             (k >= 18 && k <= 20) ||
7746             (k >= 33 && k <= 35) ||
7747             (k >= 36 && k <= 39) ||
7748             (k >= 44 && k <= 45);
7749         },
7750         /**
7751          * Cancels bubbling of the event.
7752          */
7753         stopPropagation : function(){
7754             if(this.browserEvent){
7755                 if(this.type == 'mousedown'){
7756                     Roo.EventManager.stoppedMouseDownEvent.fire(this);
7757                 }
7758                 E.stopPropagation(this.browserEvent);
7759             }
7760         },
7761
7762         /**
7763          * Gets the key code for the event.
7764          * @return {Number}
7765          */
7766         getCharCode : function(){
7767             return this.charCode || this.keyCode;
7768         },
7769
7770         /**
7771          * Returns a normalized keyCode for the event.
7772          * @return {Number} The key code
7773          */
7774         getKey : function(){
7775             var k = this.keyCode || this.charCode;
7776             return Roo.isSafari ? (safariKeys[k] || k) : k;
7777         },
7778
7779         /**
7780          * Gets the x coordinate of the event.
7781          * @return {Number}
7782          */
7783         getPageX : function(){
7784             return this.xy[0];
7785         },
7786
7787         /**
7788          * Gets the y coordinate of the event.
7789          * @return {Number}
7790          */
7791         getPageY : function(){
7792             return this.xy[1];
7793         },
7794
7795         /**
7796          * Gets the time of the event.
7797          * @return {Number}
7798          */
7799         getTime : function(){
7800             if(this.browserEvent){
7801                 return E.getTime(this.browserEvent);
7802             }
7803             return null;
7804         },
7805
7806         /**
7807          * Gets the page coordinates of the event.
7808          * @return {Array} The xy values like [x, y]
7809          */
7810         getXY : function(){
7811             return this.xy;
7812         },
7813
7814         /**
7815          * Gets the target for the event.
7816          * @param {String} selector (optional) A simple selector to filter the target or look for an ancestor of the target
7817          * @param {Number/String/HTMLElement/Element} maxDepth (optional) The max depth to
7818                 search as a number or element (defaults to 10 || document.body)
7819          * @param {Boolean} returnEl (optional) True to return a Roo.Element object instead of DOM node
7820          * @return {HTMLelement}
7821          */
7822         getTarget : function(selector, maxDepth, returnEl){
7823             return selector ? Roo.fly(this.target).findParent(selector, maxDepth, returnEl) : this.target;
7824         },
7825         /**
7826          * Gets the related target.
7827          * @return {HTMLElement}
7828          */
7829         getRelatedTarget : function(){
7830             if(this.browserEvent){
7831                 return E.getRelatedTarget(this.browserEvent);
7832             }
7833             return null;
7834         },
7835
7836         /**
7837          * Normalizes mouse wheel delta across browsers
7838          * @return {Number} The delta
7839          */
7840         getWheelDelta : function(){
7841             var e = this.browserEvent;
7842             var delta = 0;
7843             if(e.wheelDelta){ /* IE/Opera. */
7844                 delta = e.wheelDelta/120;
7845             }else if(e.detail){ /* Mozilla case. */
7846                 delta = -e.detail/3;
7847             }
7848             return delta;
7849         },
7850
7851         /**
7852          * Returns true if the control, meta, shift or alt key was pressed during this event.
7853          * @return {Boolean}
7854          */
7855         hasModifier : function(){
7856             return !!((this.ctrlKey || this.altKey) || this.shiftKey);
7857         },
7858
7859         /**
7860          * Returns true if the target of this event equals el or is a child of el
7861          * @param {String/HTMLElement/Element} el
7862          * @param {Boolean} related (optional) true to test if the related target is within el instead of the target
7863          * @return {Boolean}
7864          */
7865         within : function(el, related){
7866             var t = this[related ? "getRelatedTarget" : "getTarget"]();
7867             return t && Roo.fly(el).contains(t);
7868         },
7869
7870         getPoint : function(){
7871             return new Roo.lib.Point(this.xy[0], this.xy[1]);
7872         }
7873     };
7874
7875     return new Roo.EventObjectImpl();
7876 }();
7877             
7878     /*
7879  * Based on:
7880  * Ext JS Library 1.1.1
7881  * Copyright(c) 2006-2007, Ext JS, LLC.
7882  *
7883  * Originally Released Under LGPL - original licence link has changed is not relivant.
7884  *
7885  * Fork - LGPL
7886  * <script type="text/javascript">
7887  */
7888
7889  
7890 // was in Composite Element!??!?!
7891  
7892 (function(){
7893     var D = Roo.lib.Dom;
7894     var E = Roo.lib.Event;
7895     var A = Roo.lib.Anim;
7896
7897     // local style camelizing for speed
7898     var propCache = {};
7899     var camelRe = /(-[a-z])/gi;
7900     var camelFn = function(m, a){ return a.charAt(1).toUpperCase(); };
7901     var view = document.defaultView;
7902
7903 /**
7904  * @class Roo.Element
7905  * Represents an Element in the DOM.<br><br>
7906  * Usage:<br>
7907 <pre><code>
7908 var el = Roo.get("my-div");
7909
7910 // or with getEl
7911 var el = getEl("my-div");
7912
7913 // or with a DOM element
7914 var el = Roo.get(myDivElement);
7915 </code></pre>
7916  * Using Roo.get() or getEl() instead of calling the constructor directly ensures you get the same object
7917  * each call instead of constructing a new one.<br><br>
7918  * <b>Animations</b><br />
7919  * Many of the functions for manipulating an element have an optional "animate" parameter. The animate parameter
7920  * should either be a boolean (true) or an object literal with animation options. The animation options are:
7921 <pre>
7922 Option    Default   Description
7923 --------- --------  ---------------------------------------------
7924 duration  .35       The duration of the animation in seconds
7925 easing    easeOut   The YUI easing method
7926 callback  none      A function to execute when the anim completes
7927 scope     this      The scope (this) of the callback function
7928 </pre>
7929 * Also, the Anim object being used for the animation will be set on your options object as "anim", which allows you to stop or
7930 * manipulate the animation. Here's an example:
7931 <pre><code>
7932 var el = Roo.get("my-div");
7933
7934 // no animation
7935 el.setWidth(100);
7936
7937 // default animation
7938 el.setWidth(100, true);
7939
7940 // animation with some options set
7941 el.setWidth(100, {
7942     duration: 1,
7943     callback: this.foo,
7944     scope: this
7945 });
7946
7947 // using the "anim" property to get the Anim object
7948 var opt = {
7949     duration: 1,
7950     callback: this.foo,
7951     scope: this
7952 };
7953 el.setWidth(100, opt);
7954 ...
7955 if(opt.anim.isAnimated()){
7956     opt.anim.stop();
7957 }
7958 </code></pre>
7959 * <b> Composite (Collections of) Elements</b><br />
7960  * For working with collections of Elements, see <a href="Roo.CompositeElement.html">Roo.CompositeElement</a>
7961  * @constructor Create a new Element directly.
7962  * @param {String/HTMLElement} element
7963  * @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).
7964  */
7965     Roo.Element = function(element, forceNew)
7966     {
7967         var dom = typeof element == "string" ?
7968                 document.getElementById(element) : element;
7969         
7970         this.listeners = {};
7971         
7972         if(!dom){ // invalid id/element
7973             return null;
7974         }
7975         var id = dom.id;
7976         if(forceNew !== true && id && Roo.Element.cache[id]){ // element object already exists
7977             return Roo.Element.cache[id];
7978         }
7979
7980         /**
7981          * The DOM element
7982          * @type HTMLElement
7983          */
7984         this.dom = dom;
7985
7986         /**
7987          * The DOM element ID
7988          * @type String
7989          */
7990         this.id = id || Roo.id(dom);
7991         
7992         return this; // assumed for cctor?
7993     };
7994
7995     var El = Roo.Element;
7996
7997     El.prototype = {
7998         /**
7999          * The element's default display mode  (defaults to "") 
8000          * @type String
8001          */
8002         originalDisplay : "",
8003
8004         
8005         // note this is overridden in BS version..
8006         visibilityMode : 1, 
8007         /**
8008          * The default unit to append to CSS values where a unit isn't provided (defaults to px).
8009          * @type String
8010          */
8011         defaultUnit : "px",
8012         
8013         /**
8014          * Sets the element's visibility mode. When setVisible() is called it
8015          * will use this to determine whether to set the visibility or the display property.
8016          * @param visMode Element.VISIBILITY or Element.DISPLAY
8017          * @return {Roo.Element} this
8018          */
8019         setVisibilityMode : function(visMode){
8020             this.visibilityMode = visMode;
8021             return this;
8022         },
8023         /**
8024          * Convenience method for setVisibilityMode(Element.DISPLAY)
8025          * @param {String} display (optional) What to set display to when visible
8026          * @return {Roo.Element} this
8027          */
8028         enableDisplayMode : function(display){
8029             this.setVisibilityMode(El.DISPLAY);
8030             if(typeof display != "undefined") { this.originalDisplay = display; }
8031             return this;
8032         },
8033
8034         /**
8035          * 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)
8036          * @param {String} selector The simple selector to test
8037          * @param {Number/String/HTMLElement/Element} maxDepth (optional) The max depth to
8038                 search as a number or element (defaults to 10 || document.body)
8039          * @param {Boolean} returnEl (optional) True to return a Roo.Element object instead of DOM node
8040          * @return {HTMLElement} The matching DOM node (or null if no match was found)
8041          */
8042         findParent : function(simpleSelector, maxDepth, returnEl){
8043             var p = this.dom, b = document.body, depth = 0, dq = Roo.DomQuery, stopEl;
8044             maxDepth = maxDepth || 50;
8045             if(typeof maxDepth != "number"){
8046                 stopEl = Roo.getDom(maxDepth);
8047                 maxDepth = 10;
8048             }
8049             while(p && p.nodeType == 1 && depth < maxDepth && p != b && p != stopEl){
8050                 if(dq.is(p, simpleSelector)){
8051                     return returnEl ? Roo.get(p) : p;
8052                 }
8053                 depth++;
8054                 p = p.parentNode;
8055             }
8056             return null;
8057         },
8058
8059
8060         /**
8061          * Looks at parent nodes for a match of the passed simple selector (e.g. div.some-class or span:first-child)
8062          * @param {String} selector The simple selector to test
8063          * @param {Number/String/HTMLElement/Element} maxDepth (optional) The max depth to
8064                 search as a number or element (defaults to 10 || document.body)
8065          * @param {Boolean} returnEl (optional) True to return a Roo.Element object instead of DOM node
8066          * @return {HTMLElement} The matching DOM node (or null if no match was found)
8067          */
8068         findParentNode : function(simpleSelector, maxDepth, returnEl){
8069             var p = Roo.fly(this.dom.parentNode, '_internal');
8070             return p ? p.findParent(simpleSelector, maxDepth, returnEl) : null;
8071         },
8072         
8073         /**
8074          * Looks at  the scrollable parent element
8075          */
8076         findScrollableParent : function()
8077         {
8078             var overflowRegex = /(auto|scroll)/;
8079             
8080             if(this.getStyle('position') === 'fixed'){
8081                 return Roo.isAndroid ? Roo.get(document.documentElement) : Roo.get(document.body);
8082             }
8083             
8084             var excludeStaticParent = this.getStyle('position') === "absolute";
8085             
8086             for (var parent = this; (parent = Roo.get(parent.dom.parentNode));){
8087                 
8088                 if (excludeStaticParent && parent.getStyle('position') === "static") {
8089                     continue;
8090                 }
8091                 
8092                 if (overflowRegex.test(parent.getStyle('overflow') + parent.getStyle('overflow-x') + parent.getStyle('overflow-y'))){
8093                     return parent;
8094                 }
8095                 
8096                 if(parent.dom.nodeName.toLowerCase() == 'body'){
8097                     return Roo.isAndroid ? Roo.get(document.documentElement) : Roo.get(document.body);
8098                 }
8099             }
8100             
8101             return Roo.isAndroid ? Roo.get(document.documentElement) : Roo.get(document.body);
8102         },
8103
8104         /**
8105          * Walks up the dom looking for a parent node that matches the passed simple selector (e.g. div.some-class or span:first-child).
8106          * This is a shortcut for findParentNode() that always returns an Roo.Element.
8107          * @param {String} selector The simple selector to test
8108          * @param {Number/String/HTMLElement/Element} maxDepth (optional) The max depth to
8109                 search as a number or element (defaults to 10 || document.body)
8110          * @return {Roo.Element} The matching DOM node (or null if no match was found)
8111          */
8112         up : function(simpleSelector, maxDepth){
8113             return this.findParentNode(simpleSelector, maxDepth, true);
8114         },
8115
8116
8117
8118         /**
8119          * Returns true if this element matches the passed simple selector (e.g. div.some-class or span:first-child)
8120          * @param {String} selector The simple selector to test
8121          * @return {Boolean} True if this element matches the selector, else false
8122          */
8123         is : function(simpleSelector){
8124             return Roo.DomQuery.is(this.dom, simpleSelector);
8125         },
8126
8127         /**
8128          * Perform animation on this element.
8129          * @param {Object} args The YUI animation control args
8130          * @param {Float} duration (optional) How long the animation lasts in seconds (defaults to .35)
8131          * @param {Function} onComplete (optional) Function to call when animation completes
8132          * @param {String} easing (optional) Easing method to use (defaults to 'easeOut')
8133          * @param {String} animType (optional) 'run' is the default. Can also be 'color', 'motion', or 'scroll'
8134          * @return {Roo.Element} this
8135          */
8136         animate : function(args, duration, onComplete, easing, animType){
8137             this.anim(args, {duration: duration, callback: onComplete, easing: easing}, animType);
8138             return this;
8139         },
8140
8141         /*
8142          * @private Internal animation call
8143          */
8144         anim : function(args, opt, animType, defaultDur, defaultEase, cb){
8145             animType = animType || 'run';
8146             opt = opt || {};
8147             var anim = Roo.lib.Anim[animType](
8148                 this.dom, args,
8149                 (opt.duration || defaultDur) || .35,
8150                 (opt.easing || defaultEase) || 'easeOut',
8151                 function(){
8152                     Roo.callback(cb, this);
8153                     Roo.callback(opt.callback, opt.scope || this, [this, opt]);
8154                 },
8155                 this
8156             );
8157             opt.anim = anim;
8158             return anim;
8159         },
8160
8161         // private legacy anim prep
8162         preanim : function(a, i){
8163             return !a[i] ? false : (typeof a[i] == "object" ? a[i]: {duration: a[i+1], callback: a[i+2], easing: a[i+3]});
8164         },
8165
8166         /**
8167          * Removes worthless text nodes
8168          * @param {Boolean} forceReclean (optional) By default the element
8169          * keeps track if it has been cleaned already so
8170          * you can call this over and over. However, if you update the element and
8171          * need to force a reclean, you can pass true.
8172          */
8173         clean : function(forceReclean){
8174             if(this.isCleaned && forceReclean !== true){
8175                 return this;
8176             }
8177             var ns = /\S/;
8178             var d = this.dom, n = d.firstChild, ni = -1;
8179             while(n){
8180                 var nx = n.nextSibling;
8181                 if(n.nodeType == 3 && !ns.test(n.nodeValue)){
8182                     d.removeChild(n);
8183                 }else{
8184                     n.nodeIndex = ++ni;
8185                 }
8186                 n = nx;
8187             }
8188             this.isCleaned = true;
8189             return this;
8190         },
8191
8192         // private
8193         calcOffsetsTo : function(el){
8194             el = Roo.get(el);
8195             var d = el.dom;
8196             var restorePos = false;
8197             if(el.getStyle('position') == 'static'){
8198                 el.position('relative');
8199                 restorePos = true;
8200             }
8201             var x = 0, y =0;
8202             var op = this.dom;
8203             while(op && op != d && op.tagName != 'HTML'){
8204                 x+= op.offsetLeft;
8205                 y+= op.offsetTop;
8206                 op = op.offsetParent;
8207             }
8208             if(restorePos){
8209                 el.position('static');
8210             }
8211             return [x, y];
8212         },
8213
8214         /**
8215          * Scrolls this element into view within the passed container.
8216          * @param {String/HTMLElement/Element} container (optional) The container element to scroll (defaults to document.body)
8217          * @param {Boolean} hscroll (optional) False to disable horizontal scroll (defaults to true)
8218          * @return {Roo.Element} this
8219          */
8220         scrollIntoView : function(container, hscroll){
8221             var c = Roo.getDom(container) || document.body;
8222             var el = this.dom;
8223
8224             var o = this.calcOffsetsTo(c),
8225                 l = o[0],
8226                 t = o[1],
8227                 b = t+el.offsetHeight,
8228                 r = l+el.offsetWidth;
8229
8230             var ch = c.clientHeight;
8231             var ct = parseInt(c.scrollTop, 10);
8232             var cl = parseInt(c.scrollLeft, 10);
8233             var cb = ct + ch;
8234             var cr = cl + c.clientWidth;
8235
8236             if(t < ct){
8237                 c.scrollTop = t;
8238             }else if(b > cb){
8239                 c.scrollTop = b-ch;
8240             }
8241
8242             if(hscroll !== false){
8243                 if(l < cl){
8244                     c.scrollLeft = l;
8245                 }else if(r > cr){
8246                     c.scrollLeft = r-c.clientWidth;
8247                 }
8248             }
8249             return this;
8250         },
8251
8252         // private
8253         scrollChildIntoView : function(child, hscroll){
8254             Roo.fly(child, '_scrollChildIntoView').scrollIntoView(this, hscroll);
8255         },
8256
8257         /**
8258          * Measures the element's content height and updates height to match. Note: this function uses setTimeout so
8259          * the new height may not be available immediately.
8260          * @param {Boolean} animate (optional) Animate the transition (defaults to false)
8261          * @param {Float} duration (optional) Length of the animation in seconds (defaults to .35)
8262          * @param {Function} onComplete (optional) Function to call when animation completes
8263          * @param {String} easing (optional) Easing method to use (defaults to easeOut)
8264          * @return {Roo.Element} this
8265          */
8266         autoHeight : function(animate, duration, onComplete, easing){
8267             var oldHeight = this.getHeight();
8268             this.clip();
8269             this.setHeight(1); // force clipping
8270             setTimeout(function(){
8271                 var height = parseInt(this.dom.scrollHeight, 10); // parseInt for Safari
8272                 if(!animate){
8273                     this.setHeight(height);
8274                     this.unclip();
8275                     if(typeof onComplete == "function"){
8276                         onComplete();
8277                     }
8278                 }else{
8279                     this.setHeight(oldHeight); // restore original height
8280                     this.setHeight(height, animate, duration, function(){
8281                         this.unclip();
8282                         if(typeof onComplete == "function") { onComplete(); }
8283                     }.createDelegate(this), easing);
8284                 }
8285             }.createDelegate(this), 0);
8286             return this;
8287         },
8288
8289         /**
8290          * Returns true if this element is an ancestor of the passed element
8291          * @param {HTMLElement/String} el The element to check
8292          * @return {Boolean} True if this element is an ancestor of el, else false
8293          */
8294         contains : function(el){
8295             if(!el){return false;}
8296             return D.isAncestor(this.dom, el.dom ? el.dom : el);
8297         },
8298
8299         /**
8300          * Checks whether the element is currently visible using both visibility and display properties.
8301          * @param {Boolean} deep (optional) True to walk the dom and see if parent elements are hidden (defaults to false)
8302          * @return {Boolean} True if the element is currently visible, else false
8303          */
8304         isVisible : function(deep) {
8305             var vis = !(this.getStyle("visibility") == "hidden" || this.getStyle("display") == "none");
8306             if(deep !== true || !vis){
8307                 return vis;
8308             }
8309             var p = this.dom.parentNode;
8310             while(p && p.tagName.toLowerCase() != "body"){
8311                 if(!Roo.fly(p, '_isVisible').isVisible()){
8312                     return false;
8313                 }
8314                 p = p.parentNode;
8315             }
8316             return true;
8317         },
8318
8319         /**
8320          * Creates a {@link Roo.CompositeElement} for child nodes based on the passed CSS selector (the selector should not contain an id).
8321          * @param {String} selector The CSS selector
8322          * @param {Boolean} unique (optional) True to create a unique Roo.Element for each child (defaults to false, which creates a single shared flyweight object)
8323          * @return {CompositeElement/CompositeElementLite} The composite element
8324          */
8325         select : function(selector, unique){
8326             return El.select(selector, unique, this.dom);
8327         },
8328
8329         /**
8330          * Selects child nodes based on the passed CSS selector (the selector should not contain an id).
8331          * @param {String} selector The CSS selector
8332          * @return {Array} An array of the matched nodes
8333          */
8334         query : function(selector, unique){
8335             return Roo.DomQuery.select(selector, this.dom);
8336         },
8337
8338         /**
8339          * Selects a single child at any depth below this element based on the passed CSS selector (the selector should not contain an id).
8340          * @param {String} selector The CSS selector
8341          * @param {Boolean} returnDom (optional) True to return the DOM node instead of Roo.Element (defaults to false)
8342          * @return {HTMLElement/Roo.Element} The child Roo.Element (or DOM node if returnDom = true)
8343          */
8344         child : function(selector, returnDom){
8345             var n = Roo.DomQuery.selectNode(selector, this.dom);
8346             return returnDom ? n : Roo.get(n);
8347         },
8348
8349         /**
8350          * Selects a single *direct* child based on the passed CSS selector (the selector should not contain an id).
8351          * @param {String} selector The CSS selector
8352          * @param {Boolean} returnDom (optional) True to return the DOM node instead of Roo.Element (defaults to false)
8353          * @return {HTMLElement/Roo.Element} The child Roo.Element (or DOM node if returnDom = true)
8354          */
8355         down : function(selector, returnDom){
8356             var n = Roo.DomQuery.selectNode(" > " + selector, this.dom);
8357             return returnDom ? n : Roo.get(n);
8358         },
8359
8360         /**
8361          * Initializes a {@link Roo.dd.DD} drag drop object for this element.
8362          * @param {String} group The group the DD object is member of
8363          * @param {Object} config The DD config object
8364          * @param {Object} overrides An object containing methods to override/implement on the DD object
8365          * @return {Roo.dd.DD} The DD object
8366          */
8367         initDD : function(group, config, overrides){
8368             var dd = new Roo.dd.DD(Roo.id(this.dom), group, config);
8369             return Roo.apply(dd, overrides);
8370         },
8371
8372         /**
8373          * Initializes a {@link Roo.dd.DDProxy} object for this element.
8374          * @param {String} group The group the DDProxy object is member of
8375          * @param {Object} config The DDProxy config object
8376          * @param {Object} overrides An object containing methods to override/implement on the DDProxy object
8377          * @return {Roo.dd.DDProxy} The DDProxy object
8378          */
8379         initDDProxy : function(group, config, overrides){
8380             var dd = new Roo.dd.DDProxy(Roo.id(this.dom), group, config);
8381             return Roo.apply(dd, overrides);
8382         },
8383
8384         /**
8385          * Initializes a {@link Roo.dd.DDTarget} object for this element.
8386          * @param {String} group The group the DDTarget object is member of
8387          * @param {Object} config The DDTarget config object
8388          * @param {Object} overrides An object containing methods to override/implement on the DDTarget object
8389          * @return {Roo.dd.DDTarget} The DDTarget object
8390          */
8391         initDDTarget : function(group, config, overrides){
8392             var dd = new Roo.dd.DDTarget(Roo.id(this.dom), group, config);
8393             return Roo.apply(dd, overrides);
8394         },
8395
8396         /**
8397          * Sets the visibility of the element (see details). If the visibilityMode is set to Element.DISPLAY, it will use
8398          * the display property to hide the element, otherwise it uses visibility. The default is to hide and show using the visibility property.
8399          * @param {Boolean} visible Whether the element is visible
8400          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
8401          * @return {Roo.Element} this
8402          */
8403          setVisible : function(visible, animate){
8404             if(!animate || !A){
8405                 if(this.visibilityMode == El.DISPLAY){
8406                     this.setDisplayed(visible);
8407                 }else{
8408                     this.fixDisplay();
8409                     this.dom.style.visibility = visible ? "visible" : "hidden";
8410                 }
8411             }else{
8412                 // closure for composites
8413                 var dom = this.dom;
8414                 var visMode = this.visibilityMode;
8415                 if(visible){
8416                     this.setOpacity(.01);
8417                     this.setVisible(true);
8418                 }
8419                 this.anim({opacity: { to: (visible?1:0) }},
8420                       this.preanim(arguments, 1),
8421                       null, .35, 'easeIn', function(){
8422                          if(!visible){
8423                              if(visMode == El.DISPLAY){
8424                                  dom.style.display = "none";
8425                              }else{
8426                                  dom.style.visibility = "hidden";
8427                              }
8428                              Roo.get(dom).setOpacity(1);
8429                          }
8430                      });
8431             }
8432             return this;
8433         },
8434
8435         /**
8436          * Returns true if display is not "none"
8437          * @return {Boolean}
8438          */
8439         isDisplayed : function() {
8440             return this.getStyle("display") != "none";
8441         },
8442
8443         /**
8444          * Toggles the element's visibility or display, depending on visibility mode.
8445          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
8446          * @return {Roo.Element} this
8447          */
8448         toggle : function(animate){
8449             this.setVisible(!this.isVisible(), this.preanim(arguments, 0));
8450             return this;
8451         },
8452
8453         /**
8454          * Sets the CSS display property. Uses originalDisplay if the specified value is a boolean true.
8455          * @param {Boolean} value Boolean value to display the element using its default display, or a string to set the display directly
8456          * @return {Roo.Element} this
8457          */
8458         setDisplayed : function(value) {
8459             if(typeof value == "boolean"){
8460                value = value ? this.originalDisplay : "none";
8461             }
8462             this.setStyle("display", value);
8463             return this;
8464         },
8465
8466         /**
8467          * Tries to focus the element. Any exceptions are caught and ignored.
8468          * @return {Roo.Element} this
8469          */
8470         focus : function() {
8471             try{
8472                 this.dom.focus();
8473             }catch(e){}
8474             return this;
8475         },
8476
8477         /**
8478          * Tries to blur the element. Any exceptions are caught and ignored.
8479          * @return {Roo.Element} this
8480          */
8481         blur : function() {
8482             try{
8483                 this.dom.blur();
8484             }catch(e){}
8485             return this;
8486         },
8487
8488         /**
8489          * Adds one or more CSS classes to the element. Duplicate classes are automatically filtered out.
8490          * @param {String/Array} className The CSS class to add, or an array of classes
8491          * @return {Roo.Element} this
8492          */
8493         addClass : function(className){
8494             if(className instanceof Array){
8495                 for(var i = 0, len = className.length; i < len; i++) {
8496                     this.addClass(className[i]);
8497                 }
8498             }else{
8499                 if(className && !this.hasClass(className)){
8500                     if (this.dom instanceof SVGElement) {
8501                         this.dom.className.baseVal =this.dom.className.baseVal  + " " + className;
8502                     } else {
8503                         this.dom.className = this.dom.className + " " + className;
8504                     }
8505                 }
8506             }
8507             return this;
8508         },
8509
8510         /**
8511          * Adds one or more CSS classes to this element and removes the same class(es) from all siblings.
8512          * @param {String/Array} className The CSS class to add, or an array of classes
8513          * @return {Roo.Element} this
8514          */
8515         radioClass : function(className){
8516             var siblings = this.dom.parentNode.childNodes;
8517             for(var i = 0; i < siblings.length; i++) {
8518                 var s = siblings[i];
8519                 if(s.nodeType == 1){
8520                     Roo.get(s).removeClass(className);
8521                 }
8522             }
8523             this.addClass(className);
8524             return this;
8525         },
8526
8527         /**
8528          * Removes one or more CSS classes from the element.
8529          * @param {String/Array} className The CSS class to remove, or an array of classes
8530          * @return {Roo.Element} this
8531          */
8532         removeClass : function(className){
8533             
8534             var cn = this.dom instanceof SVGElement ? this.dom.className.baseVal : this.dom.className;
8535             if(!className || !cn){
8536                 return this;
8537             }
8538             if(className instanceof Array){
8539                 for(var i = 0, len = className.length; i < len; i++) {
8540                     this.removeClass(className[i]);
8541                 }
8542             }else{
8543                 if(this.hasClass(className)){
8544                     var re = this.classReCache[className];
8545                     if (!re) {
8546                        re = new RegExp('(?:^|\\s+)' + className + '(?:\\s+|$)', "g");
8547                        this.classReCache[className] = re;
8548                     }
8549                     if (this.dom instanceof SVGElement) {
8550                         this.dom.className.baseVal = cn.replace(re, " ");
8551                     } else {
8552                         this.dom.className = cn.replace(re, " ");
8553                     }
8554                 }
8555             }
8556             return this;
8557         },
8558
8559         // private
8560         classReCache: {},
8561
8562         /**
8563          * Toggles the specified CSS class on this element (removes it if it already exists, otherwise adds it).
8564          * @param {String} className The CSS class to toggle
8565          * @return {Roo.Element} this
8566          */
8567         toggleClass : function(className){
8568             if(this.hasClass(className)){
8569                 this.removeClass(className);
8570             }else{
8571                 this.addClass(className);
8572             }
8573             return this;
8574         },
8575
8576         /**
8577          * Checks if the specified CSS class exists on this element's DOM node.
8578          * @param {String} className The CSS class to check for
8579          * @return {Boolean} True if the class exists, else false
8580          */
8581         hasClass : function(className){
8582             if (this.dom instanceof SVGElement) {
8583                 return className && (' '+this.dom.className.baseVal +' ').indexOf(' '+className+' ') != -1; 
8584             } 
8585             return className && (' '+this.dom.className+' ').indexOf(' '+className+' ') != -1;
8586         },
8587
8588         /**
8589          * Replaces a CSS class on the element with another.  If the old name does not exist, the new name will simply be added.
8590          * @param {String} oldClassName The CSS class to replace
8591          * @param {String} newClassName The replacement CSS class
8592          * @return {Roo.Element} this
8593          */
8594         replaceClass : function(oldClassName, newClassName){
8595             this.removeClass(oldClassName);
8596             this.addClass(newClassName);
8597             return this;
8598         },
8599
8600         /**
8601          * Returns an object with properties matching the styles requested.
8602          * For example, el.getStyles('color', 'font-size', 'width') might return
8603          * {'color': '#FFFFFF', 'font-size': '13px', 'width': '100px'}.
8604          * @param {String} style1 A style name
8605          * @param {String} style2 A style name
8606          * @param {String} etc.
8607          * @return {Object} The style object
8608          */
8609         getStyles : function(){
8610             var a = arguments, len = a.length, r = {};
8611             for(var i = 0; i < len; i++){
8612                 r[a[i]] = this.getStyle(a[i]);
8613             }
8614             return r;
8615         },
8616
8617         /**
8618          * Normalizes currentStyle and computedStyle. This is not YUI getStyle, it is an optimised version.
8619          * @param {String} property The style property whose value is returned.
8620          * @return {String} The current value of the style property for this element.
8621          */
8622         getStyle : function(){
8623             return view && view.getComputedStyle ?
8624                 function(prop){
8625                     var el = this.dom, v, cs, camel;
8626                     if(prop == 'float'){
8627                         prop = "cssFloat";
8628                     }
8629                     if(el.style && (v = el.style[prop])){
8630                         return v;
8631                     }
8632                     if(cs = view.getComputedStyle(el, "")){
8633                         if(!(camel = propCache[prop])){
8634                             camel = propCache[prop] = prop.replace(camelRe, camelFn);
8635                         }
8636                         return cs[camel];
8637                     }
8638                     return null;
8639                 } :
8640                 function(prop){
8641                     var el = this.dom, v, cs, camel;
8642                     if(prop == 'opacity'){
8643                         if(typeof el.style.filter == 'string'){
8644                             var m = el.style.filter.match(/alpha\(opacity=(.*)\)/i);
8645                             if(m){
8646                                 var fv = parseFloat(m[1]);
8647                                 if(!isNaN(fv)){
8648                                     return fv ? fv / 100 : 0;
8649                                 }
8650                             }
8651                         }
8652                         return 1;
8653                     }else if(prop == 'float'){
8654                         prop = "styleFloat";
8655                     }
8656                     if(!(camel = propCache[prop])){
8657                         camel = propCache[prop] = prop.replace(camelRe, camelFn);
8658                     }
8659                     if(v = el.style[camel]){
8660                         return v;
8661                     }
8662                     if(cs = el.currentStyle){
8663                         return cs[camel];
8664                     }
8665                     return null;
8666                 };
8667         }(),
8668
8669         /**
8670          * Wrapper for setting style properties, also takes single object parameter of multiple styles.
8671          * @param {String/Object} property The style property to be set, or an object of multiple styles.
8672          * @param {String} value (optional) The value to apply to the given property, or null if an object was passed.
8673          * @return {Roo.Element} this
8674          */
8675         setStyle : function(prop, value){
8676             if(typeof prop == "string"){
8677                 
8678                 if (prop == 'float') {
8679                     this.setStyle(Roo.isIE ? 'styleFloat'  : 'cssFloat', value);
8680                     return this;
8681                 }
8682                 
8683                 var camel;
8684                 if(!(camel = propCache[prop])){
8685                     camel = propCache[prop] = prop.replace(camelRe, camelFn);
8686                 }
8687                 
8688                 if(camel == 'opacity') {
8689                     this.setOpacity(value);
8690                 }else{
8691                     this.dom.style[camel] = value;
8692                 }
8693             }else{
8694                 for(var style in prop){
8695                     if(typeof prop[style] != "function"){
8696                        this.setStyle(style, prop[style]);
8697                     }
8698                 }
8699             }
8700             return this;
8701         },
8702
8703         /**
8704          * More flexible version of {@link #setStyle} for setting style properties.
8705          * @param {String/Object/Function} styles A style specification string, e.g. "width:100px", or object in the form {width:"100px"}, or
8706          * a function which returns such a specification.
8707          * @return {Roo.Element} this
8708          */
8709         applyStyles : function(style){
8710             Roo.DomHelper.applyStyles(this.dom, style);
8711             return this;
8712         },
8713
8714         /**
8715           * 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).
8716           * @return {Number} The X position of the element
8717           */
8718         getX : function(){
8719             return D.getX(this.dom);
8720         },
8721
8722         /**
8723           * 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).
8724           * @return {Number} The Y position of the element
8725           */
8726         getY : function(){
8727             return D.getY(this.dom);
8728         },
8729
8730         /**
8731           * 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).
8732           * @return {Array} The XY position of the element
8733           */
8734         getXY : function(){
8735             return D.getXY(this.dom);
8736         },
8737
8738         /**
8739          * 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).
8740          * @param {Number} The X position of the element
8741          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
8742          * @return {Roo.Element} this
8743          */
8744         setX : function(x, animate){
8745             if(!animate || !A){
8746                 D.setX(this.dom, x);
8747             }else{
8748                 this.setXY([x, this.getY()], this.preanim(arguments, 1));
8749             }
8750             return this;
8751         },
8752
8753         /**
8754          * 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).
8755          * @param {Number} The Y position of the element
8756          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
8757          * @return {Roo.Element} this
8758          */
8759         setY : function(y, animate){
8760             if(!animate || !A){
8761                 D.setY(this.dom, y);
8762             }else{
8763                 this.setXY([this.getX(), y], this.preanim(arguments, 1));
8764             }
8765             return this;
8766         },
8767
8768         /**
8769          * Sets the element's left position directly using CSS style (instead of {@link #setX}).
8770          * @param {String} left The left CSS property value
8771          * @return {Roo.Element} this
8772          */
8773         setLeft : function(left){
8774             this.setStyle("left", this.addUnits(left));
8775             return this;
8776         },
8777
8778         /**
8779          * Sets the element's top position directly using CSS style (instead of {@link #setY}).
8780          * @param {String} top The top CSS property value
8781          * @return {Roo.Element} this
8782          */
8783         setTop : function(top){
8784             this.setStyle("top", this.addUnits(top));
8785             return this;
8786         },
8787
8788         /**
8789          * Sets the element's CSS right style.
8790          * @param {String} right The right CSS property value
8791          * @return {Roo.Element} this
8792          */
8793         setRight : function(right){
8794             this.setStyle("right", this.addUnits(right));
8795             return this;
8796         },
8797
8798         /**
8799          * Sets the element's CSS bottom style.
8800          * @param {String} bottom The bottom CSS property value
8801          * @return {Roo.Element} this
8802          */
8803         setBottom : function(bottom){
8804             this.setStyle("bottom", this.addUnits(bottom));
8805             return this;
8806         },
8807
8808         /**
8809          * Sets the position of the element in page coordinates, regardless of how the element is positioned.
8810          * The element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
8811          * @param {Array} pos Contains X & Y [x, y] values for new position (coordinates are page-based)
8812          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
8813          * @return {Roo.Element} this
8814          */
8815         setXY : function(pos, animate){
8816             if(!animate || !A){
8817                 D.setXY(this.dom, pos);
8818             }else{
8819                 this.anim({points: {to: pos}}, this.preanim(arguments, 1), 'motion');
8820             }
8821             return this;
8822         },
8823
8824         /**
8825          * Sets the position of the element in page coordinates, regardless of how the element is positioned.
8826          * The element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
8827          * @param {Number} x X value for new position (coordinates are page-based)
8828          * @param {Number} y Y value for new position (coordinates are page-based)
8829          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
8830          * @return {Roo.Element} this
8831          */
8832         setLocation : function(x, y, animate){
8833             this.setXY([x, y], this.preanim(arguments, 2));
8834             return this;
8835         },
8836
8837         /**
8838          * Sets the position of the element in page coordinates, regardless of how the element is positioned.
8839          * The element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
8840          * @param {Number} x X value for new position (coordinates are page-based)
8841          * @param {Number} y Y value for new position (coordinates are page-based)
8842          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
8843          * @return {Roo.Element} this
8844          */
8845         moveTo : function(x, y, animate){
8846             this.setXY([x, y], this.preanim(arguments, 2));
8847             return this;
8848         },
8849
8850         /**
8851          * Returns the region of the given element.
8852          * The element must be part of the DOM tree to have a region (display:none or elements not appended return false).
8853          * @return {Region} A Roo.lib.Region containing "top, left, bottom, right" member data.
8854          */
8855         getRegion : function(){
8856             return D.getRegion(this.dom);
8857         },
8858
8859         /**
8860          * Returns the offset height of the element
8861          * @param {Boolean} contentHeight (optional) true to get the height minus borders and padding
8862          * @return {Number} The element's height
8863          */
8864         getHeight : function(contentHeight){
8865             var h = this.dom.offsetHeight || 0;
8866             return contentHeight !== true ? h : h-this.getBorderWidth("tb")-this.getPadding("tb");
8867         },
8868
8869         /**
8870          * Returns the offset width of the element
8871          * @param {Boolean} contentWidth (optional) true to get the width minus borders and padding
8872          * @return {Number} The element's width
8873          */
8874         getWidth : function(contentWidth){
8875             var w = this.dom.offsetWidth || 0;
8876             return contentWidth !== true ? w : w-this.getBorderWidth("lr")-this.getPadding("lr");
8877         },
8878
8879         /**
8880          * Returns either the offsetHeight or the height of this element based on CSS height adjusted by padding or borders
8881          * when needed to simulate offsetHeight when offsets aren't available. This may not work on display:none elements
8882          * if a height has not been set using CSS.
8883          * @return {Number}
8884          */
8885         getComputedHeight : function(){
8886             var h = Math.max(this.dom.offsetHeight, this.dom.clientHeight);
8887             if(!h){
8888                 h = parseInt(this.getStyle('height'), 10) || 0;
8889                 if(!this.isBorderBox()){
8890                     h += this.getFrameWidth('tb');
8891                 }
8892             }
8893             return h;
8894         },
8895
8896         /**
8897          * Returns either the offsetWidth or the width of this element based on CSS width adjusted by padding or borders
8898          * when needed to simulate offsetWidth when offsets aren't available. This may not work on display:none elements
8899          * if a width has not been set using CSS.
8900          * @return {Number}
8901          */
8902         getComputedWidth : function(){
8903             var w = Math.max(this.dom.offsetWidth, this.dom.clientWidth);
8904             if(!w){
8905                 w = parseInt(this.getStyle('width'), 10) || 0;
8906                 if(!this.isBorderBox()){
8907                     w += this.getFrameWidth('lr');
8908                 }
8909             }
8910             return w;
8911         },
8912
8913         /**
8914          * Returns the size of the element.
8915          * @param {Boolean} contentSize (optional) true to get the width/size minus borders and padding
8916          * @return {Object} An object containing the element's size {width: (element width), height: (element height)}
8917          */
8918         getSize : function(contentSize){
8919             return {width: this.getWidth(contentSize), height: this.getHeight(contentSize)};
8920         },
8921
8922         /**
8923          * Returns the width and height of the viewport.
8924          * @return {Object} An object containing the viewport's size {width: (viewport width), height: (viewport height)}
8925          */
8926         getViewSize : function(){
8927             var d = this.dom, doc = document, aw = 0, ah = 0;
8928             if(d == doc || d == doc.body){
8929                 return {width : D.getViewWidth(), height: D.getViewHeight()};
8930             }else{
8931                 return {
8932                     width : d.clientWidth,
8933                     height: d.clientHeight
8934                 };
8935             }
8936         },
8937
8938         /**
8939          * Returns the value of the "value" attribute
8940          * @param {Boolean} asNumber true to parse the value as a number
8941          * @return {String/Number}
8942          */
8943         getValue : function(asNumber){
8944             return asNumber ? parseInt(this.dom.value, 10) : this.dom.value;
8945         },
8946
8947         // private
8948         adjustWidth : function(width){
8949             if(typeof width == "number"){
8950                 if(this.autoBoxAdjust && !this.isBorderBox()){
8951                    width -= (this.getBorderWidth("lr") + this.getPadding("lr"));
8952                 }
8953                 if(width < 0){
8954                     width = 0;
8955                 }
8956             }
8957             return width;
8958         },
8959
8960         // private
8961         adjustHeight : function(height){
8962             if(typeof height == "number"){
8963                if(this.autoBoxAdjust && !this.isBorderBox()){
8964                    height -= (this.getBorderWidth("tb") + this.getPadding("tb"));
8965                }
8966                if(height < 0){
8967                    height = 0;
8968                }
8969             }
8970             return height;
8971         },
8972
8973         /**
8974          * Set the width of the element
8975          * @param {Number} width The new width
8976          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8977          * @return {Roo.Element} this
8978          */
8979         setWidth : function(width, animate){
8980             width = this.adjustWidth(width);
8981             if(!animate || !A){
8982                 this.dom.style.width = this.addUnits(width);
8983             }else{
8984                 this.anim({width: {to: width}}, this.preanim(arguments, 1));
8985             }
8986             return this;
8987         },
8988
8989         /**
8990          * Set the height of the element
8991          * @param {Number} height The new height
8992          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8993          * @return {Roo.Element} this
8994          */
8995          setHeight : function(height, animate){
8996             height = this.adjustHeight(height);
8997             if(!animate || !A){
8998                 this.dom.style.height = this.addUnits(height);
8999             }else{
9000                 this.anim({height: {to: height}}, this.preanim(arguments, 1));
9001             }
9002             return this;
9003         },
9004
9005         /**
9006          * Set the size of the element. If animation is true, both width an height will be animated concurrently.
9007          * @param {Number} width The new width
9008          * @param {Number} height The new height
9009          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
9010          * @return {Roo.Element} this
9011          */
9012          setSize : function(width, height, animate){
9013             if(typeof width == "object"){ // in case of object from getSize()
9014                 height = width.height; width = width.width;
9015             }
9016             width = this.adjustWidth(width); height = this.adjustHeight(height);
9017             if(!animate || !A){
9018                 this.dom.style.width = this.addUnits(width);
9019                 this.dom.style.height = this.addUnits(height);
9020             }else{
9021                 this.anim({width: {to: width}, height: {to: height}}, this.preanim(arguments, 2));
9022             }
9023             return this;
9024         },
9025
9026         /**
9027          * Sets the element's position and size in one shot. If animation is true then width, height, x and y will be animated concurrently.
9028          * @param {Number} x X value for new position (coordinates are page-based)
9029          * @param {Number} y Y value for new position (coordinates are page-based)
9030          * @param {Number} width The new width
9031          * @param {Number} height The new height
9032          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
9033          * @return {Roo.Element} this
9034          */
9035         setBounds : function(x, y, width, height, animate){
9036             if(!animate || !A){
9037                 this.setSize(width, height);
9038                 this.setLocation(x, y);
9039             }else{
9040                 width = this.adjustWidth(width); height = this.adjustHeight(height);
9041                 this.anim({points: {to: [x, y]}, width: {to: width}, height: {to: height}},
9042                               this.preanim(arguments, 4), 'motion');
9043             }
9044             return this;
9045         },
9046
9047         /**
9048          * 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.
9049          * @param {Roo.lib.Region} region The region to fill
9050          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
9051          * @return {Roo.Element} this
9052          */
9053         setRegion : function(region, animate){
9054             this.setBounds(region.left, region.top, region.right-region.left, region.bottom-region.top, this.preanim(arguments, 1));
9055             return this;
9056         },
9057
9058         /**
9059          * Appends an event handler
9060          *
9061          * @param {String}   eventName     The type of event to append
9062          * @param {Function} fn        The method the event invokes
9063          * @param {Object} scope       (optional) The scope (this object) of the fn
9064          * @param {Object}   options   (optional)An object with standard {@link Roo.EventManager#addListener} options
9065          */
9066         addListener : function(eventName, fn, scope, options)
9067         {
9068             if (eventName == 'dblclick') { // doublclick (touchstart) - faked on touch.
9069                 this.addListener('touchstart', this.onTapHandler, this);
9070             }
9071             
9072             // we need to handle a special case where dom element is a svg element.
9073             // in this case we do not actua
9074             if (!this.dom) {
9075                 return;
9076             }
9077             
9078             if (this.dom instanceof SVGElement && !(this.dom instanceof SVGSVGElement)) {
9079                 if (typeof(this.listeners[eventName]) == 'undefined') {
9080                     this.listeners[eventName] =  new Roo.util.Event(this, eventName);
9081                 }
9082                 this.listeners[eventName].addListener(fn, scope, options);
9083                 return;
9084             }
9085             
9086                 
9087             Roo.EventManager.on(this.dom,  eventName, fn, scope || this, options);
9088             
9089             
9090         },
9091         tapedTwice : false,
9092         onTapHandler : function(event)
9093         {
9094             if(!this.tapedTwice) {
9095                 this.tapedTwice = true;
9096                 var s = this;
9097                 setTimeout( function() {
9098                     s.tapedTwice = false;
9099                 }, 300 );
9100                 return;
9101             }
9102             event.preventDefault();
9103             var revent = new MouseEvent('dblclick',  {
9104                 view: window,
9105                 bubbles: true,
9106                 cancelable: true
9107             });
9108              
9109             this.dom.dispatchEvent(revent);
9110             //action on double tap goes below
9111              
9112         }, 
9113  
9114         /**
9115          * Removes an event handler from this element
9116          * @param {String} eventName the type of event to remove
9117          * @param {Function} fn the method the event invokes
9118          * @param {Function} scope (needed for svg fake listeners)
9119          * @return {Roo.Element} this
9120          */
9121         removeListener : function(eventName, fn, scope){
9122             Roo.EventManager.removeListener(this.dom,  eventName, fn);
9123             if (typeof(this.listeners) == 'undefined'  || typeof(this.listeners[eventName]) == 'undefined') {
9124                 return this;
9125             }
9126             this.listeners[eventName].removeListener(fn, scope);
9127             return this;
9128         },
9129
9130         /**
9131          * Removes all previous added listeners from this element
9132          * @return {Roo.Element} this
9133          */
9134         removeAllListeners : function(){
9135             E.purgeElement(this.dom);
9136             this.listeners = {};
9137             return this;
9138         },
9139
9140         relayEvent : function(eventName, observable){
9141             this.on(eventName, function(e){
9142                 observable.fireEvent(eventName, e);
9143             });
9144         },
9145
9146         
9147         /**
9148          * Set the opacity of the element
9149          * @param {Float} opacity The new opacity. 0 = transparent, .5 = 50% visibile, 1 = fully visible, etc
9150          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
9151          * @return {Roo.Element} this
9152          */
9153          setOpacity : function(opacity, animate){
9154             if(!animate || !A){
9155                 var s = this.dom.style;
9156                 if(Roo.isIE){
9157                     s.zoom = 1;
9158                     s.filter = (s.filter || '').replace(/alpha\([^\)]*\)/gi,"") +
9159                                (opacity == 1 ? "" : "alpha(opacity=" + opacity * 100 + ")");
9160                 }else{
9161                     s.opacity = opacity;
9162                 }
9163             }else{
9164                 this.anim({opacity: {to: opacity}}, this.preanim(arguments, 1), null, .35, 'easeIn');
9165             }
9166             return this;
9167         },
9168
9169         /**
9170          * Gets the left X coordinate
9171          * @param {Boolean} local True to get the local css position instead of page coordinate
9172          * @return {Number}
9173          */
9174         getLeft : function(local){
9175             if(!local){
9176                 return this.getX();
9177             }else{
9178                 return parseInt(this.getStyle("left"), 10) || 0;
9179             }
9180         },
9181
9182         /**
9183          * Gets the right X coordinate of the element (element X position + element width)
9184          * @param {Boolean} local True to get the local css position instead of page coordinate
9185          * @return {Number}
9186          */
9187         getRight : function(local){
9188             if(!local){
9189                 return this.getX() + this.getWidth();
9190             }else{
9191                 return (this.getLeft(true) + this.getWidth()) || 0;
9192             }
9193         },
9194
9195         /**
9196          * Gets the top Y coordinate
9197          * @param {Boolean} local True to get the local css position instead of page coordinate
9198          * @return {Number}
9199          */
9200         getTop : function(local) {
9201             if(!local){
9202                 return this.getY();
9203             }else{
9204                 return parseInt(this.getStyle("top"), 10) || 0;
9205             }
9206         },
9207
9208         /**
9209          * Gets the bottom Y coordinate of the element (element Y position + element height)
9210          * @param {Boolean} local True to get the local css position instead of page coordinate
9211          * @return {Number}
9212          */
9213         getBottom : function(local){
9214             if(!local){
9215                 return this.getY() + this.getHeight();
9216             }else{
9217                 return (this.getTop(true) + this.getHeight()) || 0;
9218             }
9219         },
9220
9221         /**
9222         * Initializes positioning on this element. If a desired position is not passed, it will make the
9223         * the element positioned relative IF it is not already positioned.
9224         * @param {String} pos (optional) Positioning to use "relative", "absolute" or "fixed"
9225         * @param {Number} zIndex (optional) The zIndex to apply
9226         * @param {Number} x (optional) Set the page X position
9227         * @param {Number} y (optional) Set the page Y position
9228         */
9229         position : function(pos, zIndex, x, y){
9230             if(!pos){
9231                if(this.getStyle('position') == 'static'){
9232                    this.setStyle('position', 'relative');
9233                }
9234             }else{
9235                 this.setStyle("position", pos);
9236             }
9237             if(zIndex){
9238                 this.setStyle("z-index", zIndex);
9239             }
9240             if(x !== undefined && y !== undefined){
9241                 this.setXY([x, y]);
9242             }else if(x !== undefined){
9243                 this.setX(x);
9244             }else if(y !== undefined){
9245                 this.setY(y);
9246             }
9247         },
9248
9249         /**
9250         * Clear positioning back to the default when the document was loaded
9251         * @param {String} value (optional) The value to use for the left,right,top,bottom, defaults to '' (empty string). You could use 'auto'.
9252         * @return {Roo.Element} this
9253          */
9254         clearPositioning : function(value){
9255             value = value ||'';
9256             this.setStyle({
9257                 "left": value,
9258                 "right": value,
9259                 "top": value,
9260                 "bottom": value,
9261                 "z-index": "",
9262                 "position" : "static"
9263             });
9264             return this;
9265         },
9266
9267         /**
9268         * Gets an object with all CSS positioning properties. Useful along with setPostioning to get
9269         * snapshot before performing an update and then restoring the element.
9270         * @return {Object}
9271         */
9272         getPositioning : function(){
9273             var l = this.getStyle("left");
9274             var t = this.getStyle("top");
9275             return {
9276                 "position" : this.getStyle("position"),
9277                 "left" : l,
9278                 "right" : l ? "" : this.getStyle("right"),
9279                 "top" : t,
9280                 "bottom" : t ? "" : this.getStyle("bottom"),
9281                 "z-index" : this.getStyle("z-index")
9282             };
9283         },
9284
9285         /**
9286          * Gets the width of the border(s) for the specified side(s)
9287          * @param {String} side Can be t, l, r, b or any combination of those to add multiple values. For example,
9288          * passing lr would get the border (l)eft width + the border (r)ight width.
9289          * @return {Number} The width of the sides passed added together
9290          */
9291         getBorderWidth : function(side){
9292             return this.addStyles(side, El.borders);
9293         },
9294
9295         /**
9296          * Gets the width of the padding(s) for the specified side(s)
9297          * @param {String} side Can be t, l, r, b or any combination of those to add multiple values. For example,
9298          * passing lr would get the padding (l)eft + the padding (r)ight.
9299          * @return {Number} The padding of the sides passed added together
9300          */
9301         getPadding : function(side){
9302             return this.addStyles(side, El.paddings);
9303         },
9304
9305         /**
9306         * Set positioning with an object returned by getPositioning().
9307         * @param {Object} posCfg
9308         * @return {Roo.Element} this
9309          */
9310         setPositioning : function(pc){
9311             this.applyStyles(pc);
9312             if(pc.right == "auto"){
9313                 this.dom.style.right = "";
9314             }
9315             if(pc.bottom == "auto"){
9316                 this.dom.style.bottom = "";
9317             }
9318             return this;
9319         },
9320
9321         // private
9322         fixDisplay : function(){
9323             if(this.getStyle("display") == "none"){
9324                 this.setStyle("visibility", "hidden");
9325                 this.setStyle("display", this.originalDisplay); // first try reverting to default
9326                 if(this.getStyle("display") == "none"){ // if that fails, default to block
9327                     this.setStyle("display", "block");
9328                 }
9329             }
9330         },
9331
9332         /**
9333          * Quick set left and top adding default units
9334          * @param {String} left The left CSS property value
9335          * @param {String} top The top CSS property value
9336          * @return {Roo.Element} this
9337          */
9338          setLeftTop : function(left, top){
9339             this.dom.style.left = this.addUnits(left);
9340             this.dom.style.top = this.addUnits(top);
9341             return this;
9342         },
9343
9344         /**
9345          * Move this element relative to its current position.
9346          * @param {String} direction Possible values are: "l","left" - "r","right" - "t","top","up" - "b","bottom","down".
9347          * @param {Number} distance How far to move the element in pixels
9348          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
9349          * @return {Roo.Element} this
9350          */
9351          move : function(direction, distance, animate){
9352             var xy = this.getXY();
9353             direction = direction.toLowerCase();
9354             switch(direction){
9355                 case "l":
9356                 case "left":
9357                     this.moveTo(xy[0]-distance, xy[1], this.preanim(arguments, 2));
9358                     break;
9359                case "r":
9360                case "right":
9361                     this.moveTo(xy[0]+distance, xy[1], this.preanim(arguments, 2));
9362                     break;
9363                case "t":
9364                case "top":
9365                case "up":
9366                     this.moveTo(xy[0], xy[1]-distance, this.preanim(arguments, 2));
9367                     break;
9368                case "b":
9369                case "bottom":
9370                case "down":
9371                     this.moveTo(xy[0], xy[1]+distance, this.preanim(arguments, 2));
9372                     break;
9373             }
9374             return this;
9375         },
9376
9377         /**
9378          *  Store the current overflow setting and clip overflow on the element - use {@link #unclip} to remove
9379          * @return {Roo.Element} this
9380          */
9381         clip : function(){
9382             if(!this.isClipped){
9383                this.isClipped = true;
9384                this.originalClip = {
9385                    "o": this.getStyle("overflow"),
9386                    "x": this.getStyle("overflow-x"),
9387                    "y": this.getStyle("overflow-y")
9388                };
9389                this.setStyle("overflow", "hidden");
9390                this.setStyle("overflow-x", "hidden");
9391                this.setStyle("overflow-y", "hidden");
9392             }
9393             return this;
9394         },
9395
9396         /**
9397          *  Return clipping (overflow) to original clipping before clip() was called
9398          * @return {Roo.Element} this
9399          */
9400         unclip : function(){
9401             if(this.isClipped){
9402                 this.isClipped = false;
9403                 var o = this.originalClip;
9404                 if(o.o){this.setStyle("overflow", o.o);}
9405                 if(o.x){this.setStyle("overflow-x", o.x);}
9406                 if(o.y){this.setStyle("overflow-y", o.y);}
9407             }
9408             return this;
9409         },
9410
9411
9412         /**
9413          * Gets the x,y coordinates specified by the anchor position on the element.
9414          * @param {String} anchor (optional) The specified anchor position (defaults to "c").  See {@link #alignTo} for details on supported anchor positions.
9415          * @param {Object} size (optional) An object containing the size to use for calculating anchor position
9416          *                       {width: (target width), height: (target height)} (defaults to the element's current size)
9417          * @param {Boolean} local (optional) True to get the local (element top/left-relative) anchor position instead of page coordinates
9418          * @return {Array} [x, y] An array containing the element's x and y coordinates
9419          */
9420         getAnchorXY : function(anchor, local, s){
9421             //Passing a different size is useful for pre-calculating anchors,
9422             //especially for anchored animations that change the el size.
9423
9424             var w, h, vp = false;
9425             if(!s){
9426                 var d = this.dom;
9427                 if(d == document.body || d == document){
9428                     vp = true;
9429                     w = D.getViewWidth(); h = D.getViewHeight();
9430                 }else{
9431                     w = this.getWidth(); h = this.getHeight();
9432                 }
9433             }else{
9434                 w = s.width;  h = s.height;
9435             }
9436             var x = 0, y = 0, r = Math.round;
9437             switch((anchor || "tl").toLowerCase()){
9438                 case "c":
9439                     x = r(w*.5);
9440                     y = r(h*.5);
9441                 break;
9442                 case "t":
9443                     x = r(w*.5);
9444                     y = 0;
9445                 break;
9446                 case "l":
9447                     x = 0;
9448                     y = r(h*.5);
9449                 break;
9450                 case "r":
9451                     x = w;
9452                     y = r(h*.5);
9453                 break;
9454                 case "b":
9455                     x = r(w*.5);
9456                     y = h;
9457                 break;
9458                 case "tl":
9459                     x = 0;
9460                     y = 0;
9461                 break;
9462                 case "bl":
9463                     x = 0;
9464                     y = h;
9465                 break;
9466                 case "br":
9467                     x = w;
9468                     y = h;
9469                 break;
9470                 case "tr":
9471                     x = w;
9472                     y = 0;
9473                 break;
9474             }
9475             if(local === true){
9476                 return [x, y];
9477             }
9478             if(vp){
9479                 var sc = this.getScroll();
9480                 return [x + sc.left, y + sc.top];
9481             }
9482             //Add the element's offset xy
9483             var o = this.getXY();
9484             return [x+o[0], y+o[1]];
9485         },
9486
9487         /**
9488          * Gets the x,y coordinates to align this element with another element. See {@link #alignTo} for more info on the
9489          * supported position values.
9490          * @param {String/HTMLElement/Roo.Element} element The element to align to.
9491          * @param {String} position The position to align to.
9492          * @param {Array} offsets (optional) Offset the positioning by [x, y]
9493          * @return {Array} [x, y]
9494          */
9495         getAlignToXY : function(el, p, o)
9496         {
9497             el = Roo.get(el);
9498             var d = this.dom;
9499             if(!el.dom){
9500                 throw "Element.alignTo with an element that doesn't exist";
9501             }
9502             var c = false; //constrain to viewport
9503             var p1 = "", p2 = "";
9504             o = o || [0,0];
9505
9506             if(!p){
9507                 p = "tl-bl";
9508             }else if(p == "?"){
9509                 p = "tl-bl?";
9510             }else if(p.indexOf("-") == -1){
9511                 p = "tl-" + p;
9512             }
9513             p = p.toLowerCase();
9514             var m = p.match(/^([a-z]+)-([a-z]+)(\?)?$/);
9515             if(!m){
9516                throw "Element.alignTo with an invalid alignment " + p;
9517             }
9518             p1 = m[1]; p2 = m[2]; c = !!m[3];
9519
9520             //Subtract the aligned el's internal xy from the target's offset xy
9521             //plus custom offset to get the aligned el's new offset xy
9522             var a1 = this.getAnchorXY(p1, true);
9523             var a2 = el.getAnchorXY(p2, false);
9524             var x = a2[0] - a1[0] + o[0];
9525             var y = a2[1] - a1[1] + o[1];
9526             if(c){
9527                 //constrain the aligned el to viewport if necessary
9528                 var w = this.getWidth(), h = this.getHeight(), r = el.getRegion();
9529                 // 5px of margin for ie
9530                 var dw = D.getViewWidth()-5, dh = D.getViewHeight()-5;
9531
9532                 //If we are at a viewport boundary and the aligned el is anchored on a target border that is
9533                 //perpendicular to the vp border, allow the aligned el to slide on that border,
9534                 //otherwise swap the aligned el to the opposite border of the target.
9535                 var p1y = p1.charAt(0), p1x = p1.charAt(p1.length-1);
9536                var p2y = p2.charAt(0), p2x = p2.charAt(p2.length-1);
9537                var swapY = ((p1y=="t" && p2y=="b") || (p1y=="b" && p2y=="t")  );
9538                var swapX = ((p1x=="r" && p2x=="l") || (p1x=="l" && p2x=="r"));
9539
9540                var doc = document;
9541                var scrollX = (doc.documentElement.scrollLeft || doc.body.scrollLeft || 0)+5;
9542                var scrollY = (doc.documentElement.scrollTop || doc.body.scrollTop || 0)+5;
9543
9544                if((x+w) > dw + scrollX){
9545                     x = swapX ? r.left-w : dw+scrollX-w;
9546                 }
9547                if(x < scrollX){
9548                    x = swapX ? r.right : scrollX;
9549                }
9550                if((y+h) > dh + scrollY){
9551                     y = swapY ? r.top-h : dh+scrollY-h;
9552                 }
9553                if (y < scrollY){
9554                    y = swapY ? r.bottom : scrollY;
9555                }
9556             }
9557             return [x,y];
9558         },
9559
9560         // private
9561         getConstrainToXY : function(){
9562             var os = {top:0, left:0, bottom:0, right: 0};
9563
9564             return function(el, local, offsets, proposedXY){
9565                 el = Roo.get(el);
9566                 offsets = offsets ? Roo.applyIf(offsets, os) : os;
9567
9568                 var vw, vh, vx = 0, vy = 0;
9569                 if(el.dom == document.body || el.dom == document){
9570                     vw = Roo.lib.Dom.getViewWidth();
9571                     vh = Roo.lib.Dom.getViewHeight();
9572                 }else{
9573                     vw = el.dom.clientWidth;
9574                     vh = el.dom.clientHeight;
9575                     if(!local){
9576                         var vxy = el.getXY();
9577                         vx = vxy[0];
9578                         vy = vxy[1];
9579                     }
9580                 }
9581
9582                 var s = el.getScroll();
9583
9584                 vx += offsets.left + s.left;
9585                 vy += offsets.top + s.top;
9586
9587                 vw -= offsets.right;
9588                 vh -= offsets.bottom;
9589
9590                 var vr = vx+vw;
9591                 var vb = vy+vh;
9592
9593                 var xy = proposedXY || (!local ? this.getXY() : [this.getLeft(true), this.getTop(true)]);
9594                 var x = xy[0], y = xy[1];
9595                 var w = this.dom.offsetWidth, h = this.dom.offsetHeight;
9596
9597                 // only move it if it needs it
9598                 var moved = false;
9599
9600                 // first validate right/bottom
9601                 if((x + w) > vr){
9602                     x = vr - w;
9603                     moved = true;
9604                 }
9605                 if((y + h) > vb){
9606                     y = vb - h;
9607                     moved = true;
9608                 }
9609                 // then make sure top/left isn't negative
9610                 if(x < vx){
9611                     x = vx;
9612                     moved = true;
9613                 }
9614                 if(y < vy){
9615                     y = vy;
9616                     moved = true;
9617                 }
9618                 return moved ? [x, y] : false;
9619             };
9620         }(),
9621
9622         // private
9623         adjustForConstraints : function(xy, parent, offsets){
9624             return this.getConstrainToXY(parent || document, false, offsets, xy) ||  xy;
9625         },
9626
9627         /**
9628          * Aligns this element with another element relative to the specified anchor points. If the other element is the
9629          * document it aligns it to the viewport.
9630          * The position parameter is optional, and can be specified in any one of the following formats:
9631          * <ul>
9632          *   <li><b>Blank</b>: Defaults to aligning the element's top-left corner to the target's bottom-left corner ("tl-bl").</li>
9633          *   <li><b>One anchor (deprecated)</b>: The passed anchor position is used as the target element's anchor point.
9634          *       The element being aligned will position its top-left corner (tl) to that point.  <i>This method has been
9635          *       deprecated in favor of the newer two anchor syntax below</i>.</li>
9636          *   <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
9637          *       element's anchor point, and the second value is used as the target's anchor point.</li>
9638          * </ul>
9639          * In addition to the anchor points, the position parameter also supports the "?" character.  If "?" is passed at the end of
9640          * the position string, the element will attempt to align as specified, but the position will be adjusted to constrain to
9641          * the viewport if necessary.  Note that the element being aligned might be swapped to align to a different position than
9642          * that specified in order to enforce the viewport constraints.
9643          * Following are all of the supported anchor positions:
9644     <pre>
9645     Value  Description
9646     -----  -----------------------------
9647     tl     The top left corner (default)
9648     t      The center of the top edge
9649     tr     The top right corner
9650     l      The center of the left edge
9651     c      In the center of the element
9652     r      The center of the right edge
9653     bl     The bottom left corner
9654     b      The center of the bottom edge
9655     br     The bottom right corner
9656     </pre>
9657     Example Usage:
9658     <pre><code>
9659     // align el to other-el using the default positioning ("tl-bl", non-constrained)
9660     el.alignTo("other-el");
9661
9662     // align the top left corner of el with the top right corner of other-el (constrained to viewport)
9663     el.alignTo("other-el", "tr?");
9664
9665     // align the bottom right corner of el with the center left edge of other-el
9666     el.alignTo("other-el", "br-l?");
9667
9668     // align the center of el with the bottom left corner of other-el and
9669     // adjust the x position by -6 pixels (and the y position by 0)
9670     el.alignTo("other-el", "c-bl", [-6, 0]);
9671     </code></pre>
9672          * @param {String/HTMLElement/Roo.Element} element The element to align to.
9673          * @param {String} position The position to align to.
9674          * @param {Array} offsets (optional) Offset the positioning by [x, y]
9675          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
9676          * @return {Roo.Element} this
9677          */
9678         alignTo : function(element, position, offsets, animate){
9679             var xy = this.getAlignToXY(element, position, offsets);
9680             this.setXY(xy, this.preanim(arguments, 3));
9681             return this;
9682         },
9683
9684         /**
9685          * Anchors an element to another element and realigns it when the window is resized.
9686          * @param {String/HTMLElement/Roo.Element} element The element to align to.
9687          * @param {String} position The position to align to.
9688          * @param {Array} offsets (optional) Offset the positioning by [x, y]
9689          * @param {Boolean/Object} animate (optional) True for the default animation or a standard Element animation config object
9690          * @param {Boolean/Number} monitorScroll (optional) True to monitor body scroll and reposition. If this parameter
9691          * is a number, it is used as the buffer delay (defaults to 50ms).
9692          * @param {Function} callback The function to call after the animation finishes
9693          * @return {Roo.Element} this
9694          */
9695         anchorTo : function(el, alignment, offsets, animate, monitorScroll, callback){
9696             var action = function(){
9697                 this.alignTo(el, alignment, offsets, animate);
9698                 Roo.callback(callback, this);
9699             };
9700             Roo.EventManager.onWindowResize(action, this);
9701             var tm = typeof monitorScroll;
9702             if(tm != 'undefined'){
9703                 Roo.EventManager.on(window, 'scroll', action, this,
9704                     {buffer: tm == 'number' ? monitorScroll : 50});
9705             }
9706             action.call(this); // align immediately
9707             return this;
9708         },
9709         /**
9710          * Clears any opacity settings from this element. Required in some cases for IE.
9711          * @return {Roo.Element} this
9712          */
9713         clearOpacity : function(){
9714             if (window.ActiveXObject) {
9715                 if(typeof this.dom.style.filter == 'string' && (/alpha/i).test(this.dom.style.filter)){
9716                     this.dom.style.filter = "";
9717                 }
9718             } else {
9719                 this.dom.style.opacity = "";
9720                 this.dom.style["-moz-opacity"] = "";
9721                 this.dom.style["-khtml-opacity"] = "";
9722             }
9723             return this;
9724         },
9725
9726         /**
9727          * Hide this element - Uses display mode to determine whether to use "display" or "visibility". See {@link #setVisible}.
9728          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
9729          * @return {Roo.Element} this
9730          */
9731         hide : function(animate){
9732             this.setVisible(false, this.preanim(arguments, 0));
9733             return this;
9734         },
9735
9736         /**
9737         * Show this element - Uses display mode to determine whether to use "display" or "visibility". See {@link #setVisible}.
9738         * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
9739          * @return {Roo.Element} this
9740          */
9741         show : function(animate){
9742             this.setVisible(true, this.preanim(arguments, 0));
9743             return this;
9744         },
9745
9746         /**
9747          * @private Test if size has a unit, otherwise appends the default
9748          */
9749         addUnits : function(size){
9750             return Roo.Element.addUnits(size, this.defaultUnit);
9751         },
9752
9753         /**
9754          * Temporarily enables offsets (width,height,x,y) for an element with display:none, use endMeasure() when done.
9755          * @return {Roo.Element} this
9756          */
9757         beginMeasure : function(){
9758             var el = this.dom;
9759             if(el.offsetWidth || el.offsetHeight){
9760                 return this; // offsets work already
9761             }
9762             var changed = [];
9763             var p = this.dom, b = document.body; // start with this element
9764             while((!el.offsetWidth && !el.offsetHeight) && p && p.tagName && p != b){
9765                 var pe = Roo.get(p);
9766                 if(pe.getStyle('display') == 'none'){
9767                     changed.push({el: p, visibility: pe.getStyle("visibility")});
9768                     p.style.visibility = "hidden";
9769                     p.style.display = "block";
9770                 }
9771                 p = p.parentNode;
9772             }
9773             this._measureChanged = changed;
9774             return this;
9775
9776         },
9777
9778         /**
9779          * Restores displays to before beginMeasure was called
9780          * @return {Roo.Element} this
9781          */
9782         endMeasure : function(){
9783             var changed = this._measureChanged;
9784             if(changed){
9785                 for(var i = 0, len = changed.length; i < len; i++) {
9786                     var r = changed[i];
9787                     r.el.style.visibility = r.visibility;
9788                     r.el.style.display = "none";
9789                 }
9790                 this._measureChanged = null;
9791             }
9792             return this;
9793         },
9794
9795         /**
9796         * Update the innerHTML of this element, optionally searching for and processing scripts
9797         * @param {String} html The new HTML
9798         * @param {Boolean} loadScripts (optional) true to look for and process scripts
9799         * @param {Function} callback For async script loading you can be noticed when the update completes
9800         * @return {Roo.Element} this
9801          */
9802         update : function(html, loadScripts, callback){
9803             if(typeof html == "undefined"){
9804                 html = "";
9805             }
9806             if(loadScripts !== true){
9807                 this.dom.innerHTML = html;
9808                 if(typeof callback == "function"){
9809                     callback();
9810                 }
9811                 return this;
9812             }
9813             var id = Roo.id();
9814             var dom = this.dom;
9815
9816             html += '<span id="' + id + '"></span>';
9817
9818             E.onAvailable(id, function(){
9819                 var hd = document.getElementsByTagName("head")[0];
9820                 var re = /(?:<script([^>]*)?>)((\n|\r|.)*?)(?:<\/script>)/ig;
9821                 var srcRe = /\ssrc=([\'\"])(.*?)\1/i;
9822                 var typeRe = /\stype=([\'\"])(.*?)\1/i;
9823
9824                 var match;
9825                 while(match = re.exec(html)){
9826                     var attrs = match[1];
9827                     var srcMatch = attrs ? attrs.match(srcRe) : false;
9828                     if(srcMatch && srcMatch[2]){
9829                        var s = document.createElement("script");
9830                        s.src = srcMatch[2];
9831                        var typeMatch = attrs.match(typeRe);
9832                        if(typeMatch && typeMatch[2]){
9833                            s.type = typeMatch[2];
9834                        }
9835                        hd.appendChild(s);
9836                     }else if(match[2] && match[2].length > 0){
9837                         if(window.execScript) {
9838                            window.execScript(match[2]);
9839                         } else {
9840                             /**
9841                              * eval:var:id
9842                              * eval:var:dom
9843                              * eval:var:html
9844                              * 
9845                              */
9846                            window.eval(match[2]);
9847                         }
9848                     }
9849                 }
9850                 var el = document.getElementById(id);
9851                 if(el){el.parentNode.removeChild(el);}
9852                 if(typeof callback == "function"){
9853                     callback();
9854                 }
9855             });
9856             dom.innerHTML = html.replace(/(?:<script.*?>)((\n|\r|.)*?)(?:<\/script>)/ig, "");
9857             return this;
9858         },
9859
9860         /**
9861          * Direct access to the UpdateManager update() method (takes the same parameters).
9862          * @param {String/Function} url The url for this request or a function to call to get the url
9863          * @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}
9864          * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess)
9865          * @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.
9866          * @return {Roo.Element} this
9867          */
9868         load : function(){
9869             var um = this.getUpdateManager();
9870             um.update.apply(um, arguments);
9871             return this;
9872         },
9873
9874         /**
9875         * Gets this element's UpdateManager
9876         * @return {Roo.UpdateManager} The UpdateManager
9877         */
9878         getUpdateManager : function(){
9879             if(!this.updateManager){
9880                 this.updateManager = new Roo.UpdateManager(this);
9881             }
9882             return this.updateManager;
9883         },
9884
9885         /**
9886          * Disables text selection for this element (normalized across browsers)
9887          * @return {Roo.Element} this
9888          */
9889         unselectable : function(){
9890             this.dom.unselectable = "on";
9891             this.swallowEvent("selectstart", true);
9892             this.applyStyles("-moz-user-select:none;-khtml-user-select:none;");
9893             this.addClass("x-unselectable");
9894             return this;
9895         },
9896
9897         /**
9898         * Calculates the x, y to center this element on the screen
9899         * @return {Array} The x, y values [x, y]
9900         */
9901         getCenterXY : function(){
9902             return this.getAlignToXY(document, 'c-c');
9903         },
9904
9905         /**
9906         * Centers the Element in either the viewport, or another Element.
9907         * @param {String/HTMLElement/Roo.Element} centerIn (optional) The element in which to center the element.
9908         */
9909         center : function(centerIn){
9910             this.alignTo(centerIn || document, 'c-c');
9911             return this;
9912         },
9913
9914         /**
9915          * Tests various css rules/browsers to determine if this element uses a border box
9916          * @return {Boolean}
9917          */
9918         isBorderBox : function(){
9919             return noBoxAdjust[this.dom.tagName.toLowerCase()] || Roo.isBorderBox;
9920         },
9921
9922         /**
9923          * Return a box {x, y, width, height} that can be used to set another elements
9924          * size/location to match this element.
9925          * @param {Boolean} contentBox (optional) If true a box for the content of the element is returned.
9926          * @param {Boolean} local (optional) If true the element's left and top are returned instead of page x/y.
9927          * @return {Object} box An object in the format {x, y, width, height}
9928          */
9929         getBox : function(contentBox, local){
9930             var xy;
9931             if(!local){
9932                 xy = this.getXY();
9933             }else{
9934                 var left = parseInt(this.getStyle("left"), 10) || 0;
9935                 var top = parseInt(this.getStyle("top"), 10) || 0;
9936                 xy = [left, top];
9937             }
9938             var el = this.dom, w = el.offsetWidth, h = el.offsetHeight, bx;
9939             if(!contentBox){
9940                 bx = {x: xy[0], y: xy[1], 0: xy[0], 1: xy[1], width: w, height: h};
9941             }else{
9942                 var l = this.getBorderWidth("l")+this.getPadding("l");
9943                 var r = this.getBorderWidth("r")+this.getPadding("r");
9944                 var t = this.getBorderWidth("t")+this.getPadding("t");
9945                 var b = this.getBorderWidth("b")+this.getPadding("b");
9946                 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)};
9947             }
9948             bx.right = bx.x + bx.width;
9949             bx.bottom = bx.y + bx.height;
9950             return bx;
9951         },
9952
9953         /**
9954          * Returns the sum width of the padding and borders for the passed "sides". See getBorderWidth()
9955          for more information about the sides.
9956          * @param {String} sides
9957          * @return {Number}
9958          */
9959         getFrameWidth : function(sides, onlyContentBox){
9960             return onlyContentBox && Roo.isBorderBox ? 0 : (this.getPadding(sides) + this.getBorderWidth(sides));
9961         },
9962
9963         /**
9964          * 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.
9965          * @param {Object} box The box to fill {x, y, width, height}
9966          * @param {Boolean} adjust (optional) Whether to adjust for box-model issues automatically
9967          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
9968          * @return {Roo.Element} this
9969          */
9970         setBox : function(box, adjust, animate){
9971             var w = box.width, h = box.height;
9972             if((adjust && !this.autoBoxAdjust) && !this.isBorderBox()){
9973                w -= (this.getBorderWidth("lr") + this.getPadding("lr"));
9974                h -= (this.getBorderWidth("tb") + this.getPadding("tb"));
9975             }
9976             this.setBounds(box.x, box.y, w, h, this.preanim(arguments, 2));
9977             return this;
9978         },
9979
9980         /**
9981          * Forces the browser to repaint this element
9982          * @return {Roo.Element} this
9983          */
9984          repaint : function(){
9985             var dom = this.dom;
9986             this.addClass("x-repaint");
9987             setTimeout(function(){
9988                 Roo.get(dom).removeClass("x-repaint");
9989             }, 1);
9990             return this;
9991         },
9992
9993         /**
9994          * Returns an object with properties top, left, right and bottom representing the margins of this element unless sides is passed,
9995          * then it returns the calculated width of the sides (see getPadding)
9996          * @param {String} sides (optional) Any combination of l, r, t, b to get the sum of those sides
9997          * @return {Object/Number}
9998          */
9999         getMargins : function(side){
10000             if(!side){
10001                 return {
10002                     top: parseInt(this.getStyle("margin-top"), 10) || 0,
10003                     left: parseInt(this.getStyle("margin-left"), 10) || 0,
10004                     bottom: parseInt(this.getStyle("margin-bottom"), 10) || 0,
10005                     right: parseInt(this.getStyle("margin-right"), 10) || 0
10006                 };
10007             }else{
10008                 return this.addStyles(side, El.margins);
10009              }
10010         },
10011
10012         // private
10013         addStyles : function(sides, styles){
10014             var val = 0, v, w;
10015             for(var i = 0, len = sides.length; i < len; i++){
10016                 v = this.getStyle(styles[sides.charAt(i)]);
10017                 if(v){
10018                      w = parseInt(v, 10);
10019                      if(w){ val += w; }
10020                 }
10021             }
10022             return val;
10023         },
10024
10025         /**
10026          * Creates a proxy element of this element
10027          * @param {String/Object} config The class name of the proxy element or a DomHelper config object
10028          * @param {String/HTMLElement} renderTo (optional) The element or element id to render the proxy to (defaults to document.body)
10029          * @param {Boolean} matchBox (optional) True to align and size the proxy to this element now (defaults to false)
10030          * @return {Roo.Element} The new proxy element
10031          */
10032         createProxy : function(config, renderTo, matchBox){
10033             if(renderTo){
10034                 renderTo = Roo.getDom(renderTo);
10035             }else{
10036                 renderTo = document.body;
10037             }
10038             config = typeof config == "object" ?
10039                 config : {tag : "div", cls: config};
10040             var proxy = Roo.DomHelper.append(renderTo, config, true);
10041             if(matchBox){
10042                proxy.setBox(this.getBox());
10043             }
10044             return proxy;
10045         },
10046
10047         /**
10048          * Puts a mask over this element to disable user interaction. Requires core.css.
10049          * This method can only be applied to elements which accept child nodes.
10050          * @param {String} msg (optional) A message to display in the mask
10051          * @param {String} msgCls (optional) A css class to apply to the msg element - use no-spinner to hide the spinner on bootstrap
10052          * @return {Element} The mask  element
10053          */
10054         mask : function(msg, msgCls)
10055         {
10056             if(this.getStyle("position") == "static" && this.dom.tagName !== 'BODY'){
10057                 this.setStyle("position", "relative");
10058             }
10059             if(!this._mask){
10060                 this._mask = Roo.DomHelper.append(this.dom, {cls:"roo-el-mask"}, true);
10061             }
10062             
10063             this.addClass("x-masked");
10064             this._mask.setDisplayed(true);
10065             
10066             // we wander
10067             var z = 0;
10068             var dom = this.dom;
10069             while (dom && dom.style) {
10070                 if (!isNaN(parseInt(dom.style.zIndex))) {
10071                     z = Math.max(z, parseInt(dom.style.zIndex));
10072                 }
10073                 dom = dom.parentNode;
10074             }
10075             // if we are masking the body - then it hides everything..
10076             if (this.dom == document.body) {
10077                 z = 1000000;
10078                 this._mask.setWidth(Roo.lib.Dom.getDocumentWidth());
10079                 this._mask.setHeight(Roo.lib.Dom.getDocumentHeight());
10080             }
10081            
10082             if(typeof msg == 'string'){
10083                 if(!this._maskMsg){
10084                     this._maskMsg = Roo.DomHelper.append(this.dom, {
10085                         cls: "roo-el-mask-msg", 
10086                         cn: [
10087                             {
10088                                 tag: 'i',
10089                                 cls: 'fa fa-spinner fa-spin'
10090                             },
10091                             {
10092                                 tag: 'div'
10093                             }   
10094                         ]
10095                     }, true);
10096                 }
10097                 var mm = this._maskMsg;
10098                 mm.dom.className = msgCls ? "roo-el-mask-msg " + msgCls : "roo-el-mask-msg";
10099                 if (mm.dom.lastChild) { // weird IE issue?
10100                     mm.dom.lastChild.innerHTML = msg;
10101                 }
10102                 mm.setDisplayed(true);
10103                 mm.center(this);
10104                 mm.setStyle('z-index', z + 102);
10105             }
10106             if(Roo.isIE && !(Roo.isIE7 && Roo.isStrict) && this.getStyle('height') == 'auto'){ // ie will not expand full height automatically
10107                 this._mask.setHeight(this.getHeight());
10108             }
10109             this._mask.setStyle('z-index', z + 100);
10110             
10111             return this._mask;
10112         },
10113
10114         /**
10115          * Removes a previously applied mask. If removeEl is true the mask overlay is destroyed, otherwise
10116          * it is cached for reuse.
10117          */
10118         unmask : function(removeEl){
10119             if(this._mask){
10120                 if(removeEl === true){
10121                     this._mask.remove();
10122                     delete this._mask;
10123                     if(this._maskMsg){
10124                         this._maskMsg.remove();
10125                         delete this._maskMsg;
10126                     }
10127                 }else{
10128                     this._mask.setDisplayed(false);
10129                     if(this._maskMsg){
10130                         this._maskMsg.setDisplayed(false);
10131                     }
10132                 }
10133             }
10134             this.removeClass("x-masked");
10135         },
10136
10137         /**
10138          * Returns true if this element is masked
10139          * @return {Boolean}
10140          */
10141         isMasked : function(){
10142             return this._mask && this._mask.isVisible();
10143         },
10144
10145         /**
10146          * Creates an iframe shim for this element to keep selects and other windowed objects from
10147          * showing through.
10148          * @return {Roo.Element} The new shim element
10149          */
10150         createShim : function(){
10151             var el = document.createElement('iframe');
10152             el.frameBorder = 'no';
10153             el.className = 'roo-shim';
10154             if(Roo.isIE && Roo.isSecure){
10155                 el.src = Roo.SSL_SECURE_URL;
10156             }
10157             var shim = Roo.get(this.dom.parentNode.insertBefore(el, this.dom));
10158             shim.autoBoxAdjust = false;
10159             return shim;
10160         },
10161
10162         /**
10163          * Removes this element from the DOM and deletes it from the cache
10164          */
10165         remove : function(){
10166             if(this.dom.parentNode){
10167                 this.dom.parentNode.removeChild(this.dom);
10168             }
10169             delete El.cache[this.dom.id];
10170         },
10171
10172         /**
10173          * Sets up event handlers to add and remove a css class when the mouse is over this element
10174          * @param {String} className
10175          * @param {Boolean} preventFlicker (optional) If set to true, it prevents flickering by filtering
10176          * mouseout events for children elements
10177          * @return {Roo.Element} this
10178          */
10179         addClassOnOver : function(className, preventFlicker){
10180             this.on("mouseover", function(){
10181                 Roo.fly(this, '_internal').addClass(className);
10182             }, this.dom);
10183             var removeFn = function(e){
10184                 if(preventFlicker !== true || !e.within(this, true)){
10185                     Roo.fly(this, '_internal').removeClass(className);
10186                 }
10187             };
10188             this.on("mouseout", removeFn, this.dom);
10189             return this;
10190         },
10191
10192         /**
10193          * Sets up event handlers to add and remove a css class when this element has the focus
10194          * @param {String} className
10195          * @return {Roo.Element} this
10196          */
10197         addClassOnFocus : function(className){
10198             this.on("focus", function(){
10199                 Roo.fly(this, '_internal').addClass(className);
10200             }, this.dom);
10201             this.on("blur", function(){
10202                 Roo.fly(this, '_internal').removeClass(className);
10203             }, this.dom);
10204             return this;
10205         },
10206         /**
10207          * 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)
10208          * @param {String} className
10209          * @return {Roo.Element} this
10210          */
10211         addClassOnClick : function(className){
10212             var dom = this.dom;
10213             this.on("mousedown", function(){
10214                 Roo.fly(dom, '_internal').addClass(className);
10215                 var d = Roo.get(document);
10216                 var fn = function(){
10217                     Roo.fly(dom, '_internal').removeClass(className);
10218                     d.removeListener("mouseup", fn);
10219                 };
10220                 d.on("mouseup", fn);
10221             });
10222             return this;
10223         },
10224
10225         /**
10226          * Stops the specified event from bubbling and optionally prevents the default action
10227          * @param {String} eventName
10228          * @param {Boolean} preventDefault (optional) true to prevent the default action too
10229          * @return {Roo.Element} this
10230          */
10231         swallowEvent : function(eventName, preventDefault){
10232             var fn = function(e){
10233                 e.stopPropagation();
10234                 if(preventDefault){
10235                     e.preventDefault();
10236                 }
10237             };
10238             if(eventName instanceof Array){
10239                 for(var i = 0, len = eventName.length; i < len; i++){
10240                      this.on(eventName[i], fn);
10241                 }
10242                 return this;
10243             }
10244             this.on(eventName, fn);
10245             return this;
10246         },
10247
10248         /**
10249          * @private
10250          */
10251         fitToParentDelegate : Roo.emptyFn, // keep a reference to the fitToParent delegate
10252
10253         /**
10254          * Sizes this element to its parent element's dimensions performing
10255          * neccessary box adjustments.
10256          * @param {Boolean} monitorResize (optional) If true maintains the fit when the browser window is resized.
10257          * @param {String/HTMLElment/Element} targetParent (optional) The target parent, default to the parentNode.
10258          * @return {Roo.Element} this
10259          */
10260         fitToParent : function(monitorResize, targetParent) {
10261           Roo.EventManager.removeResizeListener(this.fitToParentDelegate); // always remove previous fitToParent delegate from onWindowResize
10262           this.fitToParentDelegate = Roo.emptyFn; // remove reference to previous delegate
10263           if (monitorResize === true && !this.dom.parentNode) { // check if this Element still exists
10264             return this;
10265           }
10266           var p = Roo.get(targetParent || this.dom.parentNode);
10267           this.setSize(p.getComputedWidth() - p.getFrameWidth('lr'), p.getComputedHeight() - p.getFrameWidth('tb'));
10268           if (monitorResize === true) {
10269             this.fitToParentDelegate = this.fitToParent.createDelegate(this, [true, targetParent]);
10270             Roo.EventManager.onWindowResize(this.fitToParentDelegate);
10271           }
10272           return this;
10273         },
10274
10275         /**
10276          * Gets the next sibling, skipping text nodes
10277          * @return {HTMLElement} The next sibling or null
10278          */
10279         getNextSibling : function(){
10280             var n = this.dom.nextSibling;
10281             while(n && n.nodeType != 1){
10282                 n = n.nextSibling;
10283             }
10284             return n;
10285         },
10286
10287         /**
10288          * Gets the previous sibling, skipping text nodes
10289          * @return {HTMLElement} The previous sibling or null
10290          */
10291         getPrevSibling : function(){
10292             var n = this.dom.previousSibling;
10293             while(n && n.nodeType != 1){
10294                 n = n.previousSibling;
10295             }
10296             return n;
10297         },
10298
10299
10300         /**
10301          * Appends the passed element(s) to this element
10302          * @param {String/HTMLElement/Array/Element/CompositeElement} el
10303          * @return {Roo.Element} this
10304          */
10305         appendChild: function(el){
10306             el = Roo.get(el);
10307             el.appendTo(this);
10308             return this;
10309         },
10310
10311         /**
10312          * Creates the passed DomHelper config and appends it to this element or optionally inserts it before the passed child element.
10313          * @param {Object} config DomHelper element config object.  If no tag is specified (e.g., {tag:'input'}) then a div will be
10314          * automatically generated with the specified attributes.
10315          * @param {HTMLElement} insertBefore (optional) a child element of this element
10316          * @param {Boolean} returnDom (optional) true to return the dom node instead of creating an Element
10317          * @return {Roo.Element} The new child element
10318          */
10319         createChild: function(config, insertBefore, returnDom){
10320             config = config || {tag:'div'};
10321             if(insertBefore){
10322                 return Roo.DomHelper.insertBefore(insertBefore, config, returnDom !== true);
10323             }
10324             return Roo.DomHelper[!this.dom.firstChild ? 'overwrite' : 'append'](this.dom, config,  returnDom !== true);
10325         },
10326
10327         /**
10328          * Appends this element to the passed element
10329          * @param {String/HTMLElement/Element} el The new parent element
10330          * @return {Roo.Element} this
10331          */
10332         appendTo: function(el){
10333             el = Roo.getDom(el);
10334             el.appendChild(this.dom);
10335             return this;
10336         },
10337
10338         /**
10339          * Inserts this element before the passed element in the DOM
10340          * @param {String/HTMLElement/Element} el The element to insert before
10341          * @return {Roo.Element} this
10342          */
10343         insertBefore: function(el){
10344             el = Roo.getDom(el);
10345             el.parentNode.insertBefore(this.dom, el);
10346             return this;
10347         },
10348
10349         /**
10350          * Inserts this element after the passed element in the DOM
10351          * @param {String/HTMLElement/Element} el The element to insert after
10352          * @return {Roo.Element} this
10353          */
10354         insertAfter: function(el){
10355             el = Roo.getDom(el);
10356             el.parentNode.insertBefore(this.dom, el.nextSibling);
10357             return this;
10358         },
10359
10360         /**
10361          * Inserts (or creates) an element (or DomHelper config) as the first child of the this element
10362          * @param {String/HTMLElement/Element/Object} el The id or element to insert or a DomHelper config to create and insert
10363          * @return {Roo.Element} The new child
10364          */
10365         insertFirst: function(el, returnDom){
10366             el = el || {};
10367             if(typeof el == 'object' && !el.nodeType){ // dh config
10368                 return this.createChild(el, this.dom.firstChild, returnDom);
10369             }else{
10370                 el = Roo.getDom(el);
10371                 this.dom.insertBefore(el, this.dom.firstChild);
10372                 return !returnDom ? Roo.get(el) : el;
10373             }
10374         },
10375
10376         /**
10377          * Inserts (or creates) the passed element (or DomHelper config) as a sibling of this element
10378          * @param {String/HTMLElement/Element/Object} el The id or element to insert or a DomHelper config to create and insert
10379          * @param {String} where (optional) 'before' or 'after' defaults to before
10380          * @param {Boolean} returnDom (optional) True to return the raw DOM element instead of Roo.Element
10381          * @return {Roo.Element} the inserted Element
10382          */
10383         insertSibling: function(el, where, returnDom){
10384             where = where ? where.toLowerCase() : 'before';
10385             el = el || {};
10386             var rt, refNode = where == 'before' ? this.dom : this.dom.nextSibling;
10387
10388             if(typeof el == 'object' && !el.nodeType){ // dh config
10389                 if(where == 'after' && !this.dom.nextSibling){
10390                     rt = Roo.DomHelper.append(this.dom.parentNode, el, !returnDom);
10391                 }else{
10392                     rt = Roo.DomHelper[where == 'after' ? 'insertAfter' : 'insertBefore'](this.dom, el, !returnDom);
10393                 }
10394
10395             }else{
10396                 rt = this.dom.parentNode.insertBefore(Roo.getDom(el),
10397                             where == 'before' ? this.dom : this.dom.nextSibling);
10398                 if(!returnDom){
10399                     rt = Roo.get(rt);
10400                 }
10401             }
10402             return rt;
10403         },
10404
10405         /**
10406          * Creates and wraps this element with another element
10407          * @param {Object} config (optional) DomHelper element config object for the wrapper element or null for an empty div
10408          * @param {Boolean} returnDom (optional) True to return the raw DOM element instead of Roo.Element
10409          * @return {HTMLElement/Element} The newly created wrapper element
10410          */
10411         wrap: function(config, returnDom){
10412             if(!config){
10413                 config = {tag: "div"};
10414             }
10415             var newEl = Roo.DomHelper.insertBefore(this.dom, config, !returnDom);
10416             newEl.dom ? newEl.dom.appendChild(this.dom) : newEl.appendChild(this.dom);
10417             return newEl;
10418         },
10419
10420         /**
10421          * Replaces the passed element with this element
10422          * @param {String/HTMLElement/Element} el The element to replace
10423          * @return {Roo.Element} this
10424          */
10425         replace: function(el){
10426             el = Roo.get(el);
10427             this.insertBefore(el);
10428             el.remove();
10429             return this;
10430         },
10431
10432         /**
10433          * Inserts an html fragment into this element
10434          * @param {String} where Where to insert the html in relation to the this element - beforeBegin, afterBegin, beforeEnd, afterEnd.
10435          * @param {String} html The HTML fragment
10436          * @param {Boolean} returnEl True to return an Roo.Element
10437          * @return {HTMLElement/Roo.Element} The inserted node (or nearest related if more than 1 inserted)
10438          */
10439         insertHtml : function(where, html, returnEl){
10440             var el = Roo.DomHelper.insertHtml(where, this.dom, html);
10441             return returnEl ? Roo.get(el) : el;
10442         },
10443
10444         /**
10445          * Sets the passed attributes as attributes of this element (a style attribute can be a string, object or function)
10446          * @param {Object} o The object with the attributes
10447          * @param {Boolean} useSet (optional) false to override the default setAttribute to use expandos.
10448          * @return {Roo.Element} this
10449          */
10450         set : function(o, useSet){
10451             var el = this.dom;
10452             useSet = typeof useSet == 'undefined' ? (el.setAttribute ? true : false) : useSet;
10453             for(var attr in o){
10454                 if(attr == "style" || typeof o[attr] == "function")  { continue; }
10455                 if(attr=="cls"){
10456                     el.className = o["cls"];
10457                 }else{
10458                     if(useSet) {
10459                         el.setAttribute(attr, o[attr]);
10460                     } else {
10461                         el[attr] = o[attr];
10462                     }
10463                 }
10464             }
10465             if(o.style){
10466                 Roo.DomHelper.applyStyles(el, o.style);
10467             }
10468             return this;
10469         },
10470
10471         /**
10472          * Convenience method for constructing a KeyMap
10473          * @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:
10474          *                                  {key: (number or array), shift: (true/false), ctrl: (true/false), alt: (true/false)}
10475          * @param {Function} fn The function to call
10476          * @param {Object} scope (optional) The scope of the function
10477          * @return {Roo.KeyMap} The KeyMap created
10478          */
10479         addKeyListener : function(key, fn, scope){
10480             var config;
10481             if(typeof key != "object" || key instanceof Array){
10482                 config = {
10483                     key: key,
10484                     fn: fn,
10485                     scope: scope
10486                 };
10487             }else{
10488                 config = {
10489                     key : key.key,
10490                     shift : key.shift,
10491                     ctrl : key.ctrl,
10492                     alt : key.alt,
10493                     fn: fn,
10494                     scope: scope
10495                 };
10496             }
10497             return new Roo.KeyMap(this, config);
10498         },
10499
10500         /**
10501          * Creates a KeyMap for this element
10502          * @param {Object} config The KeyMap config. See {@link Roo.KeyMap} for more details
10503          * @return {Roo.KeyMap} The KeyMap created
10504          */
10505         addKeyMap : function(config){
10506             return new Roo.KeyMap(this, config);
10507         },
10508
10509         /**
10510          * Returns true if this element is scrollable.
10511          * @return {Boolean}
10512          */
10513          isScrollable : function(){
10514             var dom = this.dom;
10515             return dom.scrollHeight > dom.clientHeight || dom.scrollWidth > dom.clientWidth;
10516         },
10517
10518         /**
10519          * 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().
10520          * @param {String} side Either "left" for scrollLeft values or "top" for scrollTop values.
10521          * @param {Number} value The new scroll value
10522          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
10523          * @return {Element} this
10524          */
10525
10526         scrollTo : function(side, value, animate){
10527             var prop = side.toLowerCase() == "left" ? "scrollLeft" : "scrollTop";
10528             if(!animate || !A){
10529                 this.dom[prop] = value;
10530             }else{
10531                 var to = prop == "scrollLeft" ? [value, this.dom.scrollTop] : [this.dom.scrollLeft, value];
10532                 this.anim({scroll: {"to": to}}, this.preanim(arguments, 2), 'scroll');
10533             }
10534             return this;
10535         },
10536
10537         /**
10538          * Scrolls this element the specified direction. Does bounds checking to make sure the scroll is
10539          * within this element's scrollable range.
10540          * @param {String} direction Possible values are: "l","left" - "r","right" - "t","top","up" - "b","bottom","down".
10541          * @param {Number} distance How far to scroll the element in pixels
10542          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
10543          * @return {Boolean} Returns true if a scroll was triggered or false if the element
10544          * was scrolled as far as it could go.
10545          */
10546          scroll : function(direction, distance, animate){
10547              if(!this.isScrollable()){
10548                  return;
10549              }
10550              var el = this.dom;
10551              var l = el.scrollLeft, t = el.scrollTop;
10552              var w = el.scrollWidth, h = el.scrollHeight;
10553              var cw = el.clientWidth, ch = el.clientHeight;
10554              direction = direction.toLowerCase();
10555              var scrolled = false;
10556              var a = this.preanim(arguments, 2);
10557              switch(direction){
10558                  case "l":
10559                  case "left":
10560                      if(w - l > cw){
10561                          var v = Math.min(l + distance, w-cw);
10562                          this.scrollTo("left", v, a);
10563                          scrolled = true;
10564                      }
10565                      break;
10566                 case "r":
10567                 case "right":
10568                      if(l > 0){
10569                          var v = Math.max(l - distance, 0);
10570                          this.scrollTo("left", v, a);
10571                          scrolled = true;
10572                      }
10573                      break;
10574                 case "t":
10575                 case "top":
10576                 case "up":
10577                      if(t > 0){
10578                          var v = Math.max(t - distance, 0);
10579                          this.scrollTo("top", v, a);
10580                          scrolled = true;
10581                      }
10582                      break;
10583                 case "b":
10584                 case "bottom":
10585                 case "down":
10586                      if(h - t > ch){
10587                          var v = Math.min(t + distance, h-ch);
10588                          this.scrollTo("top", v, a);
10589                          scrolled = true;
10590                      }
10591                      break;
10592              }
10593              return scrolled;
10594         },
10595
10596         /**
10597          * Translates the passed page coordinates into left/top css values for this element
10598          * @param {Number/Array} x The page x or an array containing [x, y]
10599          * @param {Number} y The page y
10600          * @return {Object} An object with left and top properties. e.g. {left: (value), top: (value)}
10601          */
10602         translatePoints : function(x, y){
10603             if(typeof x == 'object' || x instanceof Array){
10604                 y = x[1]; x = x[0];
10605             }
10606             var p = this.getStyle('position');
10607             var o = this.getXY();
10608
10609             var l = parseInt(this.getStyle('left'), 10);
10610             var t = parseInt(this.getStyle('top'), 10);
10611
10612             if(isNaN(l)){
10613                 l = (p == "relative") ? 0 : this.dom.offsetLeft;
10614             }
10615             if(isNaN(t)){
10616                 t = (p == "relative") ? 0 : this.dom.offsetTop;
10617             }
10618
10619             return {left: (x - o[0] + l), top: (y - o[1] + t)};
10620         },
10621
10622         /**
10623          * Returns the current scroll position of the element.
10624          * @return {Object} An object containing the scroll position in the format {left: (scrollLeft), top: (scrollTop)}
10625          */
10626         getScroll : function(){
10627             var d = this.dom, doc = document;
10628             if(d == doc || d == doc.body){
10629                 var l = window.pageXOffset || doc.documentElement.scrollLeft || doc.body.scrollLeft || 0;
10630                 var t = window.pageYOffset || doc.documentElement.scrollTop || doc.body.scrollTop || 0;
10631                 return {left: l, top: t};
10632             }else{
10633                 return {left: d.scrollLeft, top: d.scrollTop};
10634             }
10635         },
10636
10637         /**
10638          * Return the CSS color for the specified CSS attribute. rgb, 3 digit (like #fff) and valid values
10639          * are convert to standard 6 digit hex color.
10640          * @param {String} attr The css attribute
10641          * @param {String} defaultValue The default value to use when a valid color isn't found
10642          * @param {String} prefix (optional) defaults to #. Use an empty string when working with
10643          * YUI color anims.
10644          */
10645         getColor : function(attr, defaultValue, prefix){
10646             var v = this.getStyle(attr);
10647             if(!v || v == "transparent" || v == "inherit") {
10648                 return defaultValue;
10649             }
10650             var color = typeof prefix == "undefined" ? "#" : prefix;
10651             if(v.substr(0, 4) == "rgb("){
10652                 var rvs = v.slice(4, v.length -1).split(",");
10653                 for(var i = 0; i < 3; i++){
10654                     var h = parseInt(rvs[i]).toString(16);
10655                     if(h < 16){
10656                         h = "0" + h;
10657                     }
10658                     color += h;
10659                 }
10660             } else {
10661                 if(v.substr(0, 1) == "#"){
10662                     if(v.length == 4) {
10663                         for(var i = 1; i < 4; i++){
10664                             var c = v.charAt(i);
10665                             color +=  c + c;
10666                         }
10667                     }else if(v.length == 7){
10668                         color += v.substr(1);
10669                     }
10670                 }
10671             }
10672             return(color.length > 5 ? color.toLowerCase() : defaultValue);
10673         },
10674
10675         /**
10676          * Wraps the specified element with a special markup/CSS block that renders by default as a gray container with a
10677          * gradient background, rounded corners and a 4-way shadow.
10678          * @param {String} class (optional) A base CSS class to apply to the containing wrapper element (defaults to 'x-box').
10679          * Note that there are a number of CSS rules that are dependent on this name to make the overall effect work,
10680          * so if you supply an alternate base class, make sure you also supply all of the necessary rules.
10681          * @return {Roo.Element} this
10682          */
10683         boxWrap : function(cls){
10684             cls = cls || 'x-box';
10685             var el = Roo.get(this.insertHtml('beforeBegin', String.format('<div class="{0}">'+El.boxMarkup+'</div>', cls)));
10686             el.child('.'+cls+'-mc').dom.appendChild(this.dom);
10687             return el;
10688         },
10689
10690         /**
10691          * Returns the value of a namespaced attribute from the element's underlying DOM node.
10692          * @param {String} namespace The namespace in which to look for the attribute
10693          * @param {String} name The attribute name
10694          * @return {String} The attribute value
10695          */
10696         getAttributeNS : Roo.isIE ? function(ns, name){
10697             var d = this.dom;
10698             var type = typeof d[ns+":"+name];
10699             if(type != 'undefined' && type != 'unknown'){
10700                 return d[ns+":"+name];
10701             }
10702             return d[name];
10703         } : function(ns, name){
10704             var d = this.dom;
10705             return d.getAttributeNS(ns, name) || d.getAttribute(ns+":"+name) || d.getAttribute(name) || d[name];
10706         },
10707         
10708         
10709         /**
10710          * Sets or Returns the value the dom attribute value
10711          * @param {String|Object} name The attribute name (or object to set multiple attributes)
10712          * @param {String} value (optional) The value to set the attribute to
10713          * @return {String} The attribute value
10714          */
10715         attr : function(name){
10716             if (arguments.length > 1) {
10717                 this.dom.setAttribute(name, arguments[1]);
10718                 return arguments[1];
10719             }
10720             if (typeof(name) == 'object') {
10721                 for(var i in name) {
10722                     this.attr(i, name[i]);
10723                 }
10724                 return name;
10725             }
10726             
10727             
10728             if (!this.dom.hasAttribute(name)) {
10729                 return undefined;
10730             }
10731             return this.dom.getAttribute(name);
10732         }
10733         
10734         
10735         
10736     };
10737
10738     var ep = El.prototype;
10739
10740     /**
10741      * Appends an event handler (Shorthand for addListener)
10742      * @param {String}   eventName     The type of event to append
10743      * @param {Function} fn        The method the event invokes
10744      * @param {Object} scope       (optional) The scope (this object) of the fn
10745      * @param {Object}   options   (optional)An object with standard {@link Roo.EventManager#addListener} options
10746      * @method
10747      */
10748     ep.on = ep.addListener;
10749         // backwards compat
10750     ep.mon = ep.addListener;
10751
10752     /**
10753      * Removes an event handler from this element (shorthand for removeListener)
10754      * @param {String} eventName the type of event to remove
10755      * @param {Function} fn the method the event invokes
10756      * @return {Roo.Element} this
10757      * @method
10758      */
10759     ep.un = ep.removeListener;
10760
10761     /**
10762      * true to automatically adjust width and height settings for box-model issues (default to true)
10763      */
10764     ep.autoBoxAdjust = true;
10765
10766     // private
10767     El.unitPattern = /\d+(px|em|%|en|ex|pt|in|cm|mm|pc)$/i;
10768
10769     // private
10770     El.addUnits = function(v, defaultUnit){
10771         if(v === "" || v == "auto"){
10772             return v;
10773         }
10774         if(v === undefined){
10775             return '';
10776         }
10777         if(typeof v == "number" || !El.unitPattern.test(v)){
10778             return v + (defaultUnit || 'px');
10779         }
10780         return v;
10781     };
10782
10783     // special markup used throughout Roo when box wrapping elements
10784     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>';
10785     /**
10786      * Visibility mode constant - Use visibility to hide element
10787      * @static
10788      * @type Number
10789      */
10790     El.VISIBILITY = 1;
10791     /**
10792      * Visibility mode constant - Use display to hide element
10793      * @static
10794      * @type Number
10795      */
10796     El.DISPLAY = 2;
10797
10798     El.borders = {l: "border-left-width", r: "border-right-width", t: "border-top-width", b: "border-bottom-width"};
10799     El.paddings = {l: "padding-left", r: "padding-right", t: "padding-top", b: "padding-bottom"};
10800     El.margins = {l: "margin-left", r: "margin-right", t: "margin-top", b: "margin-bottom"};
10801
10802
10803
10804     /**
10805      * @private
10806      */
10807     El.cache = {};
10808
10809     var docEl;
10810
10811     /**
10812      * Static method to retrieve Element objects. Uses simple caching to consistently return the same object.
10813      * Automatically fixes if an object was recreated with the same id via AJAX or DOM.
10814      * @param {String/HTMLElement/Element} el The id of the node, a DOM Node or an existing Element.
10815      * @return {Element} The Element object
10816      * @static
10817      */
10818     El.get = function(el){
10819         var ex, elm, id;
10820         if(!el){ return null; }
10821         if(typeof el == "string"){ // element id
10822             if(!(elm = document.getElementById(el))){
10823                 return null;
10824             }
10825             if(ex = El.cache[el]){
10826                 ex.dom = elm;
10827             }else{
10828                 ex = El.cache[el] = new El(elm);
10829             }
10830             return ex;
10831         }else if(el.tagName){ // dom element
10832             if(!(id = el.id)){
10833                 id = Roo.id(el);
10834             }
10835             if(ex = El.cache[id]){
10836                 ex.dom = el;
10837             }else{
10838                 ex = El.cache[id] = new El(el);
10839             }
10840             return ex;
10841         }else if(el instanceof El){
10842             if(el != docEl){
10843                 el.dom = document.getElementById(el.id) || el.dom; // refresh dom element in case no longer valid,
10844                                                               // catch case where it hasn't been appended
10845                 El.cache[el.id] = el; // in case it was created directly with Element(), let's cache it
10846             }
10847             return el;
10848         }else if(el.isComposite){
10849             return el;
10850         }else if(el instanceof Array){
10851             return El.select(el);
10852         }else if(el == document){
10853             // create a bogus element object representing the document object
10854             if(!docEl){
10855                 var f = function(){};
10856                 f.prototype = El.prototype;
10857                 docEl = new f();
10858                 docEl.dom = document;
10859             }
10860             return docEl;
10861         }
10862         return null;
10863     };
10864
10865     // private
10866     El.uncache = function(el){
10867         for(var i = 0, a = arguments, len = a.length; i < len; i++) {
10868             if(a[i]){
10869                 delete El.cache[a[i].id || a[i]];
10870             }
10871         }
10872     };
10873
10874     // private
10875     // Garbage collection - uncache elements/purge listeners on orphaned elements
10876     // so we don't hold a reference and cause the browser to retain them
10877     El.garbageCollect = function(){
10878         if(!Roo.enableGarbageCollector){
10879             clearInterval(El.collectorThread);
10880             return;
10881         }
10882         for(var eid in El.cache){
10883             var el = El.cache[eid], d = el.dom;
10884             // -------------------------------------------------------
10885             // Determining what is garbage:
10886             // -------------------------------------------------------
10887             // !d
10888             // dom node is null, definitely garbage
10889             // -------------------------------------------------------
10890             // !d.parentNode
10891             // no parentNode == direct orphan, definitely garbage
10892             // -------------------------------------------------------
10893             // !d.offsetParent && !document.getElementById(eid)
10894             // display none elements have no offsetParent so we will
10895             // also try to look it up by it's id. However, check
10896             // offsetParent first so we don't do unneeded lookups.
10897             // This enables collection of elements that are not orphans
10898             // directly, but somewhere up the line they have an orphan
10899             // parent.
10900             // -------------------------------------------------------
10901             if(!d || !d.parentNode || (!d.offsetParent && !document.getElementById(eid))){
10902                 delete El.cache[eid];
10903                 if(d && Roo.enableListenerCollection){
10904                     E.purgeElement(d);
10905                 }
10906             }
10907         }
10908     }
10909     El.collectorThreadId = setInterval(El.garbageCollect, 30000);
10910
10911
10912     // dom is optional
10913     El.Flyweight = function(dom){
10914         this.dom = dom;
10915     };
10916     El.Flyweight.prototype = El.prototype;
10917
10918     El._flyweights = {};
10919     /**
10920      * Gets the globally shared flyweight Element, with the passed node as the active element. Do not store a reference to this element -
10921      * the dom node can be overwritten by other code.
10922      * @param {String/HTMLElement} el The dom node or id
10923      * @param {String} named (optional) Allows for creation of named reusable flyweights to
10924      *                                  prevent conflicts (e.g. internally Roo uses "_internal")
10925      * @static
10926      * @return {Element} The shared Element object
10927      */
10928     El.fly = function(el, named){
10929         named = named || '_global';
10930         el = Roo.getDom(el);
10931         if(!el){
10932             return null;
10933         }
10934         if(!El._flyweights[named]){
10935             El._flyweights[named] = new El.Flyweight();
10936         }
10937         El._flyweights[named].dom = el;
10938         return El._flyweights[named];
10939     };
10940
10941     /**
10942      * Static method to retrieve Element objects. Uses simple caching to consistently return the same object.
10943      * Automatically fixes if an object was recreated with the same id via AJAX or DOM.
10944      * Shorthand of {@link Roo.Element#get}
10945      * @param {String/HTMLElement/Element} el The id of the node, a DOM Node or an existing Element.
10946      * @return {Element} The Element object
10947      * @member Roo
10948      * @method get
10949      */
10950     Roo.get = El.get;
10951     /**
10952      * Gets the globally shared flyweight Element, with the passed node as the active element. Do not store a reference to this element -
10953      * the dom node can be overwritten by other code.
10954      * Shorthand of {@link Roo.Element#fly}
10955      * @param {String/HTMLElement} el The dom node or id
10956      * @param {String} named (optional) Allows for creation of named reusable flyweights to
10957      *                                  prevent conflicts (e.g. internally Roo uses "_internal")
10958      * @static
10959      * @return {Element} The shared Element object
10960      * @member Roo
10961      * @method fly
10962      */
10963     Roo.fly = El.fly;
10964
10965     // speedy lookup for elements never to box adjust
10966     var noBoxAdjust = Roo.isStrict ? {
10967         select:1
10968     } : {
10969         input:1, select:1, textarea:1
10970     };
10971     if(Roo.isIE || Roo.isGecko){
10972         noBoxAdjust['button'] = 1;
10973     }
10974
10975
10976     Roo.EventManager.on(window, 'unload', function(){
10977         delete El.cache;
10978         delete El._flyweights;
10979     });
10980 })();
10981
10982
10983
10984
10985 if(Roo.DomQuery){
10986     Roo.Element.selectorFunction = Roo.DomQuery.select;
10987 }
10988
10989 Roo.Element.select = function(selector, unique, root){
10990     var els;
10991     if(typeof selector == "string"){
10992         els = Roo.Element.selectorFunction(selector, root);
10993     }else if(selector.length !== undefined){
10994         els = selector;
10995     }else{
10996         throw "Invalid selector";
10997     }
10998     if(unique === true){
10999         return new Roo.CompositeElement(els);
11000     }else{
11001         return new Roo.CompositeElementLite(els);
11002     }
11003 };
11004 /**
11005  * Selects elements based on the passed CSS selector to enable working on them as 1.
11006  * @param {String/Array} selector The CSS selector or an array of elements
11007  * @param {Boolean} unique (optional) true to create a unique Roo.Element for each element (defaults to a shared flyweight object)
11008  * @param {HTMLElement/String} root (optional) The root element of the query or id of the root
11009  * @return {CompositeElementLite/CompositeElement}
11010  * @member Roo
11011  * @method select
11012  */
11013 Roo.select = Roo.Element.select;
11014
11015
11016
11017
11018
11019
11020
11021
11022
11023
11024
11025
11026
11027
11028 /*
11029  * Based on:
11030  * Ext JS Library 1.1.1
11031  * Copyright(c) 2006-2007, Ext JS, LLC.
11032  *
11033  * Originally Released Under LGPL - original licence link has changed is not relivant.
11034  *
11035  * Fork - LGPL
11036  * <script type="text/javascript">
11037  */
11038
11039
11040
11041 //Notifies Element that fx methods are available
11042 Roo.enableFx = true;
11043
11044 /**
11045  * @class Roo.Fx
11046  * <p>A class to provide basic animation and visual effects support.  <b>Note:</b> This class is automatically applied
11047  * to the {@link Roo.Element} interface when included, so all effects calls should be performed via Element.
11048  * Conversely, since the effects are not actually defined in Element, Roo.Fx <b>must</b> be included in order for the 
11049  * Element effects to work.</p><br/>
11050  *
11051  * <p>It is important to note that although the Fx methods and many non-Fx Element methods support "method chaining" in that
11052  * they return the Element object itself as the method return value, it is not always possible to mix the two in a single
11053  * method chain.  The Fx methods use an internal effects queue so that each effect can be properly timed and sequenced.
11054  * Non-Fx methods, on the other hand, have no such internal queueing and will always execute immediately.  For this reason,
11055  * while it may be possible to mix certain Fx and non-Fx method calls in a single chain, it may not always provide the
11056  * expected results and should be done with care.</p><br/>
11057  *
11058  * <p>Motion effects support 8-way anchoring, meaning that you can choose one of 8 different anchor points on the Element
11059  * that will serve as either the start or end point of the animation.  Following are all of the supported anchor positions:</p>
11060 <pre>
11061 Value  Description
11062 -----  -----------------------------
11063 tl     The top left corner
11064 t      The center of the top edge
11065 tr     The top right corner
11066 l      The center of the left edge
11067 r      The center of the right edge
11068 bl     The bottom left corner
11069 b      The center of the bottom edge
11070 br     The bottom right corner
11071 </pre>
11072  * <b>Although some Fx methods accept specific custom config parameters, the ones shown in the Config Options section
11073  * below are common options that can be passed to any Fx method.</b>
11074  * @cfg {Function} callback A function called when the effect is finished
11075  * @cfg {Object} scope The scope of the effect function
11076  * @cfg {String} easing A valid Easing value for the effect
11077  * @cfg {String} afterCls A css class to apply after the effect
11078  * @cfg {Number} duration The length of time (in seconds) that the effect should last
11079  * @cfg {Boolean} remove Whether the Element should be removed from the DOM and destroyed after the effect finishes
11080  * @cfg {Boolean} useDisplay Whether to use the <i>display</i> CSS property instead of <i>visibility</i> when hiding Elements (only applies to 
11081  * effects that end with the element being visually hidden, ignored otherwise)
11082  * @cfg {String/Object/Function} afterStyle A style specification string, e.g. "width:100px", or an object in the form {width:"100px"}, or
11083  * a function which returns such a specification that will be applied to the Element after the effect finishes
11084  * @cfg {Boolean} block Whether the effect should block other effects from queueing while it runs
11085  * @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
11086  * @cfg {Boolean} stopFx Whether subsequent effects should be stopped and removed after the current effect finishes
11087  */
11088 Roo.Fx = {
11089         /**
11090          * Slides the element into view.  An anchor point can be optionally passed to set the point of
11091          * origin for the slide effect.  This function automatically handles wrapping the element with
11092          * a fixed-size container if needed.  See the Fx class overview for valid anchor point options.
11093          * Usage:
11094          *<pre><code>
11095 // default: slide the element in from the top
11096 el.slideIn();
11097
11098 // custom: slide the element in from the right with a 2-second duration
11099 el.slideIn('r', { duration: 2 });
11100
11101 // common config options shown with default values
11102 el.slideIn('t', {
11103     easing: 'easeOut',
11104     duration: .5
11105 });
11106 </code></pre>
11107          * @param {String} anchor (optional) One of the valid Fx anchor positions (defaults to top: 't')
11108          * @param {Object} options (optional) Object literal with any of the Fx config options
11109          * @return {Roo.Element} The Element
11110          */
11111     slideIn : function(anchor, o){
11112         var el = this.getFxEl();
11113         o = o || {};
11114
11115         el.queueFx(o, function(){
11116
11117             anchor = anchor || "t";
11118
11119             // fix display to visibility
11120             this.fixDisplay();
11121
11122             // restore values after effect
11123             var r = this.getFxRestore();
11124             var b = this.getBox();
11125             // fixed size for slide
11126             this.setSize(b);
11127
11128             // wrap if needed
11129             var wrap = this.fxWrap(r.pos, o, "hidden");
11130
11131             var st = this.dom.style;
11132             st.visibility = "visible";
11133             st.position = "absolute";
11134
11135             // clear out temp styles after slide and unwrap
11136             var after = function(){
11137                 el.fxUnwrap(wrap, r.pos, o);
11138                 st.width = r.width;
11139                 st.height = r.height;
11140                 el.afterFx(o);
11141             };
11142             // time to calc the positions
11143             var a, pt = {to: [b.x, b.y]}, bw = {to: b.width}, bh = {to: b.height};
11144
11145             switch(anchor.toLowerCase()){
11146                 case "t":
11147                     wrap.setSize(b.width, 0);
11148                     st.left = st.bottom = "0";
11149                     a = {height: bh};
11150                 break;
11151                 case "l":
11152                     wrap.setSize(0, b.height);
11153                     st.right = st.top = "0";
11154                     a = {width: bw};
11155                 break;
11156                 case "r":
11157                     wrap.setSize(0, b.height);
11158                     wrap.setX(b.right);
11159                     st.left = st.top = "0";
11160                     a = {width: bw, points: pt};
11161                 break;
11162                 case "b":
11163                     wrap.setSize(b.width, 0);
11164                     wrap.setY(b.bottom);
11165                     st.left = st.top = "0";
11166                     a = {height: bh, points: pt};
11167                 break;
11168                 case "tl":
11169                     wrap.setSize(0, 0);
11170                     st.right = st.bottom = "0";
11171                     a = {width: bw, height: bh};
11172                 break;
11173                 case "bl":
11174                     wrap.setSize(0, 0);
11175                     wrap.setY(b.y+b.height);
11176                     st.right = st.top = "0";
11177                     a = {width: bw, height: bh, points: pt};
11178                 break;
11179                 case "br":
11180                     wrap.setSize(0, 0);
11181                     wrap.setXY([b.right, b.bottom]);
11182                     st.left = st.top = "0";
11183                     a = {width: bw, height: bh, points: pt};
11184                 break;
11185                 case "tr":
11186                     wrap.setSize(0, 0);
11187                     wrap.setX(b.x+b.width);
11188                     st.left = st.bottom = "0";
11189                     a = {width: bw, height: bh, points: pt};
11190                 break;
11191             }
11192             this.dom.style.visibility = "visible";
11193             wrap.show();
11194
11195             arguments.callee.anim = wrap.fxanim(a,
11196                 o,
11197                 'motion',
11198                 .5,
11199                 'easeOut', after);
11200         });
11201         return this;
11202     },
11203     
11204         /**
11205          * Slides the element out of view.  An anchor point can be optionally passed to set the end point
11206          * for the slide effect.  When the effect is completed, the element will be hidden (visibility = 
11207          * 'hidden') but block elements will still take up space in the document.  The element must be removed
11208          * from the DOM using the 'remove' config option if desired.  This function automatically handles 
11209          * wrapping the element with a fixed-size container if needed.  See the Fx class overview for valid anchor point options.
11210          * Usage:
11211          *<pre><code>
11212 // default: slide the element out to the top
11213 el.slideOut();
11214
11215 // custom: slide the element out to the right with a 2-second duration
11216 el.slideOut('r', { duration: 2 });
11217
11218 // common config options shown with default values
11219 el.slideOut('t', {
11220     easing: 'easeOut',
11221     duration: .5,
11222     remove: false,
11223     useDisplay: false
11224 });
11225 </code></pre>
11226          * @param {String} anchor (optional) One of the valid Fx anchor positions (defaults to top: 't')
11227          * @param {Object} options (optional) Object literal with any of the Fx config options
11228          * @return {Roo.Element} The Element
11229          */
11230     slideOut : function(anchor, o){
11231         var el = this.getFxEl();
11232         o = o || {};
11233
11234         el.queueFx(o, function(){
11235
11236             anchor = anchor || "t";
11237
11238             // restore values after effect
11239             var r = this.getFxRestore();
11240             
11241             var b = this.getBox();
11242             // fixed size for slide
11243             this.setSize(b);
11244
11245             // wrap if needed
11246             var wrap = this.fxWrap(r.pos, o, "visible");
11247
11248             var st = this.dom.style;
11249             st.visibility = "visible";
11250             st.position = "absolute";
11251
11252             wrap.setSize(b);
11253
11254             var after = function(){
11255                 if(o.useDisplay){
11256                     el.setDisplayed(false);
11257                 }else{
11258                     el.hide();
11259                 }
11260
11261                 el.fxUnwrap(wrap, r.pos, o);
11262
11263                 st.width = r.width;
11264                 st.height = r.height;
11265
11266                 el.afterFx(o);
11267             };
11268
11269             var a, zero = {to: 0};
11270             switch(anchor.toLowerCase()){
11271                 case "t":
11272                     st.left = st.bottom = "0";
11273                     a = {height: zero};
11274                 break;
11275                 case "l":
11276                     st.right = st.top = "0";
11277                     a = {width: zero};
11278                 break;
11279                 case "r":
11280                     st.left = st.top = "0";
11281                     a = {width: zero, points: {to:[b.right, b.y]}};
11282                 break;
11283                 case "b":
11284                     st.left = st.top = "0";
11285                     a = {height: zero, points: {to:[b.x, b.bottom]}};
11286                 break;
11287                 case "tl":
11288                     st.right = st.bottom = "0";
11289                     a = {width: zero, height: zero};
11290                 break;
11291                 case "bl":
11292                     st.right = st.top = "0";
11293                     a = {width: zero, height: zero, points: {to:[b.x, b.bottom]}};
11294                 break;
11295                 case "br":
11296                     st.left = st.top = "0";
11297                     a = {width: zero, height: zero, points: {to:[b.x+b.width, b.bottom]}};
11298                 break;
11299                 case "tr":
11300                     st.left = st.bottom = "0";
11301                     a = {width: zero, height: zero, points: {to:[b.right, b.y]}};
11302                 break;
11303             }
11304
11305             arguments.callee.anim = wrap.fxanim(a,
11306                 o,
11307                 'motion',
11308                 .5,
11309                 "easeOut", after);
11310         });
11311         return this;
11312     },
11313
11314         /**
11315          * Fades the element out while slowly expanding it in all directions.  When the effect is completed, the 
11316          * element will be hidden (visibility = 'hidden') but block elements will still take up space in the document. 
11317          * The element must be removed from the DOM using the 'remove' config option if desired.
11318          * Usage:
11319          *<pre><code>
11320 // default
11321 el.puff();
11322
11323 // common config options shown with default values
11324 el.puff({
11325     easing: 'easeOut',
11326     duration: .5,
11327     remove: false,
11328     useDisplay: false
11329 });
11330 </code></pre>
11331          * @param {Object} options (optional) Object literal with any of the Fx config options
11332          * @return {Roo.Element} The Element
11333          */
11334     puff : function(o){
11335         var el = this.getFxEl();
11336         o = o || {};
11337
11338         el.queueFx(o, function(){
11339             this.clearOpacity();
11340             this.show();
11341
11342             // restore values after effect
11343             var r = this.getFxRestore();
11344             var st = this.dom.style;
11345
11346             var after = function(){
11347                 if(o.useDisplay){
11348                     el.setDisplayed(false);
11349                 }else{
11350                     el.hide();
11351                 }
11352
11353                 el.clearOpacity();
11354
11355                 el.setPositioning(r.pos);
11356                 st.width = r.width;
11357                 st.height = r.height;
11358                 st.fontSize = '';
11359                 el.afterFx(o);
11360             };
11361
11362             var width = this.getWidth();
11363             var height = this.getHeight();
11364
11365             arguments.callee.anim = this.fxanim({
11366                     width : {to: this.adjustWidth(width * 2)},
11367                     height : {to: this.adjustHeight(height * 2)},
11368                     points : {by: [-(width * .5), -(height * .5)]},
11369                     opacity : {to: 0},
11370                     fontSize: {to:200, unit: "%"}
11371                 },
11372                 o,
11373                 'motion',
11374                 .5,
11375                 "easeOut", after);
11376         });
11377         return this;
11378     },
11379
11380         /**
11381          * Blinks the element as if it was clicked and then collapses on its center (similar to switching off a television).
11382          * When the effect is completed, the element will be hidden (visibility = 'hidden') but block elements will still 
11383          * take up space in the document. The element must be removed from the DOM using the 'remove' config option if desired.
11384          * Usage:
11385          *<pre><code>
11386 // default
11387 el.switchOff();
11388
11389 // all config options shown with default values
11390 el.switchOff({
11391     easing: 'easeIn',
11392     duration: .3,
11393     remove: false,
11394     useDisplay: false
11395 });
11396 </code></pre>
11397          * @param {Object} options (optional) Object literal with any of the Fx config options
11398          * @return {Roo.Element} The Element
11399          */
11400     switchOff : function(o){
11401         var el = this.getFxEl();
11402         o = o || {};
11403
11404         el.queueFx(o, function(){
11405             this.clearOpacity();
11406             this.clip();
11407
11408             // restore values after effect
11409             var r = this.getFxRestore();
11410             var st = this.dom.style;
11411
11412             var after = function(){
11413                 if(o.useDisplay){
11414                     el.setDisplayed(false);
11415                 }else{
11416                     el.hide();
11417                 }
11418
11419                 el.clearOpacity();
11420                 el.setPositioning(r.pos);
11421                 st.width = r.width;
11422                 st.height = r.height;
11423
11424                 el.afterFx(o);
11425             };
11426
11427             this.fxanim({opacity:{to:0.3}}, null, null, .1, null, function(){
11428                 this.clearOpacity();
11429                 (function(){
11430                     this.fxanim({
11431                         height:{to:1},
11432                         points:{by:[0, this.getHeight() * .5]}
11433                     }, o, 'motion', 0.3, 'easeIn', after);
11434                 }).defer(100, this);
11435             });
11436         });
11437         return this;
11438     },
11439
11440     /**
11441      * Highlights the Element by setting a color (applies to the background-color by default, but can be
11442      * changed using the "attr" config option) and then fading back to the original color. If no original
11443      * color is available, you should provide the "endColor" config option which will be cleared after the animation.
11444      * Usage:
11445 <pre><code>
11446 // default: highlight background to yellow
11447 el.highlight();
11448
11449 // custom: highlight foreground text to blue for 2 seconds
11450 el.highlight("0000ff", { attr: 'color', duration: 2 });
11451
11452 // common config options shown with default values
11453 el.highlight("ffff9c", {
11454     attr: "background-color", //can be any valid CSS property (attribute) that supports a color value
11455     endColor: (current color) or "ffffff",
11456     easing: 'easeIn',
11457     duration: 1
11458 });
11459 </code></pre>
11460      * @param {String} color (optional) The highlight color. Should be a 6 char hex color without the leading # (defaults to yellow: 'ffff9c')
11461      * @param {Object} options (optional) Object literal with any of the Fx config options
11462      * @return {Roo.Element} The Element
11463      */ 
11464     highlight : function(color, o){
11465         var el = this.getFxEl();
11466         o = o || {};
11467
11468         el.queueFx(o, function(){
11469             color = color || "ffff9c";
11470             attr = o.attr || "backgroundColor";
11471
11472             this.clearOpacity();
11473             this.show();
11474
11475             var origColor = this.getColor(attr);
11476             var restoreColor = this.dom.style[attr];
11477             endColor = (o.endColor || origColor) || "ffffff";
11478
11479             var after = function(){
11480                 el.dom.style[attr] = restoreColor;
11481                 el.afterFx(o);
11482             };
11483
11484             var a = {};
11485             a[attr] = {from: color, to: endColor};
11486             arguments.callee.anim = this.fxanim(a,
11487                 o,
11488                 'color',
11489                 1,
11490                 'easeIn', after);
11491         });
11492         return this;
11493     },
11494
11495    /**
11496     * Shows a ripple of exploding, attenuating borders to draw attention to an Element.
11497     * Usage:
11498 <pre><code>
11499 // default: a single light blue ripple
11500 el.frame();
11501
11502 // custom: 3 red ripples lasting 3 seconds total
11503 el.frame("ff0000", 3, { duration: 3 });
11504
11505 // common config options shown with default values
11506 el.frame("C3DAF9", 1, {
11507     duration: 1 //duration of entire animation (not each individual ripple)
11508     // Note: Easing is not configurable and will be ignored if included
11509 });
11510 </code></pre>
11511     * @param {String} color (optional) The color of the border.  Should be a 6 char hex color without the leading # (defaults to light blue: 'C3DAF9').
11512     * @param {Number} count (optional) The number of ripples to display (defaults to 1)
11513     * @param {Object} options (optional) Object literal with any of the Fx config options
11514     * @return {Roo.Element} The Element
11515     */
11516     frame : function(color, count, o){
11517         var el = this.getFxEl();
11518         o = o || {};
11519
11520         el.queueFx(o, function(){
11521             color = color || "#C3DAF9";
11522             if(color.length == 6){
11523                 color = "#" + color;
11524             }
11525             count = count || 1;
11526             duration = o.duration || 1;
11527             this.show();
11528
11529             var b = this.getBox();
11530             var animFn = function(){
11531                 var proxy = this.createProxy({
11532
11533                      style:{
11534                         visbility:"hidden",
11535                         position:"absolute",
11536                         "z-index":"35000", // yee haw
11537                         border:"0px solid " + color
11538                      }
11539                   });
11540                 var scale = Roo.isBorderBox ? 2 : 1;
11541                 proxy.animate({
11542                     top:{from:b.y, to:b.y - 20},
11543                     left:{from:b.x, to:b.x - 20},
11544                     borderWidth:{from:0, to:10},
11545                     opacity:{from:1, to:0},
11546                     height:{from:b.height, to:(b.height + (20*scale))},
11547                     width:{from:b.width, to:(b.width + (20*scale))}
11548                 }, duration, function(){
11549                     proxy.remove();
11550                 });
11551                 if(--count > 0){
11552                      animFn.defer((duration/2)*1000, this);
11553                 }else{
11554                     el.afterFx(o);
11555                 }
11556             };
11557             animFn.call(this);
11558         });
11559         return this;
11560     },
11561
11562    /**
11563     * Creates a pause before any subsequent queued effects begin.  If there are
11564     * no effects queued after the pause it will have no effect.
11565     * Usage:
11566 <pre><code>
11567 el.pause(1);
11568 </code></pre>
11569     * @param {Number} seconds The length of time to pause (in seconds)
11570     * @return {Roo.Element} The Element
11571     */
11572     pause : function(seconds){
11573         var el = this.getFxEl();
11574         var o = {};
11575
11576         el.queueFx(o, function(){
11577             setTimeout(function(){
11578                 el.afterFx(o);
11579             }, seconds * 1000);
11580         });
11581         return this;
11582     },
11583
11584    /**
11585     * Fade an element in (from transparent to opaque).  The ending opacity can be specified
11586     * using the "endOpacity" config option.
11587     * Usage:
11588 <pre><code>
11589 // default: fade in from opacity 0 to 100%
11590 el.fadeIn();
11591
11592 // custom: fade in from opacity 0 to 75% over 2 seconds
11593 el.fadeIn({ endOpacity: .75, duration: 2});
11594
11595 // common config options shown with default values
11596 el.fadeIn({
11597     endOpacity: 1, //can be any value between 0 and 1 (e.g. .5)
11598     easing: 'easeOut',
11599     duration: .5
11600 });
11601 </code></pre>
11602     * @param {Object} options (optional) Object literal with any of the Fx config options
11603     * @return {Roo.Element} The Element
11604     */
11605     fadeIn : function(o){
11606         var el = this.getFxEl();
11607         o = o || {};
11608         el.queueFx(o, function(){
11609             this.setOpacity(0);
11610             this.fixDisplay();
11611             this.dom.style.visibility = 'visible';
11612             var to = o.endOpacity || 1;
11613             arguments.callee.anim = this.fxanim({opacity:{to:to}},
11614                 o, null, .5, "easeOut", function(){
11615                 if(to == 1){
11616                     this.clearOpacity();
11617                 }
11618                 el.afterFx(o);
11619             });
11620         });
11621         return this;
11622     },
11623
11624    /**
11625     * Fade an element out (from opaque to transparent).  The ending opacity can be specified
11626     * using the "endOpacity" config option.
11627     * Usage:
11628 <pre><code>
11629 // default: fade out from the element's current opacity to 0
11630 el.fadeOut();
11631
11632 // custom: fade out from the element's current opacity to 25% over 2 seconds
11633 el.fadeOut({ endOpacity: .25, duration: 2});
11634
11635 // common config options shown with default values
11636 el.fadeOut({
11637     endOpacity: 0, //can be any value between 0 and 1 (e.g. .5)
11638     easing: 'easeOut',
11639     duration: .5
11640     remove: false,
11641     useDisplay: false
11642 });
11643 </code></pre>
11644     * @param {Object} options (optional) Object literal with any of the Fx config options
11645     * @return {Roo.Element} The Element
11646     */
11647     fadeOut : function(o){
11648         var el = this.getFxEl();
11649         o = o || {};
11650         el.queueFx(o, function(){
11651             arguments.callee.anim = this.fxanim({opacity:{to:o.endOpacity || 0}},
11652                 o, null, .5, "easeOut", function(){
11653                 if(this.visibilityMode == Roo.Element.DISPLAY || o.useDisplay){
11654                      this.dom.style.display = "none";
11655                 }else{
11656                      this.dom.style.visibility = "hidden";
11657                 }
11658                 this.clearOpacity();
11659                 el.afterFx(o);
11660             });
11661         });
11662         return this;
11663     },
11664
11665    /**
11666     * Animates the transition of an element's dimensions from a starting height/width
11667     * to an ending height/width.
11668     * Usage:
11669 <pre><code>
11670 // change height and width to 100x100 pixels
11671 el.scale(100, 100);
11672
11673 // common config options shown with default values.  The height and width will default to
11674 // the element's existing values if passed as null.
11675 el.scale(
11676     [element's width],
11677     [element's height], {
11678     easing: 'easeOut',
11679     duration: .35
11680 });
11681 </code></pre>
11682     * @param {Number} width  The new width (pass undefined to keep the original width)
11683     * @param {Number} height  The new height (pass undefined to keep the original height)
11684     * @param {Object} options (optional) Object literal with any of the Fx config options
11685     * @return {Roo.Element} The Element
11686     */
11687     scale : function(w, h, o){
11688         this.shift(Roo.apply({}, o, {
11689             width: w,
11690             height: h
11691         }));
11692         return this;
11693     },
11694
11695    /**
11696     * Animates the transition of any combination of an element's dimensions, xy position and/or opacity.
11697     * Any of these properties not specified in the config object will not be changed.  This effect 
11698     * requires that at least one new dimension, position or opacity setting must be passed in on
11699     * the config object in order for the function to have any effect.
11700     * Usage:
11701 <pre><code>
11702 // slide the element horizontally to x position 200 while changing the height and opacity
11703 el.shift({ x: 200, height: 50, opacity: .8 });
11704
11705 // common config options shown with default values.
11706 el.shift({
11707     width: [element's width],
11708     height: [element's height],
11709     x: [element's x position],
11710     y: [element's y position],
11711     opacity: [element's opacity],
11712     easing: 'easeOut',
11713     duration: .35
11714 });
11715 </code></pre>
11716     * @param {Object} options  Object literal with any of the Fx config options
11717     * @return {Roo.Element} The Element
11718     */
11719     shift : function(o){
11720         var el = this.getFxEl();
11721         o = o || {};
11722         el.queueFx(o, function(){
11723             var a = {}, w = o.width, h = o.height, x = o.x, y = o.y,  op = o.opacity;
11724             if(w !== undefined){
11725                 a.width = {to: this.adjustWidth(w)};
11726             }
11727             if(h !== undefined){
11728                 a.height = {to: this.adjustHeight(h)};
11729             }
11730             if(x !== undefined || y !== undefined){
11731                 a.points = {to: [
11732                     x !== undefined ? x : this.getX(),
11733                     y !== undefined ? y : this.getY()
11734                 ]};
11735             }
11736             if(op !== undefined){
11737                 a.opacity = {to: op};
11738             }
11739             if(o.xy !== undefined){
11740                 a.points = {to: o.xy};
11741             }
11742             arguments.callee.anim = this.fxanim(a,
11743                 o, 'motion', .35, "easeOut", function(){
11744                 el.afterFx(o);
11745             });
11746         });
11747         return this;
11748     },
11749
11750         /**
11751          * Slides the element while fading it out of view.  An anchor point can be optionally passed to set the 
11752          * ending point of the effect.
11753          * Usage:
11754          *<pre><code>
11755 // default: slide the element downward while fading out
11756 el.ghost();
11757
11758 // custom: slide the element out to the right with a 2-second duration
11759 el.ghost('r', { duration: 2 });
11760
11761 // common config options shown with default values
11762 el.ghost('b', {
11763     easing: 'easeOut',
11764     duration: .5
11765     remove: false,
11766     useDisplay: false
11767 });
11768 </code></pre>
11769          * @param {String} anchor (optional) One of the valid Fx anchor positions (defaults to bottom: 'b')
11770          * @param {Object} options (optional) Object literal with any of the Fx config options
11771          * @return {Roo.Element} The Element
11772          */
11773     ghost : function(anchor, o){
11774         var el = this.getFxEl();
11775         o = o || {};
11776
11777         el.queueFx(o, function(){
11778             anchor = anchor || "b";
11779
11780             // restore values after effect
11781             var r = this.getFxRestore();
11782             var w = this.getWidth(),
11783                 h = this.getHeight();
11784
11785             var st = this.dom.style;
11786
11787             var after = function(){
11788                 if(o.useDisplay){
11789                     el.setDisplayed(false);
11790                 }else{
11791                     el.hide();
11792                 }
11793
11794                 el.clearOpacity();
11795                 el.setPositioning(r.pos);
11796                 st.width = r.width;
11797                 st.height = r.height;
11798
11799                 el.afterFx(o);
11800             };
11801
11802             var a = {opacity: {to: 0}, points: {}}, pt = a.points;
11803             switch(anchor.toLowerCase()){
11804                 case "t":
11805                     pt.by = [0, -h];
11806                 break;
11807                 case "l":
11808                     pt.by = [-w, 0];
11809                 break;
11810                 case "r":
11811                     pt.by = [w, 0];
11812                 break;
11813                 case "b":
11814                     pt.by = [0, h];
11815                 break;
11816                 case "tl":
11817                     pt.by = [-w, -h];
11818                 break;
11819                 case "bl":
11820                     pt.by = [-w, h];
11821                 break;
11822                 case "br":
11823                     pt.by = [w, h];
11824                 break;
11825                 case "tr":
11826                     pt.by = [w, -h];
11827                 break;
11828             }
11829
11830             arguments.callee.anim = this.fxanim(a,
11831                 o,
11832                 'motion',
11833                 .5,
11834                 "easeOut", after);
11835         });
11836         return this;
11837     },
11838
11839         /**
11840          * Ensures that all effects queued after syncFx is called on the element are
11841          * run concurrently.  This is the opposite of {@link #sequenceFx}.
11842          * @return {Roo.Element} The Element
11843          */
11844     syncFx : function(){
11845         this.fxDefaults = Roo.apply(this.fxDefaults || {}, {
11846             block : false,
11847             concurrent : true,
11848             stopFx : false
11849         });
11850         return this;
11851     },
11852
11853         /**
11854          * Ensures that all effects queued after sequenceFx is called on the element are
11855          * run in sequence.  This is the opposite of {@link #syncFx}.
11856          * @return {Roo.Element} The Element
11857          */
11858     sequenceFx : function(){
11859         this.fxDefaults = Roo.apply(this.fxDefaults || {}, {
11860             block : false,
11861             concurrent : false,
11862             stopFx : false
11863         });
11864         return this;
11865     },
11866
11867         /* @private */
11868     nextFx : function(){
11869         var ef = this.fxQueue[0];
11870         if(ef){
11871             ef.call(this);
11872         }
11873     },
11874
11875         /**
11876          * Returns true if the element has any effects actively running or queued, else returns false.
11877          * @return {Boolean} True if element has active effects, else false
11878          */
11879     hasActiveFx : function(){
11880         return this.fxQueue && this.fxQueue[0];
11881     },
11882
11883         /**
11884          * Stops any running effects and clears the element's internal effects queue if it contains
11885          * any additional effects that haven't started yet.
11886          * @return {Roo.Element} The Element
11887          */
11888     stopFx : function(){
11889         if(this.hasActiveFx()){
11890             var cur = this.fxQueue[0];
11891             if(cur && cur.anim && cur.anim.isAnimated()){
11892                 this.fxQueue = [cur]; // clear out others
11893                 cur.anim.stop(true);
11894             }
11895         }
11896         return this;
11897     },
11898
11899         /* @private */
11900     beforeFx : function(o){
11901         if(this.hasActiveFx() && !o.concurrent){
11902            if(o.stopFx){
11903                this.stopFx();
11904                return true;
11905            }
11906            return false;
11907         }
11908         return true;
11909     },
11910
11911         /**
11912          * Returns true if the element is currently blocking so that no other effect can be queued
11913          * until this effect is finished, else returns false if blocking is not set.  This is commonly
11914          * used to ensure that an effect initiated by a user action runs to completion prior to the
11915          * same effect being restarted (e.g., firing only one effect even if the user clicks several times).
11916          * @return {Boolean} True if blocking, else false
11917          */
11918     hasFxBlock : function(){
11919         var q = this.fxQueue;
11920         return q && q[0] && q[0].block;
11921     },
11922
11923         /* @private */
11924     queueFx : function(o, fn){
11925         if(!this.fxQueue){
11926             this.fxQueue = [];
11927         }
11928         if(!this.hasFxBlock()){
11929             Roo.applyIf(o, this.fxDefaults);
11930             if(!o.concurrent){
11931                 var run = this.beforeFx(o);
11932                 fn.block = o.block;
11933                 this.fxQueue.push(fn);
11934                 if(run){
11935                     this.nextFx();
11936                 }
11937             }else{
11938                 fn.call(this);
11939             }
11940         }
11941         return this;
11942     },
11943
11944         /* @private */
11945     fxWrap : function(pos, o, vis){
11946         var wrap;
11947         if(!o.wrap || !(wrap = Roo.get(o.wrap))){
11948             var wrapXY;
11949             if(o.fixPosition){
11950                 wrapXY = this.getXY();
11951             }
11952             var div = document.createElement("div");
11953             div.style.visibility = vis;
11954             wrap = Roo.get(this.dom.parentNode.insertBefore(div, this.dom));
11955             wrap.setPositioning(pos);
11956             if(wrap.getStyle("position") == "static"){
11957                 wrap.position("relative");
11958             }
11959             this.clearPositioning('auto');
11960             wrap.clip();
11961             wrap.dom.appendChild(this.dom);
11962             if(wrapXY){
11963                 wrap.setXY(wrapXY);
11964             }
11965         }
11966         return wrap;
11967     },
11968
11969         /* @private */
11970     fxUnwrap : function(wrap, pos, o){
11971         this.clearPositioning();
11972         this.setPositioning(pos);
11973         if(!o.wrap){
11974             wrap.dom.parentNode.insertBefore(this.dom, wrap.dom);
11975             wrap.remove();
11976         }
11977     },
11978
11979         /* @private */
11980     getFxRestore : function(){
11981         var st = this.dom.style;
11982         return {pos: this.getPositioning(), width: st.width, height : st.height};
11983     },
11984
11985         /* @private */
11986     afterFx : function(o){
11987         if(o.afterStyle){
11988             this.applyStyles(o.afterStyle);
11989         }
11990         if(o.afterCls){
11991             this.addClass(o.afterCls);
11992         }
11993         if(o.remove === true){
11994             this.remove();
11995         }
11996         Roo.callback(o.callback, o.scope, [this]);
11997         if(!o.concurrent){
11998             this.fxQueue.shift();
11999             this.nextFx();
12000         }
12001     },
12002
12003         /* @private */
12004     getFxEl : function(){ // support for composite element fx
12005         return Roo.get(this.dom);
12006     },
12007
12008         /* @private */
12009     fxanim : function(args, opt, animType, defaultDur, defaultEase, cb){
12010         animType = animType || 'run';
12011         opt = opt || {};
12012         var anim = Roo.lib.Anim[animType](
12013             this.dom, args,
12014             (opt.duration || defaultDur) || .35,
12015             (opt.easing || defaultEase) || 'easeOut',
12016             function(){
12017                 Roo.callback(cb, this);
12018             },
12019             this
12020         );
12021         opt.anim = anim;
12022         return anim;
12023     }
12024 };
12025
12026 // backwords compat
12027 Roo.Fx.resize = Roo.Fx.scale;
12028
12029 //When included, Roo.Fx is automatically applied to Element so that all basic
12030 //effects are available directly via the Element API
12031 Roo.apply(Roo.Element.prototype, Roo.Fx);/*
12032  * Based on:
12033  * Ext JS Library 1.1.1
12034  * Copyright(c) 2006-2007, Ext JS, LLC.
12035  *
12036  * Originally Released Under LGPL - original licence link has changed is not relivant.
12037  *
12038  * Fork - LGPL
12039  * <script type="text/javascript">
12040  */
12041
12042
12043 /**
12044  * @class Roo.CompositeElement
12045  * Standard composite class. Creates a Roo.Element for every element in the collection.
12046  * <br><br>
12047  * <b>NOTE: Although they are not listed, this class supports all of the set/update methods of Roo.Element. All Roo.Element
12048  * actions will be performed on all the elements in this collection.</b>
12049  * <br><br>
12050  * All methods return <i>this</i> and can be chained.
12051  <pre><code>
12052  var els = Roo.select("#some-el div.some-class", true);
12053  // or select directly from an existing element
12054  var el = Roo.get('some-el');
12055  el.select('div.some-class', true);
12056
12057  els.setWidth(100); // all elements become 100 width
12058  els.hide(true); // all elements fade out and hide
12059  // or
12060  els.setWidth(100).hide(true);
12061  </code></pre>
12062  */
12063 Roo.CompositeElement = function(els){
12064     this.elements = [];
12065     this.addElements(els);
12066 };
12067 Roo.CompositeElement.prototype = {
12068     isComposite: true,
12069     addElements : function(els){
12070         if(!els) {
12071             return this;
12072         }
12073         if(typeof els == "string"){
12074             els = Roo.Element.selectorFunction(els);
12075         }
12076         var yels = this.elements;
12077         var index = yels.length-1;
12078         for(var i = 0, len = els.length; i < len; i++) {
12079                 yels[++index] = Roo.get(els[i]);
12080         }
12081         return this;
12082     },
12083
12084     /**
12085     * Clears this composite and adds the elements returned by the passed selector.
12086     * @param {String/Array} els A string CSS selector, an array of elements or an element
12087     * @return {CompositeElement} this
12088     */
12089     fill : function(els){
12090         this.elements = [];
12091         this.add(els);
12092         return this;
12093     },
12094
12095     /**
12096     * Filters this composite to only elements that match the passed selector.
12097     * @param {String} selector A string CSS selector
12098     * @param {Boolean} inverse return inverse filter (not matches)
12099     * @return {CompositeElement} this
12100     */
12101     filter : function(selector, inverse){
12102         var els = [];
12103         inverse = inverse || false;
12104         this.each(function(el){
12105             var match = inverse ? !el.is(selector) : el.is(selector);
12106             if(match){
12107                 els[els.length] = el.dom;
12108             }
12109         });
12110         this.fill(els);
12111         return this;
12112     },
12113
12114     invoke : function(fn, args){
12115         var els = this.elements;
12116         for(var i = 0, len = els.length; i < len; i++) {
12117                 Roo.Element.prototype[fn].apply(els[i], args);
12118         }
12119         return this;
12120     },
12121     /**
12122     * Adds elements to this composite.
12123     * @param {String/Array} els A string CSS selector, an array of elements or an element
12124     * @return {CompositeElement} this
12125     */
12126     add : function(els){
12127         if(typeof els == "string"){
12128             this.addElements(Roo.Element.selectorFunction(els));
12129         }else if(els.length !== undefined){
12130             this.addElements(els);
12131         }else{
12132             this.addElements([els]);
12133         }
12134         return this;
12135     },
12136     /**
12137     * Calls the passed function passing (el, this, index) for each element in this composite.
12138     * @param {Function} fn The function to call
12139     * @param {Object} scope (optional) The <i>this</i> object (defaults to the element)
12140     * @return {CompositeElement} this
12141     */
12142     each : function(fn, scope){
12143         var els = this.elements;
12144         for(var i = 0, len = els.length; i < len; i++){
12145             if(fn.call(scope || els[i], els[i], this, i) === false) {
12146                 break;
12147             }
12148         }
12149         return this;
12150     },
12151
12152     /**
12153      * Returns the Element object at the specified index
12154      * @param {Number} index
12155      * @return {Roo.Element}
12156      */
12157     item : function(index){
12158         return this.elements[index] || null;
12159     },
12160
12161     /**
12162      * Returns the first Element
12163      * @return {Roo.Element}
12164      */
12165     first : function(){
12166         return this.item(0);
12167     },
12168
12169     /**
12170      * Returns the last Element
12171      * @return {Roo.Element}
12172      */
12173     last : function(){
12174         return this.item(this.elements.length-1);
12175     },
12176
12177     /**
12178      * Returns the number of elements in this composite
12179      * @return Number
12180      */
12181     getCount : function(){
12182         return this.elements.length;
12183     },
12184
12185     /**
12186      * Returns true if this composite contains the passed element
12187      * @return Boolean
12188      */
12189     contains : function(el){
12190         return this.indexOf(el) !== -1;
12191     },
12192
12193     /**
12194      * Returns true if this composite contains the passed element
12195      * @return Boolean
12196      */
12197     indexOf : function(el){
12198         return this.elements.indexOf(Roo.get(el));
12199     },
12200
12201
12202     /**
12203     * Removes the specified element(s).
12204     * @param {Mixed} el The id of an element, the Element itself, the index of the element in this composite
12205     * or an array of any of those.
12206     * @param {Boolean} removeDom (optional) True to also remove the element from the document
12207     * @return {CompositeElement} this
12208     */
12209     removeElement : function(el, removeDom){
12210         if(el instanceof Array){
12211             for(var i = 0, len = el.length; i < len; i++){
12212                 this.removeElement(el[i]);
12213             }
12214             return this;
12215         }
12216         var index = typeof el == 'number' ? el : this.indexOf(el);
12217         if(index !== -1){
12218             if(removeDom){
12219                 var d = this.elements[index];
12220                 if(d.dom){
12221                     d.remove();
12222                 }else{
12223                     d.parentNode.removeChild(d);
12224                 }
12225             }
12226             this.elements.splice(index, 1);
12227         }
12228         return this;
12229     },
12230
12231     /**
12232     * Replaces the specified element with the passed element.
12233     * @param {String/HTMLElement/Element/Number} el The id of an element, the Element itself, the index of the element in this composite
12234     * to replace.
12235     * @param {String/HTMLElement/Element} replacement The id of an element or the Element itself.
12236     * @param {Boolean} domReplace (Optional) True to remove and replace the element in the document too.
12237     * @return {CompositeElement} this
12238     */
12239     replaceElement : function(el, replacement, domReplace){
12240         var index = typeof el == 'number' ? el : this.indexOf(el);
12241         if(index !== -1){
12242             if(domReplace){
12243                 this.elements[index].replaceWith(replacement);
12244             }else{
12245                 this.elements.splice(index, 1, Roo.get(replacement))
12246             }
12247         }
12248         return this;
12249     },
12250
12251     /**
12252      * Removes all elements.
12253      */
12254     clear : function(){
12255         this.elements = [];
12256     }
12257 };
12258 (function(){
12259     Roo.CompositeElement.createCall = function(proto, fnName){
12260         if(!proto[fnName]){
12261             proto[fnName] = function(){
12262                 return this.invoke(fnName, arguments);
12263             };
12264         }
12265     };
12266     for(var fnName in Roo.Element.prototype){
12267         if(typeof Roo.Element.prototype[fnName] == "function"){
12268             Roo.CompositeElement.createCall(Roo.CompositeElement.prototype, fnName);
12269         }
12270     };
12271 })();
12272 /*
12273  * Based on:
12274  * Ext JS Library 1.1.1
12275  * Copyright(c) 2006-2007, Ext JS, LLC.
12276  *
12277  * Originally Released Under LGPL - original licence link has changed is not relivant.
12278  *
12279  * Fork - LGPL
12280  * <script type="text/javascript">
12281  */
12282
12283 /**
12284  * @class Roo.CompositeElementLite
12285  * @extends Roo.CompositeElement
12286  * Flyweight composite class. Reuses the same Roo.Element for element operations.
12287  <pre><code>
12288  var els = Roo.select("#some-el div.some-class");
12289  // or select directly from an existing element
12290  var el = Roo.get('some-el');
12291  el.select('div.some-class');
12292
12293  els.setWidth(100); // all elements become 100 width
12294  els.hide(true); // all elements fade out and hide
12295  // or
12296  els.setWidth(100).hide(true);
12297  </code></pre><br><br>
12298  * <b>NOTE: Although they are not listed, this class supports all of the set/update methods of Roo.Element. All Roo.Element
12299  * actions will be performed on all the elements in this collection.</b>
12300  */
12301 Roo.CompositeElementLite = function(els){
12302     Roo.CompositeElementLite.superclass.constructor.call(this, els);
12303     this.el = new Roo.Element.Flyweight();
12304 };
12305 Roo.extend(Roo.CompositeElementLite, Roo.CompositeElement, {
12306     addElements : function(els){
12307         if(els){
12308             if(els instanceof Array){
12309                 this.elements = this.elements.concat(els);
12310             }else{
12311                 var yels = this.elements;
12312                 var index = yels.length-1;
12313                 for(var i = 0, len = els.length; i < len; i++) {
12314                     yels[++index] = els[i];
12315                 }
12316             }
12317         }
12318         return this;
12319     },
12320     invoke : function(fn, args){
12321         var els = this.elements;
12322         var el = this.el;
12323         for(var i = 0, len = els.length; i < len; i++) {
12324             el.dom = els[i];
12325                 Roo.Element.prototype[fn].apply(el, args);
12326         }
12327         return this;
12328     },
12329     /**
12330      * Returns a flyweight Element of the dom element object at the specified index
12331      * @param {Number} index
12332      * @return {Roo.Element}
12333      */
12334     item : function(index){
12335         if(!this.elements[index]){
12336             return null;
12337         }
12338         this.el.dom = this.elements[index];
12339         return this.el;
12340     },
12341
12342     // fixes scope with flyweight
12343     addListener : function(eventName, handler, scope, opt){
12344         var els = this.elements;
12345         for(var i = 0, len = els.length; i < len; i++) {
12346             Roo.EventManager.on(els[i], eventName, handler, scope || els[i], opt);
12347         }
12348         return this;
12349     },
12350
12351     /**
12352     * Calls the passed function passing (el, this, index) for each element in this composite. <b>The element
12353     * passed is the flyweight (shared) Roo.Element instance, so if you require a
12354     * a reference to the dom node, use el.dom.</b>
12355     * @param {Function} fn The function to call
12356     * @param {Object} scope (optional) The <i>this</i> object (defaults to the element)
12357     * @return {CompositeElement} this
12358     */
12359     each : function(fn, scope){
12360         var els = this.elements;
12361         var el = this.el;
12362         for(var i = 0, len = els.length; i < len; i++){
12363             el.dom = els[i];
12364                 if(fn.call(scope || el, el, this, i) === false){
12365                 break;
12366             }
12367         }
12368         return this;
12369     },
12370
12371     indexOf : function(el){
12372         return this.elements.indexOf(Roo.getDom(el));
12373     },
12374
12375     replaceElement : function(el, replacement, domReplace){
12376         var index = typeof el == 'number' ? el : this.indexOf(el);
12377         if(index !== -1){
12378             replacement = Roo.getDom(replacement);
12379             if(domReplace){
12380                 var d = this.elements[index];
12381                 d.parentNode.insertBefore(replacement, d);
12382                 d.parentNode.removeChild(d);
12383             }
12384             this.elements.splice(index, 1, replacement);
12385         }
12386         return this;
12387     }
12388 });
12389 Roo.CompositeElementLite.prototype.on = Roo.CompositeElementLite.prototype.addListener;
12390
12391 /*
12392  * Based on:
12393  * Ext JS Library 1.1.1
12394  * Copyright(c) 2006-2007, Ext JS, LLC.
12395  *
12396  * Originally Released Under LGPL - original licence link has changed is not relivant.
12397  *
12398  * Fork - LGPL
12399  * <script type="text/javascript">
12400  */
12401
12402  
12403
12404 /**
12405  * @class Roo.data.Connection
12406  * @extends Roo.util.Observable
12407  * The class encapsulates a connection to the page's originating domain, allowing requests to be made
12408  * either to a configured URL, or to a URL specified at request time. 
12409  * 
12410  * Requests made by this class are asynchronous, and will return immediately. No data from
12411  * the server will be available to the statement immediately following the {@link #request} call.
12412  * To process returned data, use a callback in the request options object, or an event listener.
12413  * 
12414  * Note: If you are doing a file upload, you will not get a normal response object sent back to
12415  * your callback or event handler.  Since the upload is handled via in IFRAME, there is no XMLHttpRequest.
12416  * The response object is created using the innerHTML of the IFRAME's document as the responseText
12417  * property and, if present, the IFRAME's XML document as the responseXML property.
12418  * 
12419  * This means that a valid XML or HTML document must be returned. If JSON data is required, it is suggested
12420  * that it be placed either inside a &lt;textarea> in an HTML document and retrieved from the responseText
12421  * using a regex, or inside a CDATA section in an XML document and retrieved from the responseXML using
12422  * standard DOM methods.
12423  * @constructor
12424  * @param {Object} config a configuration object.
12425  */
12426 Roo.data.Connection = function(config){
12427     Roo.apply(this, config);
12428     this.addEvents({
12429         /**
12430          * @event beforerequest
12431          * Fires before a network request is made to retrieve a data object.
12432          * @param {Connection} conn This Connection object.
12433          * @param {Object} options The options config object passed to the {@link #request} method.
12434          */
12435         "beforerequest" : true,
12436         /**
12437          * @event requestcomplete
12438          * Fires if the request was successfully completed.
12439          * @param {Connection} conn This Connection object.
12440          * @param {Object} response The XHR object containing the response data.
12441          * See {@link http://www.w3.org/TR/XMLHttpRequest/} for details.
12442          * @param {Object} options The options config object passed to the {@link #request} method.
12443          */
12444         "requestcomplete" : true,
12445         /**
12446          * @event requestexception
12447          * Fires if an error HTTP status was returned from the server.
12448          * See {@link http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html} for details of HTTP status codes.
12449          * @param {Connection} conn This Connection object.
12450          * @param {Object} response The XHR object containing the response data.
12451          * See {@link http://www.w3.org/TR/XMLHttpRequest/} for details.
12452          * @param {Object} options The options config object passed to the {@link #request} method.
12453          */
12454         "requestexception" : true
12455     });
12456     Roo.data.Connection.superclass.constructor.call(this);
12457 };
12458
12459 Roo.extend(Roo.data.Connection, Roo.util.Observable, {
12460     /**
12461      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
12462      */
12463     /**
12464      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
12465      * extra parameters to each request made by this object. (defaults to undefined)
12466      */
12467     /**
12468      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
12469      *  to each request made by this object. (defaults to undefined)
12470      */
12471     /**
12472      * @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)
12473      */
12474     /**
12475      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
12476      */
12477     timeout : 30000,
12478     /**
12479      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
12480      * @type Boolean
12481      */
12482     autoAbort:false,
12483
12484     /**
12485      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
12486      * @type Boolean
12487      */
12488     disableCaching: true,
12489
12490     /**
12491      * Sends an HTTP request to a remote server.
12492      * @param {Object} options An object which may contain the following properties:<ul>
12493      * <li><b>url</b> {String} (Optional) The URL to which to send the request. Defaults to configured URL</li>
12494      * <li><b>params</b> {Object/String/Function} (Optional) An object containing properties which are used as parameters to the
12495      * request, a url encoded string or a function to call to get either.</li>
12496      * <li><b>method</b> {String} (Optional) The HTTP method to use for the request. Defaults to the configured method, or
12497      * if no method was configured, "GET" if no parameters are being sent, and "POST" if parameters are being sent.</li>
12498      * <li><b>callback</b> {Function} (Optional) The function to be called upon receipt of the HTTP response.
12499      * The callback is called regardless of success or failure and is passed the following parameters:<ul>
12500      * <li>options {Object} The parameter to the request call.</li>
12501      * <li>success {Boolean} True if the request succeeded.</li>
12502      * <li>response {Object} The XMLHttpRequest object containing the response data.</li>
12503      * </ul></li>
12504      * <li><b>success</b> {Function} (Optional) The function to be called upon success of the request.
12505      * The callback is passed the following parameters:<ul>
12506      * <li>response {Object} The XMLHttpRequest object containing the response data.</li>
12507      * <li>options {Object} The parameter to the request call.</li>
12508      * </ul></li>
12509      * <li><b>failure</b> {Function} (Optional) The function to be called upon failure of the request.
12510      * The callback is passed the following parameters:<ul>
12511      * <li>response {Object} The XMLHttpRequest object containing the response data.</li>
12512      * <li>options {Object} The parameter to the request call.</li>
12513      * </ul></li>
12514      * <li><b>scope</b> {Object} (Optional) The scope in which to execute the callbacks: The "this" object
12515      * for the callback function. Defaults to the browser window.</li>
12516      * <li><b>form</b> {Object/String} (Optional) A form object or id to pull parameters from.</li>
12517      * <li><b>isUpload</b> {Boolean} (Optional) True if the form object is a file upload (will usually be automatically detected).</li>
12518      * <li><b>headers</b> {Object} (Optional) Request headers to set for the request.</li>
12519      * <li><b>xmlData</b> {Object} (Optional) XML document to use for the post. Note: This will be used instead of
12520      * params for the post data. Any params will be appended to the URL.</li>
12521      * <li><b>disableCaching</b> {Boolean} (Optional) True to add a unique cache-buster param to GET requests.</li>
12522      * </ul>
12523      * @return {Number} transactionId
12524      */
12525     request : function(o){
12526         if(this.fireEvent("beforerequest", this, o) !== false){
12527             var p = o.params;
12528
12529             if(typeof p == "function"){
12530                 p = p.call(o.scope||window, o);
12531             }
12532             if(typeof p == "object"){
12533                 p = Roo.urlEncode(o.params);
12534             }
12535             if(this.extraParams){
12536                 var extras = Roo.urlEncode(this.extraParams);
12537                 p = p ? (p + '&' + extras) : extras;
12538             }
12539
12540             var url = o.url || this.url;
12541             if(typeof url == 'function'){
12542                 url = url.call(o.scope||window, o);
12543             }
12544
12545             if(o.form){
12546                 var form = Roo.getDom(o.form);
12547                 url = url || form.action;
12548
12549                 var enctype = form.getAttribute("enctype");
12550                 
12551                 if (o.formData) {
12552                     return this.doFormDataUpload(o, url);
12553                 }
12554                 
12555                 if(o.isUpload || (enctype && enctype.toLowerCase() == 'multipart/form-data')){
12556                     return this.doFormUpload(o, p, url);
12557                 }
12558                 var f = Roo.lib.Ajax.serializeForm(form);
12559                 p = p ? (p + '&' + f) : f;
12560             }
12561             
12562             if (!o.form && o.formData) {
12563                 o.formData = o.formData === true ? new FormData() : o.formData;
12564                 for (var k in o.params) {
12565                     o.formData.append(k,o.params[k]);
12566                 }
12567                     
12568                 return this.doFormDataUpload(o, url);
12569             }
12570             
12571
12572             var hs = o.headers;
12573             if(this.defaultHeaders){
12574                 hs = Roo.apply(hs || {}, this.defaultHeaders);
12575                 if(!o.headers){
12576                     o.headers = hs;
12577                 }
12578             }
12579
12580             var cb = {
12581                 success: this.handleResponse,
12582                 failure: this.handleFailure,
12583                 scope: this,
12584                 argument: {options: o},
12585                 timeout : o.timeout || this.timeout
12586             };
12587
12588             var method = o.method||this.method||(p ? "POST" : "GET");
12589
12590             if(method == 'GET' && (this.disableCaching && o.disableCaching !== false) || o.disableCaching === true){
12591                 url += (url.indexOf('?') != -1 ? '&' : '?') + '_dc=' + (new Date().getTime());
12592             }
12593
12594             if(typeof o.autoAbort == 'boolean'){ // options gets top priority
12595                 if(o.autoAbort){
12596                     this.abort();
12597                 }
12598             }else if(this.autoAbort !== false){
12599                 this.abort();
12600             }
12601
12602             if((method == 'GET' && p) || o.xmlData){
12603                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
12604                 p = '';
12605             }
12606             Roo.lib.Ajax.useDefaultHeader = typeof(o.headers) == 'undefined' || typeof(o.headers['Content-Type']) == 'undefined';
12607             this.transId = Roo.lib.Ajax.request(method, url, cb, p, o);
12608             Roo.lib.Ajax.useDefaultHeader == true;
12609             return this.transId;
12610         }else{
12611             Roo.callback(o.callback, o.scope, [o, null, null]);
12612             return null;
12613         }
12614     },
12615
12616     /**
12617      * Determine whether this object has a request outstanding.
12618      * @param {Number} transactionId (Optional) defaults to the last transaction
12619      * @return {Boolean} True if there is an outstanding request.
12620      */
12621     isLoading : function(transId){
12622         if(transId){
12623             return Roo.lib.Ajax.isCallInProgress(transId);
12624         }else{
12625             return this.transId ? true : false;
12626         }
12627     },
12628
12629     /**
12630      * Aborts any outstanding request.
12631      * @param {Number} transactionId (Optional) defaults to the last transaction
12632      */
12633     abort : function(transId){
12634         if(transId || this.isLoading()){
12635             Roo.lib.Ajax.abort(transId || this.transId);
12636         }
12637     },
12638
12639     // private
12640     handleResponse : function(response){
12641         this.transId = false;
12642         var options = response.argument.options;
12643         response.argument = options ? options.argument : null;
12644         this.fireEvent("requestcomplete", this, response, options);
12645         Roo.callback(options.success, options.scope, [response, options]);
12646         Roo.callback(options.callback, options.scope, [options, true, response]);
12647     },
12648
12649     // private
12650     handleFailure : function(response, e){
12651         this.transId = false;
12652         var options = response.argument.options;
12653         response.argument = options ? options.argument : null;
12654         this.fireEvent("requestexception", this, response, options, e);
12655         Roo.callback(options.failure, options.scope, [response, options]);
12656         Roo.callback(options.callback, options.scope, [options, false, response]);
12657     },
12658
12659     // private
12660     doFormUpload : function(o, ps, url){
12661         var id = Roo.id();
12662         var frame = document.createElement('iframe');
12663         frame.id = id;
12664         frame.name = id;
12665         frame.className = 'x-hidden';
12666         if(Roo.isIE){
12667             frame.src = Roo.SSL_SECURE_URL;
12668         }
12669         document.body.appendChild(frame);
12670
12671         if(Roo.isIE){
12672            document.frames[id].name = id;
12673         }
12674
12675         var form = Roo.getDom(o.form);
12676         form.target = id;
12677         form.method = 'POST';
12678         form.enctype = form.encoding = 'multipart/form-data';
12679         if(url){
12680             form.action = url;
12681         }
12682
12683         var hiddens, hd;
12684         if(ps){ // add dynamic params
12685             hiddens = [];
12686             ps = Roo.urlDecode(ps, false);
12687             for(var k in ps){
12688                 if(ps.hasOwnProperty(k)){
12689                     hd = document.createElement('input');
12690                     hd.type = 'hidden';
12691                     hd.name = k;
12692                     hd.value = ps[k];
12693                     form.appendChild(hd);
12694                     hiddens.push(hd);
12695                 }
12696             }
12697         }
12698
12699         function cb(){
12700             var r = {  // bogus response object
12701                 responseText : '',
12702                 responseXML : null
12703             };
12704
12705             r.argument = o ? o.argument : null;
12706
12707             try { //
12708                 var doc;
12709                 if(Roo.isIE){
12710                     doc = frame.contentWindow.document;
12711                 }else {
12712                     doc = (frame.contentDocument || window.frames[id].document);
12713                 }
12714                 if(doc && doc.body){
12715                     r.responseText = doc.body.innerHTML;
12716                 }
12717                 if(doc && doc.XMLDocument){
12718                     r.responseXML = doc.XMLDocument;
12719                 }else {
12720                     r.responseXML = doc;
12721                 }
12722             }
12723             catch(e) {
12724                 // ignore
12725             }
12726
12727             Roo.EventManager.removeListener(frame, 'load', cb, this);
12728
12729             this.fireEvent("requestcomplete", this, r, o);
12730             Roo.callback(o.success, o.scope, [r, o]);
12731             Roo.callback(o.callback, o.scope, [o, true, r]);
12732
12733             setTimeout(function(){document.body.removeChild(frame);}, 100);
12734         }
12735
12736         Roo.EventManager.on(frame, 'load', cb, this);
12737         form.submit();
12738
12739         if(hiddens){ // remove dynamic params
12740             for(var i = 0, len = hiddens.length; i < len; i++){
12741                 form.removeChild(hiddens[i]);
12742             }
12743         }
12744     },
12745     // this is a 'formdata version???'
12746     
12747     
12748     doFormDataUpload : function(o,  url)
12749     {
12750         var formData;
12751         if (o.form) {
12752             var form =  Roo.getDom(o.form);
12753             form.enctype = form.encoding = 'multipart/form-data';
12754             formData = o.formData === true ? new FormData(form) : o.formData;
12755         } else {
12756             formData = o.formData === true ? new FormData() : o.formData;
12757         }
12758         
12759       
12760         var cb = {
12761             success: this.handleResponse,
12762             failure: this.handleFailure,
12763             scope: this,
12764             argument: {options: o},
12765             timeout : o.timeout || this.timeout
12766         };
12767  
12768         if(typeof o.autoAbort == 'boolean'){ // options gets top priority
12769             if(o.autoAbort){
12770                 this.abort();
12771             }
12772         }else if(this.autoAbort !== false){
12773             this.abort();
12774         }
12775
12776         //Roo.lib.Ajax.defaultPostHeader = null;
12777         Roo.lib.Ajax.useDefaultHeader = false;
12778         this.transId = Roo.lib.Ajax.request( "POST", url, cb,  formData, o);
12779         Roo.lib.Ajax.useDefaultHeader = true;
12780  
12781          
12782     }
12783     
12784 });
12785 /*
12786  * Based on:
12787  * Ext JS Library 1.1.1
12788  * Copyright(c) 2006-2007, Ext JS, LLC.
12789  *
12790  * Originally Released Under LGPL - original licence link has changed is not relivant.
12791  *
12792  * Fork - LGPL
12793  * <script type="text/javascript">
12794  */
12795  
12796 /**
12797  * Global Ajax request class.
12798  * 
12799  * @class Roo.Ajax
12800  * @extends Roo.data.Connection
12801  * @static
12802  * 
12803  * @cfg {String} url  The default URL to be used for requests to the server. (defaults to undefined)
12804  * @cfg {Object} extraParams  An object containing properties which are used as extra parameters to each request made by this object. (defaults to undefined)
12805  * @cfg {Object} defaultHeaders  An object containing request headers which are added to each request made by this object. (defaults to undefined)
12806  * @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)
12807  * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
12808  * @cfg {Boolean} autoAbort (Optional) Whether a new request should abort any pending requests. (defaults to false)
12809  * @cfg {Boolean} disableCaching (Optional)   True to add a unique cache-buster param to GET requests. (defaults to true)
12810  */
12811 Roo.Ajax = new Roo.data.Connection({
12812     // fix up the docs
12813     /**
12814      * @scope Roo.Ajax
12815      * @type {Boolear} 
12816      */
12817     autoAbort : false,
12818
12819     /**
12820      * Serialize the passed form into a url encoded string
12821      * @scope Roo.Ajax
12822      * @param {String/HTMLElement} form
12823      * @return {String}
12824      */
12825     serializeForm : function(form){
12826         return Roo.lib.Ajax.serializeForm(form);
12827     }
12828 });/*
12829  * Based on:
12830  * Ext JS Library 1.1.1
12831  * Copyright(c) 2006-2007, Ext JS, LLC.
12832  *
12833  * Originally Released Under LGPL - original licence link has changed is not relivant.
12834  *
12835  * Fork - LGPL
12836  * <script type="text/javascript">
12837  */
12838
12839  
12840 /**
12841  * @class Roo.UpdateManager
12842  * @extends Roo.util.Observable
12843  * Provides AJAX-style update for Element object.<br><br>
12844  * Usage:<br>
12845  * <pre><code>
12846  * // Get it from a Roo.Element object
12847  * var el = Roo.get("foo");
12848  * var mgr = el.getUpdateManager();
12849  * mgr.update("http://myserver.com/index.php", "param1=1&amp;param2=2");
12850  * ...
12851  * mgr.formUpdate("myFormId", "http://myserver.com/index.php");
12852  * <br>
12853  * // or directly (returns the same UpdateManager instance)
12854  * var mgr = new Roo.UpdateManager("myElementId");
12855  * mgr.startAutoRefresh(60, "http://myserver.com/index.php");
12856  * mgr.on("update", myFcnNeedsToKnow);
12857  * <br>
12858    // short handed call directly from the element object
12859    Roo.get("foo").load({
12860         url: "bar.php",
12861         scripts:true,
12862         params: "for=bar",
12863         text: "Loading Foo..."
12864    });
12865  * </code></pre>
12866  * @constructor
12867  * Create new UpdateManager directly.
12868  * @param {String/HTMLElement/Roo.Element} el The element to update
12869  * @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).
12870  */
12871 Roo.UpdateManager = function(el, forceNew){
12872     el = Roo.get(el);
12873     if(!forceNew && el.updateManager){
12874         return el.updateManager;
12875     }
12876     /**
12877      * The Element object
12878      * @type Roo.Element
12879      */
12880     this.el = el;
12881     /**
12882      * Cached url to use for refreshes. Overwritten every time update() is called unless "discardUrl" param is set to true.
12883      * @type String
12884      */
12885     this.defaultUrl = null;
12886
12887     this.addEvents({
12888         /**
12889          * @event beforeupdate
12890          * Fired before an update is made, return false from your handler and the update is cancelled.
12891          * @param {Roo.Element} el
12892          * @param {String/Object/Function} url
12893          * @param {String/Object} params
12894          */
12895         "beforeupdate": true,
12896         /**
12897          * @event update
12898          * Fired after successful update is made.
12899          * @param {Roo.Element} el
12900          * @param {Object} oResponseObject The response Object
12901          */
12902         "update": true,
12903         /**
12904          * @event failure
12905          * Fired on update failure.
12906          * @param {Roo.Element} el
12907          * @param {Object} oResponseObject The response Object
12908          */
12909         "failure": true
12910     });
12911     var d = Roo.UpdateManager.defaults;
12912     /**
12913      * Blank page URL to use with SSL file uploads (Defaults to Roo.UpdateManager.defaults.sslBlankUrl or "about:blank").
12914      * @type String
12915      */
12916     this.sslBlankUrl = d.sslBlankUrl;
12917     /**
12918      * Whether to append unique parameter on get request to disable caching (Defaults to Roo.UpdateManager.defaults.disableCaching or false).
12919      * @type Boolean
12920      */
12921     this.disableCaching = d.disableCaching;
12922     /**
12923      * Text for loading indicator (Defaults to Roo.UpdateManager.defaults.indicatorText or '&lt;div class="loading-indicator"&gt;Loading...&lt;/div&gt;').
12924      * @type String
12925      */
12926     this.indicatorText = d.indicatorText;
12927     /**
12928      * Whether to show indicatorText when loading (Defaults to Roo.UpdateManager.defaults.showLoadIndicator or true).
12929      * @type String
12930      */
12931     this.showLoadIndicator = d.showLoadIndicator;
12932     /**
12933      * Timeout for requests or form posts in seconds (Defaults to Roo.UpdateManager.defaults.timeout or 30 seconds).
12934      * @type Number
12935      */
12936     this.timeout = d.timeout;
12937
12938     /**
12939      * True to process scripts in the output (Defaults to Roo.UpdateManager.defaults.loadScripts (false)).
12940      * @type Boolean
12941      */
12942     this.loadScripts = d.loadScripts;
12943
12944     /**
12945      * Transaction object of current executing transaction
12946      */
12947     this.transaction = null;
12948
12949     /**
12950      * @private
12951      */
12952     this.autoRefreshProcId = null;
12953     /**
12954      * Delegate for refresh() prebound to "this", use myUpdater.refreshDelegate.createCallback(arg1, arg2) to bind arguments
12955      * @type Function
12956      */
12957     this.refreshDelegate = this.refresh.createDelegate(this);
12958     /**
12959      * Delegate for update() prebound to "this", use myUpdater.updateDelegate.createCallback(arg1, arg2) to bind arguments
12960      * @type Function
12961      */
12962     this.updateDelegate = this.update.createDelegate(this);
12963     /**
12964      * Delegate for formUpdate() prebound to "this", use myUpdater.formUpdateDelegate.createCallback(arg1, arg2) to bind arguments
12965      * @type Function
12966      */
12967     this.formUpdateDelegate = this.formUpdate.createDelegate(this);
12968     /**
12969      * @private
12970      */
12971     this.successDelegate = this.processSuccess.createDelegate(this);
12972     /**
12973      * @private
12974      */
12975     this.failureDelegate = this.processFailure.createDelegate(this);
12976
12977     if(!this.renderer){
12978      /**
12979       * The renderer for this UpdateManager. Defaults to {@link Roo.UpdateManager.BasicRenderer}.
12980       */
12981     this.renderer = new Roo.UpdateManager.BasicRenderer();
12982     }
12983     
12984     Roo.UpdateManager.superclass.constructor.call(this);
12985 };
12986
12987 Roo.extend(Roo.UpdateManager, Roo.util.Observable, {
12988     /**
12989      * Get the Element this UpdateManager is bound to
12990      * @return {Roo.Element} The element
12991      */
12992     getEl : function(){
12993         return this.el;
12994     },
12995     /**
12996      * Performs an async request, updating this element with the response. If params are specified it uses POST, otherwise it uses GET.
12997      * @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:
12998 <pre><code>
12999 um.update({<br/>
13000     url: "your-url.php",<br/>
13001     params: {param1: "foo", param2: "bar"}, // or a URL encoded string<br/>
13002     callback: yourFunction,<br/>
13003     scope: yourObject, //(optional scope)  <br/>
13004     discardUrl: false, <br/>
13005     nocache: false,<br/>
13006     text: "Loading...",<br/>
13007     timeout: 30,<br/>
13008     scripts: false<br/>
13009 });
13010 </code></pre>
13011      * The only required property is url. The optional properties nocache, text and scripts
13012      * are shorthand for disableCaching, indicatorText and loadScripts and are used to set their associated property on this UpdateManager instance.
13013      * @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}
13014      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess, oResponse)
13015      * @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.
13016      */
13017     update : function(url, params, callback, discardUrl){
13018         if(this.fireEvent("beforeupdate", this.el, url, params) !== false){
13019             var method = this.method,
13020                 cfg;
13021             if(typeof url == "object"){ // must be config object
13022                 cfg = url;
13023                 url = cfg.url;
13024                 params = params || cfg.params;
13025                 callback = callback || cfg.callback;
13026                 discardUrl = discardUrl || cfg.discardUrl;
13027                 if(callback && cfg.scope){
13028                     callback = callback.createDelegate(cfg.scope);
13029                 }
13030                 if(typeof cfg.method != "undefined"){method = cfg.method;};
13031                 if(typeof cfg.nocache != "undefined"){this.disableCaching = cfg.nocache;};
13032                 if(typeof cfg.text != "undefined"){this.indicatorText = '<div class="loading-indicator">'+cfg.text+"</div>";};
13033                 if(typeof cfg.scripts != "undefined"){this.loadScripts = cfg.scripts;};
13034                 if(typeof cfg.timeout != "undefined"){this.timeout = cfg.timeout;};
13035             }
13036             this.showLoading();
13037             if(!discardUrl){
13038                 this.defaultUrl = url;
13039             }
13040             if(typeof url == "function"){
13041                 url = url.call(this);
13042             }
13043
13044             method = method || (params ? "POST" : "GET");
13045             if(method == "GET"){
13046                 url = this.prepareUrl(url);
13047             }
13048
13049             var o = Roo.apply(cfg ||{}, {
13050                 url : url,
13051                 params: params,
13052                 success: this.successDelegate,
13053                 failure: this.failureDelegate,
13054                 callback: undefined,
13055                 timeout: (this.timeout*1000),
13056                 argument: {"url": url, "form": null, "callback": callback, "params": params}
13057             });
13058             Roo.log("updated manager called with timeout of " + o.timeout);
13059             this.transaction = Roo.Ajax.request(o);
13060         }
13061     },
13062
13063     /**
13064      * 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.
13065      * Uses this.sslBlankUrl for SSL file uploads to prevent IE security warning.
13066      * @param {String/HTMLElement} form The form Id or form element
13067      * @param {String} url (optional) The url to pass the form to. If omitted the action attribute on the form will be used.
13068      * @param {Boolean} reset (optional) Whether to try to reset the form after the update
13069      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess, oResponse)
13070      */
13071     formUpdate : function(form, url, reset, callback){
13072         if(this.fireEvent("beforeupdate", this.el, form, url) !== false){
13073             if(typeof url == "function"){
13074                 url = url.call(this);
13075             }
13076             form = Roo.getDom(form);
13077             this.transaction = Roo.Ajax.request({
13078                 form: form,
13079                 url:url,
13080                 success: this.successDelegate,
13081                 failure: this.failureDelegate,
13082                 timeout: (this.timeout*1000),
13083                 argument: {"url": url, "form": form, "callback": callback, "reset": reset}
13084             });
13085             this.showLoading.defer(1, this);
13086         }
13087     },
13088
13089     /**
13090      * Refresh the element with the last used url or defaultUrl. If there is no url, it returns immediately
13091      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess)
13092      */
13093     refresh : function(callback){
13094         if(this.defaultUrl == null){
13095             return;
13096         }
13097         this.update(this.defaultUrl, null, callback, true);
13098     },
13099
13100     /**
13101      * Set this element to auto refresh.
13102      * @param {Number} interval How often to update (in seconds).
13103      * @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)
13104      * @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}
13105      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess)
13106      * @param {Boolean} refreshNow (optional) Whether to execute the refresh now, or wait the interval
13107      */
13108     startAutoRefresh : function(interval, url, params, callback, refreshNow){
13109         if(refreshNow){
13110             this.update(url || this.defaultUrl, params, callback, true);
13111         }
13112         if(this.autoRefreshProcId){
13113             clearInterval(this.autoRefreshProcId);
13114         }
13115         this.autoRefreshProcId = setInterval(this.update.createDelegate(this, [url || this.defaultUrl, params, callback, true]), interval*1000);
13116     },
13117
13118     /**
13119      * Stop auto refresh on this element.
13120      */
13121      stopAutoRefresh : function(){
13122         if(this.autoRefreshProcId){
13123             clearInterval(this.autoRefreshProcId);
13124             delete this.autoRefreshProcId;
13125         }
13126     },
13127
13128     isAutoRefreshing : function(){
13129        return this.autoRefreshProcId ? true : false;
13130     },
13131     /**
13132      * Called to update the element to "Loading" state. Override to perform custom action.
13133      */
13134     showLoading : function(){
13135         if(this.showLoadIndicator){
13136             this.el.update(this.indicatorText);
13137         }
13138     },
13139
13140     /**
13141      * Adds unique parameter to query string if disableCaching = true
13142      * @private
13143      */
13144     prepareUrl : function(url){
13145         if(this.disableCaching){
13146             var append = "_dc=" + (new Date().getTime());
13147             if(url.indexOf("?") !== -1){
13148                 url += "&" + append;
13149             }else{
13150                 url += "?" + append;
13151             }
13152         }
13153         return url;
13154     },
13155
13156     /**
13157      * @private
13158      */
13159     processSuccess : function(response){
13160         this.transaction = null;
13161         if(response.argument.form && response.argument.reset){
13162             try{ // put in try/catch since some older FF releases had problems with this
13163                 response.argument.form.reset();
13164             }catch(e){}
13165         }
13166         if(this.loadScripts){
13167             this.renderer.render(this.el, response, this,
13168                 this.updateComplete.createDelegate(this, [response]));
13169         }else{
13170             this.renderer.render(this.el, response, this);
13171             this.updateComplete(response);
13172         }
13173     },
13174
13175     updateComplete : function(response){
13176         this.fireEvent("update", this.el, response);
13177         if(typeof response.argument.callback == "function"){
13178             response.argument.callback(this.el, true, response);
13179         }
13180     },
13181
13182     /**
13183      * @private
13184      */
13185     processFailure : function(response){
13186         this.transaction = null;
13187         this.fireEvent("failure", this.el, response);
13188         if(typeof response.argument.callback == "function"){
13189             response.argument.callback(this.el, false, response);
13190         }
13191     },
13192
13193     /**
13194      * Set the content renderer for this UpdateManager. See {@link Roo.UpdateManager.BasicRenderer#render} for more details.
13195      * @param {Object} renderer The object implementing the render() method
13196      */
13197     setRenderer : function(renderer){
13198         this.renderer = renderer;
13199     },
13200
13201     getRenderer : function(){
13202        return this.renderer;
13203     },
13204
13205     /**
13206      * Set the defaultUrl used for updates
13207      * @param {String/Function} defaultUrl The url or a function to call to get the url
13208      */
13209     setDefaultUrl : function(defaultUrl){
13210         this.defaultUrl = defaultUrl;
13211     },
13212
13213     /**
13214      * Aborts the executing transaction
13215      */
13216     abort : function(){
13217         if(this.transaction){
13218             Roo.Ajax.abort(this.transaction);
13219         }
13220     },
13221
13222     /**
13223      * Returns true if an update is in progress
13224      * @return {Boolean}
13225      */
13226     isUpdating : function(){
13227         if(this.transaction){
13228             return Roo.Ajax.isLoading(this.transaction);
13229         }
13230         return false;
13231     }
13232 });
13233
13234 /**
13235  * @class Roo.UpdateManager.defaults
13236  * @static (not really - but it helps the doc tool)
13237  * The defaults collection enables customizing the default properties of UpdateManager
13238  */
13239    Roo.UpdateManager.defaults = {
13240        /**
13241          * Timeout for requests or form posts in seconds (Defaults 30 seconds).
13242          * @type Number
13243          */
13244          timeout : 30,
13245
13246          /**
13247          * True to process scripts by default (Defaults to false).
13248          * @type Boolean
13249          */
13250         loadScripts : false,
13251
13252         /**
13253         * Blank page URL to use with SSL file uploads (Defaults to "javascript:false").
13254         * @type String
13255         */
13256         sslBlankUrl : (Roo.SSL_SECURE_URL || "javascript:false"),
13257         /**
13258          * Whether to append unique parameter on get request to disable caching (Defaults to false).
13259          * @type Boolean
13260          */
13261         disableCaching : false,
13262         /**
13263          * Whether to show indicatorText when loading (Defaults to true).
13264          * @type Boolean
13265          */
13266         showLoadIndicator : true,
13267         /**
13268          * Text for loading indicator (Defaults to '&lt;div class="loading-indicator"&gt;Loading...&lt;/div&gt;').
13269          * @type String
13270          */
13271         indicatorText : '<div class="loading-indicator">Loading...</div>'
13272    };
13273
13274 /**
13275  * Static convenience method. This method is deprecated in favor of el.load({url:'foo.php', ...}).
13276  *Usage:
13277  * <pre><code>Roo.UpdateManager.updateElement("my-div", "stuff.php");</code></pre>
13278  * @param {String/HTMLElement/Roo.Element} el The element to update
13279  * @param {String} url The url
13280  * @param {String/Object} params (optional) Url encoded param string or an object of name/value pairs
13281  * @param {Object} options (optional) A config object with any of the UpdateManager properties you want to set - for example: {disableCaching:true, indicatorText: "Loading data..."}
13282  * @static
13283  * @deprecated
13284  * @member Roo.UpdateManager
13285  */
13286 Roo.UpdateManager.updateElement = function(el, url, params, options){
13287     var um = Roo.get(el, true).getUpdateManager();
13288     Roo.apply(um, options);
13289     um.update(url, params, options ? options.callback : null);
13290 };
13291 // alias for backwards compat
13292 Roo.UpdateManager.update = Roo.UpdateManager.updateElement;
13293 /**
13294  * @class Roo.UpdateManager.BasicRenderer
13295  * Default Content renderer. Updates the elements innerHTML with the responseText.
13296  */
13297 Roo.UpdateManager.BasicRenderer = function(){};
13298
13299 Roo.UpdateManager.BasicRenderer.prototype = {
13300     /**
13301      * This is called when the transaction is completed and it's time to update the element - The BasicRenderer
13302      * updates the elements innerHTML with the responseText - To perform a custom render (i.e. XML or JSON processing),
13303      * create an object with a "render(el, response)" method and pass it to setRenderer on the UpdateManager.
13304      * @param {Roo.Element} el The element being rendered
13305      * @param {Object} response The YUI Connect response object
13306      * @param {UpdateManager} updateManager The calling update manager
13307      * @param {Function} callback A callback that will need to be called if loadScripts is true on the UpdateManager
13308      */
13309      render : function(el, response, updateManager, callback){
13310         el.update(response.responseText, updateManager.loadScripts, callback);
13311     }
13312 };
13313 /*
13314  * Based on:
13315  * Roo JS
13316  * (c)) Alan Knowles
13317  * Licence : LGPL
13318  */
13319
13320
13321 /**
13322  * @class Roo.DomTemplate
13323  * @extends Roo.Template
13324  * An effort at a dom based template engine..
13325  *
13326  * Similar to XTemplate, except it uses dom parsing to create the template..
13327  *
13328  * Supported features:
13329  *
13330  *  Tags:
13331
13332 <pre><code>
13333       {a_variable} - output encoded.
13334       {a_variable.format:("Y-m-d")} - call a method on the variable
13335       {a_variable:raw} - unencoded output
13336       {a_variable:toFixed(1,2)} - Roo.util.Format."toFixed"
13337       {a_variable:this.method_on_template(...)} - call a method on the template object.
13338  
13339 </code></pre>
13340  *  The tpl tag:
13341 <pre><code>
13342         &lt;div roo-for="a_variable or condition.."&gt;&lt;/div&gt;
13343         &lt;div roo-if="a_variable or condition"&gt;&lt;/div&gt;
13344         &lt;div roo-exec="some javascript"&gt;&lt;/div&gt;
13345         &lt;div roo-name="named_template"&gt;&lt;/div&gt; 
13346   
13347 </code></pre>
13348  *      
13349  */
13350 Roo.DomTemplate = function()
13351 {
13352      Roo.DomTemplate.superclass.constructor.apply(this, arguments);
13353      if (this.html) {
13354         this.compile();
13355      }
13356 };
13357
13358
13359 Roo.extend(Roo.DomTemplate, Roo.Template, {
13360     /**
13361      * id counter for sub templates.
13362      */
13363     id : 0,
13364     /**
13365      * flag to indicate if dom parser is inside a pre,
13366      * it will strip whitespace if not.
13367      */
13368     inPre : false,
13369     
13370     /**
13371      * The various sub templates
13372      */
13373     tpls : false,
13374     
13375     
13376     
13377     /**
13378      *
13379      * basic tag replacing syntax
13380      * WORD:WORD()
13381      *
13382      * // you can fake an object call by doing this
13383      *  x.t:(test,tesT) 
13384      * 
13385      */
13386     re : /(\{|\%7B)([\w-\.]+)(?:\:([\w\.]*)(?:\(([^)]*?)?\))?)?(\}|\%7D)/g,
13387     //re : /\{([\w-\.]+)(?:\:([\w\.]*)(?:\((.*?)?\))?)?\}/g,
13388     
13389     iterChild : function (node, method) {
13390         
13391         var oldPre = this.inPre;
13392         if (node.tagName == 'PRE') {
13393             this.inPre = true;
13394         }
13395         for( var i = 0; i < node.childNodes.length; i++) {
13396             method.call(this, node.childNodes[i]);
13397         }
13398         this.inPre = oldPre;
13399     },
13400     
13401     
13402     
13403     /**
13404      * compile the template
13405      *
13406      * This is not recursive, so I'm not sure how nested templates are really going to be handled..
13407      *
13408      */
13409     compile: function()
13410     {
13411         var s = this.html;
13412         
13413         // covert the html into DOM...
13414         var doc = false;
13415         var div =false;
13416         try {
13417             doc = document.implementation.createHTMLDocument("");
13418             doc.documentElement.innerHTML =   this.html  ;
13419             div = doc.documentElement;
13420         } catch (e) {
13421             // old IE... - nasty -- it causes all sorts of issues.. with
13422             // images getting pulled from server..
13423             div = document.createElement('div');
13424             div.innerHTML = this.html;
13425         }
13426         //doc.documentElement.innerHTML = htmlBody
13427          
13428         
13429         
13430         this.tpls = [];
13431         var _t = this;
13432         this.iterChild(div, function(n) {_t.compileNode(n, true); });
13433         
13434         var tpls = this.tpls;
13435         
13436         // create a top level template from the snippet..
13437         
13438         //Roo.log(div.innerHTML);
13439         
13440         var tpl = {
13441             uid : 'master',
13442             id : this.id++,
13443             attr : false,
13444             value : false,
13445             body : div.innerHTML,
13446             
13447             forCall : false,
13448             execCall : false,
13449             dom : div,
13450             isTop : true
13451             
13452         };
13453         tpls.unshift(tpl);
13454         
13455         
13456         // compile them...
13457         this.tpls = [];
13458         Roo.each(tpls, function(tp){
13459             this.compileTpl(tp);
13460             this.tpls[tp.id] = tp;
13461         }, this);
13462         
13463         this.master = tpls[0];
13464         return this;
13465         
13466         
13467     },
13468     
13469     compileNode : function(node, istop) {
13470         // test for
13471         //Roo.log(node);
13472         
13473         
13474         // skip anything not a tag..
13475         if (node.nodeType != 1) {
13476             if (node.nodeType == 3 && !this.inPre) {
13477                 // reduce white space..
13478                 node.nodeValue = node.nodeValue.replace(/\s+/g, ' '); 
13479                 
13480             }
13481             return;
13482         }
13483         
13484         var tpl = {
13485             uid : false,
13486             id : false,
13487             attr : false,
13488             value : false,
13489             body : '',
13490             
13491             forCall : false,
13492             execCall : false,
13493             dom : false,
13494             isTop : istop
13495             
13496             
13497         };
13498         
13499         
13500         switch(true) {
13501             case (node.hasAttribute('roo-for')): tpl.attr = 'for'; break;
13502             case (node.hasAttribute('roo-if')): tpl.attr = 'if'; break;
13503             case (node.hasAttribute('roo-name')): tpl.attr = 'name'; break;
13504             case (node.hasAttribute('roo-exec')): tpl.attr = 'exec'; break;
13505             // no default..
13506         }
13507         
13508         
13509         if (!tpl.attr) {
13510             // just itterate children..
13511             this.iterChild(node,this.compileNode);
13512             return;
13513         }
13514         tpl.uid = this.id++;
13515         tpl.value = node.getAttribute('roo-' +  tpl.attr);
13516         node.removeAttribute('roo-'+ tpl.attr);
13517         if (tpl.attr != 'name') {
13518             var placeholder = document.createTextNode('{domtpl' + tpl.uid + '}');
13519             node.parentNode.replaceChild(placeholder,  node);
13520         } else {
13521             
13522             var placeholder =  document.createElement('span');
13523             placeholder.className = 'roo-tpl-' + tpl.value;
13524             node.parentNode.replaceChild(placeholder,  node);
13525         }
13526         
13527         // parent now sees '{domtplXXXX}
13528         this.iterChild(node,this.compileNode);
13529         
13530         // we should now have node body...
13531         var div = document.createElement('div');
13532         div.appendChild(node);
13533         tpl.dom = node;
13534         // this has the unfortunate side effect of converting tagged attributes
13535         // eg. href="{...}" into %7C...%7D
13536         // this has been fixed by searching for those combo's although it's a bit hacky..
13537         
13538         
13539         tpl.body = div.innerHTML;
13540         
13541         
13542          
13543         tpl.id = tpl.uid;
13544         switch(tpl.attr) {
13545             case 'for' :
13546                 switch (tpl.value) {
13547                     case '.':  tpl.forCall = new Function('values', 'parent', 'with(values){ return values; }'); break;
13548                     case '..': tpl.forCall= new Function('values', 'parent', 'with(values){ return parent; }'); break;
13549                     default:   tpl.forCall= new Function('values', 'parent', 'with(values){ return '+tpl.value+'; }');
13550                 }
13551                 break;
13552             
13553             case 'exec':
13554                 tpl.execCall = new Function('values', 'parent', 'with(values){ '+(Roo.util.Format.htmlDecode(tpl.value))+'; }');
13555                 break;
13556             
13557             case 'if':     
13558                 tpl.ifCall = new Function('values', 'parent', 'with(values){ return '+(Roo.util.Format.htmlDecode(tpl.value))+'; }');
13559                 break;
13560             
13561             case 'name':
13562                 tpl.id  = tpl.value; // replace non characters???
13563                 break;
13564             
13565         }
13566         
13567         
13568         this.tpls.push(tpl);
13569         
13570         
13571         
13572     },
13573     
13574     
13575     
13576     
13577     /**
13578      * Compile a segment of the template into a 'sub-template'
13579      *
13580      * 
13581      * 
13582      *
13583      */
13584     compileTpl : function(tpl)
13585     {
13586         var fm = Roo.util.Format;
13587         var useF = this.disableFormats !== true;
13588         
13589         var sep = Roo.isGecko ? "+\n" : ",\n";
13590         
13591         var undef = function(str) {
13592             Roo.debug && Roo.log("Property not found :"  + str);
13593             return '';
13594         };
13595           
13596         //Roo.log(tpl.body);
13597         
13598         
13599         
13600         var fn = function(m, lbrace, name, format, args)
13601         {
13602             //Roo.log("ARGS");
13603             //Roo.log(arguments);
13604             args = args ? args.replace(/\\'/g,"'") : args;
13605             //["{TEST:(a,b,c)}", "TEST", "", "a,b,c", 0, "{TEST:(a,b,c)}"]
13606             if (typeof(format) == 'undefined') {
13607                 format =  'htmlEncode'; 
13608             }
13609             if (format == 'raw' ) {
13610                 format = false;
13611             }
13612             
13613             if(name.substr(0, 6) == 'domtpl'){
13614                 return "'"+ sep +'this.applySubTemplate('+name.substr(6)+', values, parent)'+sep+"'";
13615             }
13616             
13617             // build an array of options to determine if value is undefined..
13618             
13619             // basically get 'xxxx.yyyy' then do
13620             // (typeof(xxxx) == 'undefined' || typeof(xxx.yyyy) == 'undefined') ?
13621             //    (function () { Roo.log("Property not found"); return ''; })() :
13622             //    ......
13623             
13624             var udef_ar = [];
13625             var lookfor = '';
13626             Roo.each(name.split('.'), function(st) {
13627                 lookfor += (lookfor.length ? '.': '') + st;
13628                 udef_ar.push(  "(typeof(" + lookfor + ") == 'undefined')"  );
13629             });
13630             
13631             var udef_st = '((' + udef_ar.join(" || ") +") ? undef('" + name + "') : "; // .. needs )
13632             
13633             
13634             if(format && useF){
13635                 
13636                 args = args ? ',' + args : "";
13637                  
13638                 if(format.substr(0, 5) != "this."){
13639                     format = "fm." + format + '(';
13640                 }else{
13641                     format = 'this.call("'+ format.substr(5) + '", ';
13642                     args = ", values";
13643                 }
13644                 
13645                 return "'"+ sep +   udef_st   +    format + name + args + "))"+sep+"'";
13646             }
13647              
13648             if (args && args.length) {
13649                 // called with xxyx.yuu:(test,test)
13650                 // change to ()
13651                 return "'"+ sep + udef_st  + name + '(' +  args + "))"+sep+"'";
13652             }
13653             // raw.. - :raw modifier..
13654             return "'"+ sep + udef_st  + name + ")"+sep+"'";
13655             
13656         };
13657         var body;
13658         // branched to use + in gecko and [].join() in others
13659         if(Roo.isGecko){
13660             body = "tpl.compiled = function(values, parent){  with(values) { return '" +
13661                    tpl.body.replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn) +
13662                     "';};};";
13663         }else{
13664             body = ["tpl.compiled = function(values, parent){  with (values) { return ['"];
13665             body.push(tpl.body.replace(/(\r\n|\n)/g,
13666                             '\\n').replace(/'/g, "\\'").replace(this.re, fn));
13667             body.push("'].join('');};};");
13668             body = body.join('');
13669         }
13670         
13671         Roo.debug && Roo.log(body.replace(/\\n/,'\n'));
13672        
13673         /** eval:var:tpl eval:var:fm eval:var:useF eval:var:undef  */
13674         eval(body);
13675         
13676         return this;
13677     },
13678      
13679     /**
13680      * same as applyTemplate, except it's done to one of the subTemplates
13681      * when using named templates, you can do:
13682      *
13683      * var str = pl.applySubTemplate('your-name', values);
13684      *
13685      * 
13686      * @param {Number} id of the template
13687      * @param {Object} values to apply to template
13688      * @param {Object} parent (normaly the instance of this object)
13689      */
13690     applySubTemplate : function(id, values, parent)
13691     {
13692         
13693         
13694         var t = this.tpls[id];
13695         
13696         
13697         try { 
13698             if(t.ifCall && !t.ifCall.call(this, values, parent)){
13699                 Roo.debug && Roo.log('if call on ' + t.value + ' return false');
13700                 return '';
13701             }
13702         } catch(e) {
13703             Roo.log('Xtemplate.applySubTemplate('+ id+ '): Exception thrown on roo-if="' + t.value + '" - ' + e.toString());
13704             Roo.log(values);
13705           
13706             return '';
13707         }
13708         try { 
13709             
13710             if(t.execCall && t.execCall.call(this, values, parent)){
13711                 return '';
13712             }
13713         } catch(e) {
13714             Roo.log('Xtemplate.applySubTemplate('+ id+ '): Exception thrown on roo-for="' + t.value + '" - ' + e.toString());
13715             Roo.log(values);
13716             return '';
13717         }
13718         
13719         try {
13720             var vs = t.forCall ? t.forCall.call(this, values, parent) : values;
13721             parent = t.target ? values : parent;
13722             if(t.forCall && vs instanceof Array){
13723                 var buf = [];
13724                 for(var i = 0, len = vs.length; i < len; i++){
13725                     try {
13726                         buf[buf.length] = t.compiled.call(this, vs[i], parent);
13727                     } catch (e) {
13728                         Roo.log('Xtemplate.applySubTemplate('+ id+ '): Exception thrown on body="' + t.value + '" - ' + e.toString());
13729                         Roo.log(e.body);
13730                         //Roo.log(t.compiled);
13731                         Roo.log(vs[i]);
13732                     }   
13733                 }
13734                 return buf.join('');
13735             }
13736         } catch (e) {
13737             Roo.log('Xtemplate.applySubTemplate('+ id+ '): Exception thrown on roo-for="' + t.value + '" - ' + e.toString());
13738             Roo.log(values);
13739             return '';
13740         }
13741         try {
13742             return t.compiled.call(this, vs, parent);
13743         } catch (e) {
13744             Roo.log('Xtemplate.applySubTemplate('+ id+ '): Exception thrown on body="' + t.value + '" - ' + e.toString());
13745             Roo.log(e.body);
13746             //Roo.log(t.compiled);
13747             Roo.log(values);
13748             return '';
13749         }
13750     },
13751
13752    
13753
13754     applyTemplate : function(values){
13755         return this.master.compiled.call(this, values, {});
13756         //var s = this.subs;
13757     },
13758
13759     apply : function(){
13760         return this.applyTemplate.apply(this, arguments);
13761     }
13762
13763  });
13764
13765 Roo.DomTemplate.from = function(el){
13766     el = Roo.getDom(el);
13767     return new Roo.Domtemplate(el.value || el.innerHTML);
13768 };/*
13769  * Based on:
13770  * Ext JS Library 1.1.1
13771  * Copyright(c) 2006-2007, Ext JS, LLC.
13772  *
13773  * Originally Released Under LGPL - original licence link has changed is not relivant.
13774  *
13775  * Fork - LGPL
13776  * <script type="text/javascript">
13777  */
13778
13779 /**
13780  * @class Roo.util.DelayedTask
13781  * Provides a convenient method of performing setTimeout where a new
13782  * timeout cancels the old timeout. An example would be performing validation on a keypress.
13783  * You can use this class to buffer
13784  * the keypress events for a certain number of milliseconds, and perform only if they stop
13785  * for that amount of time.
13786  * @constructor The parameters to this constructor serve as defaults and are not required.
13787  * @param {Function} fn (optional) The default function to timeout
13788  * @param {Object} scope (optional) The default scope of that timeout
13789  * @param {Array} args (optional) The default Array of arguments
13790  */
13791 Roo.util.DelayedTask = function(fn, scope, args){
13792     var id = null, d, t;
13793
13794     var call = function(){
13795         var now = new Date().getTime();
13796         if(now - t >= d){
13797             clearInterval(id);
13798             id = null;
13799             fn.apply(scope, args || []);
13800         }
13801     };
13802     /**
13803      * Cancels any pending timeout and queues a new one
13804      * @param {Number} delay The milliseconds to delay
13805      * @param {Function} newFn (optional) Overrides function passed to constructor
13806      * @param {Object} newScope (optional) Overrides scope passed to constructor
13807      * @param {Array} newArgs (optional) Overrides args passed to constructor
13808      */
13809     this.delay = function(delay, newFn, newScope, newArgs){
13810         if(id && delay != d){
13811             this.cancel();
13812         }
13813         d = delay;
13814         t = new Date().getTime();
13815         fn = newFn || fn;
13816         scope = newScope || scope;
13817         args = newArgs || args;
13818         if(!id){
13819             id = setInterval(call, d);
13820         }
13821     };
13822
13823     /**
13824      * Cancel the last queued timeout
13825      */
13826     this.cancel = function(){
13827         if(id){
13828             clearInterval(id);
13829             id = null;
13830         }
13831     };
13832 };/*
13833  * Based on:
13834  * Ext JS Library 1.1.1
13835  * Copyright(c) 2006-2007, Ext JS, LLC.
13836  *
13837  * Originally Released Under LGPL - original licence link has changed is not relivant.
13838  *
13839  * Fork - LGPL
13840  * <script type="text/javascript">
13841  */
13842 /**
13843  * @class Roo.util.TaskRunner
13844  * Manage background tasks - not sure why this is better that setInterval?
13845  * @static
13846  *
13847  */
13848  
13849 Roo.util.TaskRunner = function(interval){
13850     interval = interval || 10;
13851     var tasks = [], removeQueue = [];
13852     var id = 0;
13853     var running = false;
13854
13855     var stopThread = function(){
13856         running = false;
13857         clearInterval(id);
13858         id = 0;
13859     };
13860
13861     var startThread = function(){
13862         if(!running){
13863             running = true;
13864             id = setInterval(runTasks, interval);
13865         }
13866     };
13867
13868     var removeTask = function(task){
13869         removeQueue.push(task);
13870         if(task.onStop){
13871             task.onStop();
13872         }
13873     };
13874
13875     var runTasks = function(){
13876         if(removeQueue.length > 0){
13877             for(var i = 0, len = removeQueue.length; i < len; i++){
13878                 tasks.remove(removeQueue[i]);
13879             }
13880             removeQueue = [];
13881             if(tasks.length < 1){
13882                 stopThread();
13883                 return;
13884             }
13885         }
13886         var now = new Date().getTime();
13887         for(var i = 0, len = tasks.length; i < len; ++i){
13888             var t = tasks[i];
13889             var itime = now - t.taskRunTime;
13890             if(t.interval <= itime){
13891                 var rt = t.run.apply(t.scope || t, t.args || [++t.taskRunCount]);
13892                 t.taskRunTime = now;
13893                 if(rt === false || t.taskRunCount === t.repeat){
13894                     removeTask(t);
13895                     return;
13896                 }
13897             }
13898             if(t.duration && t.duration <= (now - t.taskStartTime)){
13899                 removeTask(t);
13900             }
13901         }
13902     };
13903
13904     /**
13905      * Queues a new task.
13906      * @param {Object} task
13907      *
13908      * Task property : interval = how frequent to run.
13909      * Task object should implement
13910      * function run()
13911      * Task object may implement
13912      * function onStop()
13913      */
13914     this.start = function(task){
13915         tasks.push(task);
13916         task.taskStartTime = new Date().getTime();
13917         task.taskRunTime = 0;
13918         task.taskRunCount = 0;
13919         startThread();
13920         return task;
13921     };
13922     /**
13923      * Stop  new task.
13924      * @param {Object} task
13925      */
13926     this.stop = function(task){
13927         removeTask(task);
13928         return task;
13929     };
13930     /**
13931      * Stop all Tasks
13932      */
13933     this.stopAll = function(){
13934         stopThread();
13935         for(var i = 0, len = tasks.length; i < len; i++){
13936             if(tasks[i].onStop){
13937                 tasks[i].onStop();
13938             }
13939         }
13940         tasks = [];
13941         removeQueue = [];
13942     };
13943 };
13944
13945 Roo.TaskMgr = new Roo.util.TaskRunner();/*
13946  * Based on:
13947  * Ext JS Library 1.1.1
13948  * Copyright(c) 2006-2007, Ext JS, LLC.
13949  *
13950  * Originally Released Under LGPL - original licence link has changed is not relivant.
13951  *
13952  * Fork - LGPL
13953  * <script type="text/javascript">
13954  */
13955
13956  
13957 /**
13958  * @class Roo.util.MixedCollection
13959  * @extends Roo.util.Observable
13960  * A Collection class that maintains both numeric indexes and keys and exposes events.
13961  * @constructor
13962  * @param {Boolean} allowFunctions True if the addAll function should add function references to the
13963  * collection (defaults to false)
13964  * @param {Function} keyFn A function that can accept an item of the type(s) stored in this MixedCollection
13965  * and return the key value for that item.  This is used when available to look up the key on items that
13966  * were passed without an explicit key parameter to a MixedCollection method.  Passing this parameter is
13967  * equivalent to providing an implementation for the {@link #getKey} method.
13968  */
13969 Roo.util.MixedCollection = function(allowFunctions, keyFn){
13970     this.items = [];
13971     this.map = {};
13972     this.keys = [];
13973     this.length = 0;
13974     this.addEvents({
13975         /**
13976          * @event clear
13977          * Fires when the collection is cleared.
13978          */
13979         "clear" : true,
13980         /**
13981          * @event add
13982          * Fires when an item is added to the collection.
13983          * @param {Number} index The index at which the item was added.
13984          * @param {Object} o The item added.
13985          * @param {String} key The key associated with the added item.
13986          */
13987         "add" : true,
13988         /**
13989          * @event replace
13990          * Fires when an item is replaced in the collection.
13991          * @param {String} key he key associated with the new added.
13992          * @param {Object} old The item being replaced.
13993          * @param {Object} new The new item.
13994          */
13995         "replace" : true,
13996         /**
13997          * @event remove
13998          * Fires when an item is removed from the collection.
13999          * @param {Object} o The item being removed.
14000          * @param {String} key (optional) The key associated with the removed item.
14001          */
14002         "remove" : true,
14003         "sort" : true
14004     });
14005     this.allowFunctions = allowFunctions === true;
14006     if(keyFn){
14007         this.getKey = keyFn;
14008     }
14009     Roo.util.MixedCollection.superclass.constructor.call(this);
14010 };
14011
14012 Roo.extend(Roo.util.MixedCollection, Roo.util.Observable, {
14013     allowFunctions : false,
14014     
14015 /**
14016  * Adds an item to the collection.
14017  * @param {String} key The key to associate with the item
14018  * @param {Object} o The item to add.
14019  * @return {Object} The item added.
14020  */
14021     add : function(key, o){
14022         if(arguments.length == 1){
14023             o = arguments[0];
14024             key = this.getKey(o);
14025         }
14026         if(typeof key == "undefined" || key === null){
14027             this.length++;
14028             this.items.push(o);
14029             this.keys.push(null);
14030         }else{
14031             var old = this.map[key];
14032             if(old){
14033                 return this.replace(key, o);
14034             }
14035             this.length++;
14036             this.items.push(o);
14037             this.map[key] = o;
14038             this.keys.push(key);
14039         }
14040         this.fireEvent("add", this.length-1, o, key);
14041         return o;
14042     },
14043        
14044 /**
14045   * MixedCollection has a generic way to fetch keys if you implement getKey.
14046 <pre><code>
14047 // normal way
14048 var mc = new Roo.util.MixedCollection();
14049 mc.add(someEl.dom.id, someEl);
14050 mc.add(otherEl.dom.id, otherEl);
14051 //and so on
14052
14053 // using getKey
14054 var mc = new Roo.util.MixedCollection();
14055 mc.getKey = function(el){
14056    return el.dom.id;
14057 };
14058 mc.add(someEl);
14059 mc.add(otherEl);
14060
14061 // or via the constructor
14062 var mc = new Roo.util.MixedCollection(false, function(el){
14063    return el.dom.id;
14064 });
14065 mc.add(someEl);
14066 mc.add(otherEl);
14067 </code></pre>
14068  * @param o {Object} The item for which to find the key.
14069  * @return {Object} The key for the passed item.
14070  */
14071     getKey : function(o){
14072          return o.id; 
14073     },
14074    
14075 /**
14076  * Replaces an item in the collection.
14077  * @param {String} key The key associated with the item to replace, or the item to replace.
14078  * @param o {Object} o (optional) If the first parameter passed was a key, the item to associate with that key.
14079  * @return {Object}  The new item.
14080  */
14081     replace : function(key, o){
14082         if(arguments.length == 1){
14083             o = arguments[0];
14084             key = this.getKey(o);
14085         }
14086         var old = this.item(key);
14087         if(typeof key == "undefined" || key === null || typeof old == "undefined"){
14088              return this.add(key, o);
14089         }
14090         var index = this.indexOfKey(key);
14091         this.items[index] = o;
14092         this.map[key] = o;
14093         this.fireEvent("replace", key, old, o);
14094         return o;
14095     },
14096    
14097 /**
14098  * Adds all elements of an Array or an Object to the collection.
14099  * @param {Object/Array} objs An Object containing properties which will be added to the collection, or
14100  * an Array of values, each of which are added to the collection.
14101  */
14102     addAll : function(objs){
14103         if(arguments.length > 1 || objs instanceof Array){
14104             var args = arguments.length > 1 ? arguments : objs;
14105             for(var i = 0, len = args.length; i < len; i++){
14106                 this.add(args[i]);
14107             }
14108         }else{
14109             for(var key in objs){
14110                 if(this.allowFunctions || typeof objs[key] != "function"){
14111                     this.add(key, objs[key]);
14112                 }
14113             }
14114         }
14115     },
14116    
14117 /**
14118  * Executes the specified function once for every item in the collection, passing each
14119  * item as the first and only parameter. returning false from the function will stop the iteration.
14120  * @param {Function} fn The function to execute for each item.
14121  * @param {Object} scope (optional) The scope in which to execute the function.
14122  */
14123     each : function(fn, scope){
14124         var items = [].concat(this.items); // each safe for removal
14125         for(var i = 0, len = items.length; i < len; i++){
14126             if(fn.call(scope || items[i], items[i], i, len) === false){
14127                 break;
14128             }
14129         }
14130     },
14131    
14132 /**
14133  * Executes the specified function once for every key in the collection, passing each
14134  * key, and its associated item as the first two parameters.
14135  * @param {Function} fn The function to execute for each item.
14136  * @param {Object} scope (optional) The scope in which to execute the function.
14137  */
14138     eachKey : function(fn, scope){
14139         for(var i = 0, len = this.keys.length; i < len; i++){
14140             fn.call(scope || window, this.keys[i], this.items[i], i, len);
14141         }
14142     },
14143    
14144 /**
14145  * Returns the first item in the collection which elicits a true return value from the
14146  * passed selection function.
14147  * @param {Function} fn The selection function to execute for each item.
14148  * @param {Object} scope (optional) The scope in which to execute the function.
14149  * @return {Object} The first item in the collection which returned true from the selection function.
14150  */
14151     find : function(fn, scope){
14152         for(var i = 0, len = this.items.length; i < len; i++){
14153             if(fn.call(scope || window, this.items[i], this.keys[i])){
14154                 return this.items[i];
14155             }
14156         }
14157         return null;
14158     },
14159    
14160 /**
14161  * Inserts an item at the specified index in the collection.
14162  * @param {Number} index The index to insert the item at.
14163  * @param {String} key The key to associate with the new item, or the item itself.
14164  * @param {Object} o  (optional) If the second parameter was a key, the new item.
14165  * @return {Object} The item inserted.
14166  */
14167     insert : function(index, key, o){
14168         if(arguments.length == 2){
14169             o = arguments[1];
14170             key = this.getKey(o);
14171         }
14172         if(index >= this.length){
14173             return this.add(key, o);
14174         }
14175         this.length++;
14176         this.items.splice(index, 0, o);
14177         if(typeof key != "undefined" && key != null){
14178             this.map[key] = o;
14179         }
14180         this.keys.splice(index, 0, key);
14181         this.fireEvent("add", index, o, key);
14182         return o;
14183     },
14184    
14185 /**
14186  * Removed an item from the collection.
14187  * @param {Object} o The item to remove.
14188  * @return {Object} The item removed.
14189  */
14190     remove : function(o){
14191         return this.removeAt(this.indexOf(o));
14192     },
14193    
14194 /**
14195  * Remove an item from a specified index in the collection.
14196  * @param {Number} index The index within the collection of the item to remove.
14197  */
14198     removeAt : function(index){
14199         if(index < this.length && index >= 0){
14200             this.length--;
14201             var o = this.items[index];
14202             this.items.splice(index, 1);
14203             var key = this.keys[index];
14204             if(typeof key != "undefined"){
14205                 delete this.map[key];
14206             }
14207             this.keys.splice(index, 1);
14208             this.fireEvent("remove", o, key);
14209         }
14210     },
14211    
14212 /**
14213  * Removed an item associated with the passed key fom the collection.
14214  * @param {String} key The key of the item to remove.
14215  */
14216     removeKey : function(key){
14217         return this.removeAt(this.indexOfKey(key));
14218     },
14219    
14220 /**
14221  * Returns the number of items in the collection.
14222  * @return {Number} the number of items in the collection.
14223  */
14224     getCount : function(){
14225         return this.length; 
14226     },
14227    
14228 /**
14229  * Returns index within the collection of the passed Object.
14230  * @param {Object} o The item to find the index of.
14231  * @return {Number} index of the item.
14232  */
14233     indexOf : function(o){
14234         if(!this.items.indexOf){
14235             for(var i = 0, len = this.items.length; i < len; i++){
14236                 if(this.items[i] == o) {
14237                     return i;
14238                 }
14239             }
14240             return -1;
14241         }else{
14242             return this.items.indexOf(o);
14243         }
14244     },
14245    
14246 /**
14247  * Returns index within the collection of the passed key.
14248  * @param {String} key The key to find the index of.
14249  * @return {Number} index of the key.
14250  */
14251     indexOfKey : function(key){
14252         if(!this.keys.indexOf){
14253             for(var i = 0, len = this.keys.length; i < len; i++){
14254                 if(this.keys[i] == key) {
14255                     return i;
14256                 }
14257             }
14258             return -1;
14259         }else{
14260             return this.keys.indexOf(key);
14261         }
14262     },
14263    
14264 /**
14265  * Returns the item associated with the passed key OR index. Key has priority over index.
14266  * @param {String/Number} key The key or index of the item.
14267  * @return {Object} The item associated with the passed key.
14268  */
14269     item : function(key){
14270         if (key === 'length') {
14271             return null;
14272         }
14273         var item = typeof this.map[key] != "undefined" ? this.map[key] : this.items[key];
14274         return typeof item != 'function' || this.allowFunctions ? item : null; // for prototype!
14275     },
14276     
14277 /**
14278  * Returns the item at the specified index.
14279  * @param {Number} index The index of the item.
14280  * @return {Object}
14281  */
14282     itemAt : function(index){
14283         return this.items[index];
14284     },
14285     
14286 /**
14287  * Returns the item associated with the passed key.
14288  * @param {String/Number} key The key of the item.
14289  * @return {Object} The item associated with the passed key.
14290  */
14291     key : function(key){
14292         return this.map[key];
14293     },
14294    
14295 /**
14296  * Returns true if the collection contains the passed Object as an item.
14297  * @param {Object} o  The Object to look for in the collection.
14298  * @return {Boolean} True if the collection contains the Object as an item.
14299  */
14300     contains : function(o){
14301         return this.indexOf(o) != -1;
14302     },
14303    
14304 /**
14305  * Returns true if the collection contains the passed Object as a key.
14306  * @param {String} key The key to look for in the collection.
14307  * @return {Boolean} True if the collection contains the Object as a key.
14308  */
14309     containsKey : function(key){
14310         return typeof this.map[key] != "undefined";
14311     },
14312    
14313 /**
14314  * Removes all items from the collection.
14315  */
14316     clear : function(){
14317         this.length = 0;
14318         this.items = [];
14319         this.keys = [];
14320         this.map = {};
14321         this.fireEvent("clear");
14322     },
14323    
14324 /**
14325  * Returns the first item in the collection.
14326  * @return {Object} the first item in the collection..
14327  */
14328     first : function(){
14329         return this.items[0]; 
14330     },
14331    
14332 /**
14333  * Returns the last item in the collection.
14334  * @return {Object} the last item in the collection..
14335  */
14336     last : function(){
14337         return this.items[this.length-1];   
14338     },
14339     
14340     _sort : function(property, dir, fn){
14341         var dsc = String(dir).toUpperCase() == "DESC" ? -1 : 1;
14342         fn = fn || function(a, b){
14343             return a-b;
14344         };
14345         var c = [], k = this.keys, items = this.items;
14346         for(var i = 0, len = items.length; i < len; i++){
14347             c[c.length] = {key: k[i], value: items[i], index: i};
14348         }
14349         c.sort(function(a, b){
14350             var v = fn(a[property], b[property]) * dsc;
14351             if(v == 0){
14352                 v = (a.index < b.index ? -1 : 1);
14353             }
14354             return v;
14355         });
14356         for(var i = 0, len = c.length; i < len; i++){
14357             items[i] = c[i].value;
14358             k[i] = c[i].key;
14359         }
14360         this.fireEvent("sort", this);
14361     },
14362     
14363     /**
14364      * Sorts this collection with the passed comparison function
14365      * @param {String} direction (optional) "ASC" or "DESC"
14366      * @param {Function} fn (optional) comparison function
14367      */
14368     sort : function(dir, fn){
14369         this._sort("value", dir, fn);
14370     },
14371     
14372     /**
14373      * Sorts this collection by keys
14374      * @param {String} direction (optional) "ASC" or "DESC"
14375      * @param {Function} fn (optional) a comparison function (defaults to case insensitive string)
14376      */
14377     keySort : function(dir, fn){
14378         this._sort("key", dir, fn || function(a, b){
14379             return String(a).toUpperCase()-String(b).toUpperCase();
14380         });
14381     },
14382     
14383     /**
14384      * Returns a range of items in this collection
14385      * @param {Number} startIndex (optional) defaults to 0
14386      * @param {Number} endIndex (optional) default to the last item
14387      * @return {Array} An array of items
14388      */
14389     getRange : function(start, end){
14390         var items = this.items;
14391         if(items.length < 1){
14392             return [];
14393         }
14394         start = start || 0;
14395         end = Math.min(typeof end == "undefined" ? this.length-1 : end, this.length-1);
14396         var r = [];
14397         if(start <= end){
14398             for(var i = start; i <= end; i++) {
14399                     r[r.length] = items[i];
14400             }
14401         }else{
14402             for(var i = start; i >= end; i--) {
14403                     r[r.length] = items[i];
14404             }
14405         }
14406         return r;
14407     },
14408         
14409     /**
14410      * Filter the <i>objects</i> in this collection by a specific property. 
14411      * Returns a new collection that has been filtered.
14412      * @param {String} property A property on your objects
14413      * @param {String/RegExp} value Either string that the property values 
14414      * should start with or a RegExp to test against the property
14415      * @return {MixedCollection} The new filtered collection
14416      */
14417     filter : function(property, value){
14418         if(!value.exec){ // not a regex
14419             value = String(value);
14420             if(value.length == 0){
14421                 return this.clone();
14422             }
14423             value = new RegExp("^" + Roo.escapeRe(value), "i");
14424         }
14425         return this.filterBy(function(o){
14426             return o && value.test(o[property]);
14427         });
14428         },
14429     
14430     /**
14431      * Filter by a function. * Returns a new collection that has been filtered.
14432      * The passed function will be called with each 
14433      * object in the collection. If the function returns true, the value is included 
14434      * otherwise it is filtered.
14435      * @param {Function} fn The function to be called, it will receive the args o (the object), k (the key)
14436      * @param {Object} scope (optional) The scope of the function (defaults to this) 
14437      * @return {MixedCollection} The new filtered collection
14438      */
14439     filterBy : function(fn, scope){
14440         var r = new Roo.util.MixedCollection();
14441         r.getKey = this.getKey;
14442         var k = this.keys, it = this.items;
14443         for(var i = 0, len = it.length; i < len; i++){
14444             if(fn.call(scope||this, it[i], k[i])){
14445                                 r.add(k[i], it[i]);
14446                         }
14447         }
14448         return r;
14449     },
14450     
14451     /**
14452      * Creates a duplicate of this collection
14453      * @return {MixedCollection}
14454      */
14455     clone : function(){
14456         var r = new Roo.util.MixedCollection();
14457         var k = this.keys, it = this.items;
14458         for(var i = 0, len = it.length; i < len; i++){
14459             r.add(k[i], it[i]);
14460         }
14461         r.getKey = this.getKey;
14462         return r;
14463     }
14464 });
14465 /**
14466  * Returns the item associated with the passed key or index.
14467  * @method
14468  * @param {String/Number} key The key or index of the item.
14469  * @return {Object} The item associated with the passed key.
14470  */
14471 Roo.util.MixedCollection.prototype.get = Roo.util.MixedCollection.prototype.item;/*
14472  * Based on:
14473  * Ext JS Library 1.1.1
14474  * Copyright(c) 2006-2007, Ext JS, LLC.
14475  *
14476  * Originally Released Under LGPL - original licence link has changed is not relivant.
14477  *
14478  * Fork - LGPL
14479  * <script type="text/javascript">
14480  */
14481 /**
14482  * @class Roo.util.JSON
14483  * Modified version of Douglas Crockford"s json.js that doesn"t
14484  * mess with the Object prototype 
14485  * http://www.json.org/js.html
14486  * @static
14487  */
14488 Roo.util.JSON = new (function(){
14489     var useHasOwn = {}.hasOwnProperty ? true : false;
14490     
14491     // crashes Safari in some instances
14492     //var validRE = /^("(\\.|[^"\\\n\r])*?"|[,:{}\[\]0-9.\-+Eaeflnr-u \n\r\t])+?$/;
14493     
14494     var pad = function(n) {
14495         return n < 10 ? "0" + n : n;
14496     };
14497     
14498     var m = {
14499         "\b": '\\b',
14500         "\t": '\\t',
14501         "\n": '\\n',
14502         "\f": '\\f',
14503         "\r": '\\r',
14504         '"' : '\\"',
14505         "\\": '\\\\'
14506     };
14507
14508     var encodeString = function(s){
14509         if (/["\\\x00-\x1f]/.test(s)) {
14510             return '"' + s.replace(/([\x00-\x1f\\"])/g, function(a, b) {
14511                 var c = m[b];
14512                 if(c){
14513                     return c;
14514                 }
14515                 c = b.charCodeAt();
14516                 return "\\u00" +
14517                     Math.floor(c / 16).toString(16) +
14518                     (c % 16).toString(16);
14519             }) + '"';
14520         }
14521         return '"' + s + '"';
14522     };
14523     
14524     var encodeArray = function(o){
14525         var a = ["["], b, i, l = o.length, v;
14526             for (i = 0; i < l; i += 1) {
14527                 v = o[i];
14528                 switch (typeof v) {
14529                     case "undefined":
14530                     case "function":
14531                     case "unknown":
14532                         break;
14533                     default:
14534                         if (b) {
14535                             a.push(',');
14536                         }
14537                         a.push(v === null ? "null" : Roo.util.JSON.encode(v));
14538                         b = true;
14539                 }
14540             }
14541             a.push("]");
14542             return a.join("");
14543     };
14544     
14545     var encodeDate = function(o){
14546         return '"' + o.getFullYear() + "-" +
14547                 pad(o.getMonth() + 1) + "-" +
14548                 pad(o.getDate()) + "T" +
14549                 pad(o.getHours()) + ":" +
14550                 pad(o.getMinutes()) + ":" +
14551                 pad(o.getSeconds()) + '"';
14552     };
14553     
14554     /**
14555      * Encodes an Object, Array or other value
14556      * @param {Mixed} o The variable to encode
14557      * @return {String} The JSON string
14558      */
14559     this.encode = function(o)
14560     {
14561         // should this be extended to fully wrap stringify..
14562         
14563         if(typeof o == "undefined" || o === null){
14564             return "null";
14565         }else if(o instanceof Array){
14566             return encodeArray(o);
14567         }else if(o instanceof Date){
14568             return encodeDate(o);
14569         }else if(typeof o == "string"){
14570             return encodeString(o);
14571         }else if(typeof o == "number"){
14572             return isFinite(o) ? String(o) : "null";
14573         }else if(typeof o == "boolean"){
14574             return String(o);
14575         }else {
14576             var a = ["{"], b, i, v;
14577             for (i in o) {
14578                 if(!useHasOwn || o.hasOwnProperty(i)) {
14579                     v = o[i];
14580                     switch (typeof v) {
14581                     case "undefined":
14582                     case "function":
14583                     case "unknown":
14584                         break;
14585                     default:
14586                         if(b){
14587                             a.push(',');
14588                         }
14589                         a.push(this.encode(i), ":",
14590                                 v === null ? "null" : this.encode(v));
14591                         b = true;
14592                     }
14593                 }
14594             }
14595             a.push("}");
14596             return a.join("");
14597         }
14598     };
14599     
14600     /**
14601      * Decodes (parses) a JSON string to an object. If the JSON is invalid, this function throws a SyntaxError.
14602      * @param {String} json The JSON string
14603      * @return {Object} The resulting object
14604      */
14605     this.decode = function(json){
14606         
14607         return  /** eval:var:json */ eval("(" + json + ')');
14608     };
14609 })();
14610 /** 
14611  * Shorthand for {@link Roo.util.JSON#encode}
14612  * @member Roo encode 
14613  * @method */
14614 Roo.encode = typeof(JSON) != 'undefined' && JSON.stringify ? JSON.stringify : Roo.util.JSON.encode;
14615 /** 
14616  * Shorthand for {@link Roo.util.JSON#decode}
14617  * @member Roo decode 
14618  * @method */
14619 Roo.decode = typeof(JSON) != 'undefined' && JSON.parse ? JSON.parse : Roo.util.JSON.decode;
14620 /*
14621  * Based on:
14622  * Ext JS Library 1.1.1
14623  * Copyright(c) 2006-2007, Ext JS, LLC.
14624  *
14625  * Originally Released Under LGPL - original licence link has changed is not relivant.
14626  *
14627  * Fork - LGPL
14628  * <script type="text/javascript">
14629  */
14630  
14631 /**
14632  * @class Roo.util.Format
14633  * Reusable data formatting functions
14634  * @static
14635  */
14636 Roo.util.Format = function(){
14637     var trimRe = /^\s+|\s+$/g;
14638     return {
14639         /**
14640          * Truncate a string and add an ellipsis ('...') to the end if it exceeds the specified length
14641          * @param {String} value The string to truncate
14642          * @param {Number} length The maximum length to allow before truncating
14643          * @return {String} The converted text
14644          */
14645         ellipsis : function(value, len){
14646             if(value && value.length > len){
14647                 return value.substr(0, len-3)+"...";
14648             }
14649             return value;
14650         },
14651
14652         /**
14653          * Checks a reference and converts it to empty string if it is undefined
14654          * @param {Mixed} value Reference to check
14655          * @return {Mixed} Empty string if converted, otherwise the original value
14656          */
14657         undef : function(value){
14658             return typeof value != "undefined" ? value : "";
14659         },
14660
14661         /**
14662          * Convert certain characters (&, <, >, and ') to their HTML character equivalents for literal display in web pages.
14663          * @param {String} value The string to encode
14664          * @return {String} The encoded text
14665          */
14666         htmlEncode : function(value){
14667             return !value ? value : String(value).replace(/&/g, "&amp;").replace(/>/g, "&gt;").replace(/</g, "&lt;").replace(/"/g, "&quot;");
14668         },
14669
14670         /**
14671          * Convert certain characters (&, <, >, and ') from their HTML character equivalents.
14672          * @param {String} value The string to decode
14673          * @return {String} The decoded text
14674          */
14675         htmlDecode : function(value){
14676             return !value ? value : String(value).replace(/&amp;/g, "&").replace(/&gt;/g, ">").replace(/&lt;/g, "<").replace(/&quot;/g, '"');
14677         },
14678
14679         /**
14680          * Trims any whitespace from either side of a string
14681          * @param {String} value The text to trim
14682          * @return {String} The trimmed text
14683          */
14684         trim : function(value){
14685             return String(value).replace(trimRe, "");
14686         },
14687
14688         /**
14689          * Returns a substring from within an original string
14690          * @param {String} value The original text
14691          * @param {Number} start The start index of the substring
14692          * @param {Number} length The length of the substring
14693          * @return {String} The substring
14694          */
14695         substr : function(value, start, length){
14696             return String(value).substr(start, length);
14697         },
14698
14699         /**
14700          * Converts a string to all lower case letters
14701          * @param {String} value The text to convert
14702          * @return {String} The converted text
14703          */
14704         lowercase : function(value){
14705             return String(value).toLowerCase();
14706         },
14707
14708         /**
14709          * Converts a string to all upper case letters
14710          * @param {String} value The text to convert
14711          * @return {String} The converted text
14712          */
14713         uppercase : function(value){
14714             return String(value).toUpperCase();
14715         },
14716
14717         /**
14718          * Converts the first character only of a string to upper case
14719          * @param {String} value The text to convert
14720          * @return {String} The converted text
14721          */
14722         capitalize : function(value){
14723             return !value ? value : value.charAt(0).toUpperCase() + value.substr(1).toLowerCase();
14724         },
14725
14726         // private
14727         call : function(value, fn){
14728             if(arguments.length > 2){
14729                 var args = Array.prototype.slice.call(arguments, 2);
14730                 args.unshift(value);
14731                  
14732                 return /** eval:var:value */  eval(fn).apply(window, args);
14733             }else{
14734                 /** eval:var:value */
14735                 return /** eval:var:value */ eval(fn).call(window, value);
14736             }
14737         },
14738
14739        
14740         /**
14741          * safer version of Math.toFixed..??/
14742          * @param {Number/String} value The numeric value to format
14743          * @param {Number/String} value Decimal places 
14744          * @return {String} The formatted currency string
14745          */
14746         toFixed : function(v, n)
14747         {
14748             // why not use to fixed - precision is buggered???
14749             if (!n) {
14750                 return Math.round(v-0);
14751             }
14752             var fact = Math.pow(10,n+1);
14753             v = (Math.round((v-0)*fact))/fact;
14754             var z = (''+fact).substring(2);
14755             if (v == Math.floor(v)) {
14756                 return Math.floor(v) + '.' + z;
14757             }
14758             
14759             // now just padd decimals..
14760             var ps = String(v).split('.');
14761             var fd = (ps[1] + z);
14762             var r = fd.substring(0,n); 
14763             var rm = fd.substring(n); 
14764             if (rm < 5) {
14765                 return ps[0] + '.' + r;
14766             }
14767             r*=1; // turn it into a number;
14768             r++;
14769             if (String(r).length != n) {
14770                 ps[0]*=1;
14771                 ps[0]++;
14772                 r = String(r).substring(1); // chop the end off.
14773             }
14774             
14775             return ps[0] + '.' + r;
14776              
14777         },
14778         
14779         /**
14780          * Format a number as US currency
14781          * @param {Number/String} value The numeric value to format
14782          * @return {String} The formatted currency string
14783          */
14784         usMoney : function(v){
14785             return '$' + Roo.util.Format.number(v);
14786         },
14787         
14788         /**
14789          * Format a number
14790          * eventually this should probably emulate php's number_format
14791          * @param {Number/String} value The numeric value to format
14792          * @param {Number} decimals number of decimal places
14793          * @param {String} delimiter for thousands (default comma)
14794          * @return {String} The formatted currency string
14795          */
14796         number : function(v, decimals, thousandsDelimiter)
14797         {
14798             // multiply and round.
14799             decimals = typeof(decimals) == 'undefined' ? 2 : decimals;
14800             thousandsDelimiter = typeof(thousandsDelimiter) == 'undefined' ? ',' : thousandsDelimiter;
14801             
14802             var mul = Math.pow(10, decimals);
14803             var zero = String(mul).substring(1);
14804             v = (Math.round((v-0)*mul))/mul;
14805             
14806             // if it's '0' number.. then
14807             
14808             //v = (v == Math.floor(v)) ? v + "." + zero : ((v*10 == Math.floor(v*10)) ? v + "0" : v);
14809             v = String(v);
14810             var ps = v.split('.');
14811             var whole = ps[0];
14812             
14813             var r = /(\d+)(\d{3})/;
14814             // add comma's
14815             
14816             if(thousandsDelimiter.length != 0) {
14817                 whole = whole.replace(/\B(?=(\d{3})+(?!\d))/g, thousandsDelimiter );
14818             } 
14819             
14820             var sub = ps[1] ?
14821                     // has decimals..
14822                     (decimals ?  ('.'+ ps[1] + zero.substring(ps[1].length)) : '') :
14823                     // does not have decimals
14824                     (decimals ? ('.' + zero) : '');
14825             
14826             
14827             return whole + sub ;
14828         },
14829         
14830         /**
14831          * Parse a value into a formatted date using the specified format pattern.
14832          * @param {Mixed} value The value to format
14833          * @param {String} format (optional) Any valid date format string (defaults to 'm/d/Y')
14834          * @return {String} The formatted date string
14835          */
14836         date : function(v, format){
14837             if(!v){
14838                 return "";
14839             }
14840             if(!(v instanceof Date)){
14841                 v = new Date(Date.parse(v));
14842             }
14843             return v.dateFormat(format || Roo.util.Format.defaults.date);
14844         },
14845
14846         /**
14847          * Returns a date rendering function that can be reused to apply a date format multiple times efficiently
14848          * @param {String} format Any valid date format string
14849          * @return {Function} The date formatting function
14850          */
14851         dateRenderer : function(format){
14852             return function(v){
14853                 return Roo.util.Format.date(v, format);  
14854             };
14855         },
14856
14857         // private
14858         stripTagsRE : /<\/?[^>]+>/gi,
14859         
14860         /**
14861          * Strips all HTML tags
14862          * @param {Mixed} value The text from which to strip tags
14863          * @return {String} The stripped text
14864          */
14865         stripTags : function(v){
14866             return !v ? v : String(v).replace(this.stripTagsRE, "");
14867         },
14868         
14869         /**
14870          * Size in Mb,Gb etc.
14871          * @param {Number} value The number to be formated
14872          * @param {number} decimals how many decimal places
14873          * @return {String} the formated string
14874          */
14875         size : function(value, decimals)
14876         {
14877             var sizes = ['b', 'k', 'M', 'G', 'T'];
14878             if (value == 0) {
14879                 return 0;
14880             }
14881             var i = parseInt(Math.floor(Math.log(value) / Math.log(1024)));
14882             return Roo.util.Format.number(value/ Math.pow(1024, i) ,decimals)   + sizes[i];
14883         }
14884         
14885         
14886         
14887     };
14888 }();
14889 Roo.util.Format.defaults = {
14890     date : 'd/M/Y'
14891 };/*
14892  * Based on:
14893  * Ext JS Library 1.1.1
14894  * Copyright(c) 2006-2007, Ext JS, LLC.
14895  *
14896  * Originally Released Under LGPL - original licence link has changed is not relivant.
14897  *
14898  * Fork - LGPL
14899  * <script type="text/javascript">
14900  */
14901
14902
14903  
14904
14905 /**
14906  * @class Roo.MasterTemplate
14907  * @extends Roo.Template
14908  * Provides a template that can have child templates. The syntax is:
14909 <pre><code>
14910 var t = new Roo.MasterTemplate(
14911         '&lt;select name="{name}"&gt;',
14912                 '&lt;tpl name="options"&gt;&lt;option value="{value:trim}"&gt;{text:ellipsis(10)}&lt;/option&gt;&lt;/tpl&gt;',
14913         '&lt;/select&gt;'
14914 );
14915 t.add('options', {value: 'foo', text: 'bar'});
14916 // or you can add multiple child elements in one shot
14917 t.addAll('options', [
14918     {value: 'foo', text: 'bar'},
14919     {value: 'foo2', text: 'bar2'},
14920     {value: 'foo3', text: 'bar3'}
14921 ]);
14922 // then append, applying the master template values
14923 t.append('my-form', {name: 'my-select'});
14924 </code></pre>
14925 * A name attribute for the child template is not required if you have only one child
14926 * template or you want to refer to them by index.
14927  */
14928 Roo.MasterTemplate = function(){
14929     Roo.MasterTemplate.superclass.constructor.apply(this, arguments);
14930     this.originalHtml = this.html;
14931     var st = {};
14932     var m, re = this.subTemplateRe;
14933     re.lastIndex = 0;
14934     var subIndex = 0;
14935     while(m = re.exec(this.html)){
14936         var name = m[1], content = m[2];
14937         st[subIndex] = {
14938             name: name,
14939             index: subIndex,
14940             buffer: [],
14941             tpl : new Roo.Template(content)
14942         };
14943         if(name){
14944             st[name] = st[subIndex];
14945         }
14946         st[subIndex].tpl.compile();
14947         st[subIndex].tpl.call = this.call.createDelegate(this);
14948         subIndex++;
14949     }
14950     this.subCount = subIndex;
14951     this.subs = st;
14952 };
14953 Roo.extend(Roo.MasterTemplate, Roo.Template, {
14954     /**
14955     * The regular expression used to match sub templates
14956     * @type RegExp
14957     * @property
14958     */
14959     subTemplateRe : /<tpl(?:\sname="([\w-]+)")?>((?:.|\n)*?)<\/tpl>/gi,
14960
14961     /**
14962      * Applies the passed values to a child template.
14963      * @param {String/Number} name (optional) The name or index of the child template
14964      * @param {Array/Object} values The values to be applied to the template
14965      * @return {MasterTemplate} this
14966      */
14967      add : function(name, values){
14968         if(arguments.length == 1){
14969             values = arguments[0];
14970             name = 0;
14971         }
14972         var s = this.subs[name];
14973         s.buffer[s.buffer.length] = s.tpl.apply(values);
14974         return this;
14975     },
14976
14977     /**
14978      * Applies all the passed values to a child template.
14979      * @param {String/Number} name (optional) The name or index of the child template
14980      * @param {Array} values The values to be applied to the template, this should be an array of objects.
14981      * @param {Boolean} reset (optional) True to reset the template first
14982      * @return {MasterTemplate} this
14983      */
14984     fill : function(name, values, reset){
14985         var a = arguments;
14986         if(a.length == 1 || (a.length == 2 && typeof a[1] == "boolean")){
14987             values = a[0];
14988             name = 0;
14989             reset = a[1];
14990         }
14991         if(reset){
14992             this.reset();
14993         }
14994         for(var i = 0, len = values.length; i < len; i++){
14995             this.add(name, values[i]);
14996         }
14997         return this;
14998     },
14999
15000     /**
15001      * Resets the template for reuse
15002      * @return {MasterTemplate} this
15003      */
15004      reset : function(){
15005         var s = this.subs;
15006         for(var i = 0; i < this.subCount; i++){
15007             s[i].buffer = [];
15008         }
15009         return this;
15010     },
15011
15012     applyTemplate : function(values){
15013         var s = this.subs;
15014         var replaceIndex = -1;
15015         this.html = this.originalHtml.replace(this.subTemplateRe, function(m, name){
15016             return s[++replaceIndex].buffer.join("");
15017         });
15018         return Roo.MasterTemplate.superclass.applyTemplate.call(this, values);
15019     },
15020
15021     apply : function(){
15022         return this.applyTemplate.apply(this, arguments);
15023     },
15024
15025     compile : function(){return this;}
15026 });
15027
15028 /**
15029  * Alias for fill().
15030  * @method
15031  */
15032 Roo.MasterTemplate.prototype.addAll = Roo.MasterTemplate.prototype.fill;
15033  /**
15034  * Creates a template from the passed element's value (display:none textarea, preferred) or innerHTML. e.g.
15035  * var tpl = Roo.MasterTemplate.from('element-id');
15036  * @param {String/HTMLElement} el
15037  * @param {Object} config
15038  * @static
15039  */
15040 Roo.MasterTemplate.from = function(el, config){
15041     el = Roo.getDom(el);
15042     return new Roo.MasterTemplate(el.value || el.innerHTML, config || '');
15043 };/*
15044  * Based on:
15045  * Ext JS Library 1.1.1
15046  * Copyright(c) 2006-2007, Ext JS, LLC.
15047  *
15048  * Originally Released Under LGPL - original licence link has changed is not relivant.
15049  *
15050  * Fork - LGPL
15051  * <script type="text/javascript">
15052  */
15053
15054  
15055 /**
15056  * @class Roo.util.CSS
15057  * Utility class for manipulating CSS rules
15058  * @static
15059
15060  */
15061 Roo.util.CSS = function(){
15062         var rules = null;
15063         var doc = document;
15064
15065     var camelRe = /(-[a-z])/gi;
15066     var camelFn = function(m, a){ return a.charAt(1).toUpperCase(); };
15067
15068    return {
15069    /**
15070     * Very simple dynamic creation of stylesheets from a text blob of rules.  The text will wrapped in a style
15071     * tag and appended to the HEAD of the document.
15072     * @param {String|Object} cssText The text containing the css rules
15073     * @param {String} id An id to add to the stylesheet for later removal
15074     * @return {StyleSheet}
15075     */
15076     createStyleSheet : function(cssText, id){
15077         var ss;
15078         var head = doc.getElementsByTagName("head")[0];
15079         var nrules = doc.createElement("style");
15080         nrules.setAttribute("type", "text/css");
15081         if(id){
15082             nrules.setAttribute("id", id);
15083         }
15084         if (typeof(cssText) != 'string') {
15085             // support object maps..
15086             // not sure if this a good idea.. 
15087             // perhaps it should be merged with the general css handling
15088             // and handle js style props.
15089             var cssTextNew = [];
15090             for(var n in cssText) {
15091                 var citems = [];
15092                 for(var k in cssText[n]) {
15093                     citems.push( k + ' : ' +cssText[n][k] + ';' );
15094                 }
15095                 cssTextNew.push( n + ' { ' + citems.join(' ') + '} ');
15096                 
15097             }
15098             cssText = cssTextNew.join("\n");
15099             
15100         }
15101        
15102        
15103        if(Roo.isIE){
15104            head.appendChild(nrules);
15105            ss = nrules.styleSheet;
15106            ss.cssText = cssText;
15107        }else{
15108            try{
15109                 nrules.appendChild(doc.createTextNode(cssText));
15110            }catch(e){
15111                nrules.cssText = cssText; 
15112            }
15113            head.appendChild(nrules);
15114            ss = nrules.styleSheet ? nrules.styleSheet : (nrules.sheet || doc.styleSheets[doc.styleSheets.length-1]);
15115        }
15116        this.cacheStyleSheet(ss);
15117        return ss;
15118    },
15119
15120    /**
15121     * Removes a style or link tag by id
15122     * @param {String} id The id of the tag
15123     */
15124    removeStyleSheet : function(id){
15125        var existing = doc.getElementById(id);
15126        if(existing){
15127            existing.parentNode.removeChild(existing);
15128        }
15129    },
15130
15131    /**
15132     * Dynamically swaps an existing stylesheet reference for a new one
15133     * @param {String} id The id of an existing link tag to remove
15134     * @param {String} url The href of the new stylesheet to include
15135     */
15136    swapStyleSheet : function(id, url){
15137        this.removeStyleSheet(id);
15138        var ss = doc.createElement("link");
15139        ss.setAttribute("rel", "stylesheet");
15140        ss.setAttribute("type", "text/css");
15141        ss.setAttribute("id", id);
15142        ss.setAttribute("href", url);
15143        doc.getElementsByTagName("head")[0].appendChild(ss);
15144    },
15145    
15146    /**
15147     * Refresh the rule cache if you have dynamically added stylesheets
15148     * @return {Object} An object (hash) of rules indexed by selector
15149     */
15150    refreshCache : function(){
15151        return this.getRules(true);
15152    },
15153
15154    // private
15155    cacheStyleSheet : function(stylesheet){
15156        if(!rules){
15157            rules = {};
15158        }
15159        try{// try catch for cross domain access issue
15160            var ssRules = stylesheet.cssRules || stylesheet.rules;
15161            for(var j = ssRules.length-1; j >= 0; --j){
15162                rules[ssRules[j].selectorText] = ssRules[j];
15163            }
15164        }catch(e){}
15165    },
15166    
15167    /**
15168     * Gets all css rules for the document
15169     * @param {Boolean} refreshCache true to refresh the internal cache
15170     * @return {Object} An object (hash) of rules indexed by selector
15171     */
15172    getRules : function(refreshCache){
15173                 if(rules == null || refreshCache){
15174                         rules = {};
15175                         var ds = doc.styleSheets;
15176                         for(var i =0, len = ds.length; i < len; i++){
15177                             try{
15178                         this.cacheStyleSheet(ds[i]);
15179                     }catch(e){} 
15180                 }
15181                 }
15182                 return rules;
15183         },
15184         
15185         /**
15186     * Gets an an individual CSS rule by selector(s)
15187     * @param {String/Array} selector The CSS selector or an array of selectors to try. The first selector that is found is returned.
15188     * @param {Boolean} refreshCache true to refresh the internal cache if you have recently updated any rules or added styles dynamically
15189     * @return {CSSRule} The CSS rule or null if one is not found
15190     */
15191    getRule : function(selector, refreshCache){
15192                 var rs = this.getRules(refreshCache);
15193                 if(!(selector instanceof Array)){
15194                     return rs[selector];
15195                 }
15196                 for(var i = 0; i < selector.length; i++){
15197                         if(rs[selector[i]]){
15198                                 return rs[selector[i]];
15199                         }
15200                 }
15201                 return null;
15202         },
15203         
15204         
15205         /**
15206     * Updates a rule property
15207     * @param {String/Array} selector If it's an array it tries each selector until it finds one. Stops immediately once one is found.
15208     * @param {String} property The css property
15209     * @param {String} value The new value for the property
15210     * @return {Boolean} true If a rule was found and updated
15211     */
15212    updateRule : function(selector, property, value){
15213                 if(!(selector instanceof Array)){
15214                         var rule = this.getRule(selector);
15215                         if(rule){
15216                                 rule.style[property.replace(camelRe, camelFn)] = value;
15217                                 return true;
15218                         }
15219                 }else{
15220                         for(var i = 0; i < selector.length; i++){
15221                                 if(this.updateRule(selector[i], property, value)){
15222                                         return true;
15223                                 }
15224                         }
15225                 }
15226                 return false;
15227         }
15228    };   
15229 }();/*
15230  * Based on:
15231  * Ext JS Library 1.1.1
15232  * Copyright(c) 2006-2007, Ext JS, LLC.
15233  *
15234  * Originally Released Under LGPL - original licence link has changed is not relivant.
15235  *
15236  * Fork - LGPL
15237  * <script type="text/javascript">
15238  */
15239
15240  
15241
15242 /**
15243  * @class Roo.util.ClickRepeater
15244  * @extends Roo.util.Observable
15245  * 
15246  * A wrapper class which can be applied to any element. Fires a "click" event while the
15247  * mouse is pressed. The interval between firings may be specified in the config but
15248  * defaults to 10 milliseconds.
15249  * 
15250  * Optionally, a CSS class may be applied to the element during the time it is pressed.
15251  * 
15252  * @cfg {String/HTMLElement/Element} el The element to act as a button.
15253  * @cfg {Number} delay The initial delay before the repeating event begins firing.
15254  * Similar to an autorepeat key delay.
15255  * @cfg {Number} interval The interval between firings of the "click" event. Default 10 ms.
15256  * @cfg {String} pressClass A CSS class name to be applied to the element while pressed.
15257  * @cfg {Boolean} accelerate True if autorepeating should start slowly and accelerate.
15258  *           "interval" and "delay" are ignored. "immediate" is honored.
15259  * @cfg {Boolean} preventDefault True to prevent the default click event
15260  * @cfg {Boolean} stopDefault True to stop the default click event
15261  * 
15262  * @history
15263  *     2007-02-02 jvs Original code contributed by Nige "Animal" White
15264  *     2007-02-02 jvs Renamed to ClickRepeater
15265  *   2007-02-03 jvs Modifications for FF Mac and Safari 
15266  *
15267  *  @constructor
15268  * @param {String/HTMLElement/Element} el The element to listen on
15269  * @param {Object} config
15270  **/
15271 Roo.util.ClickRepeater = function(el, config)
15272 {
15273     this.el = Roo.get(el);
15274     this.el.unselectable();
15275
15276     Roo.apply(this, config);
15277
15278     this.addEvents({
15279     /**
15280      * @event mousedown
15281      * Fires when the mouse button is depressed.
15282      * @param {Roo.util.ClickRepeater} this
15283      */
15284         "mousedown" : true,
15285     /**
15286      * @event click
15287      * Fires on a specified interval during the time the element is pressed.
15288      * @param {Roo.util.ClickRepeater} this
15289      */
15290         "click" : true,
15291     /**
15292      * @event mouseup
15293      * Fires when the mouse key is released.
15294      * @param {Roo.util.ClickRepeater} this
15295      */
15296         "mouseup" : true
15297     });
15298
15299     this.el.on("mousedown", this.handleMouseDown, this);
15300     if(this.preventDefault || this.stopDefault){
15301         this.el.on("click", function(e){
15302             if(this.preventDefault){
15303                 e.preventDefault();
15304             }
15305             if(this.stopDefault){
15306                 e.stopEvent();
15307             }
15308         }, this);
15309     }
15310
15311     // allow inline handler
15312     if(this.handler){
15313         this.on("click", this.handler,  this.scope || this);
15314     }
15315
15316     Roo.util.ClickRepeater.superclass.constructor.call(this);
15317 };
15318
15319 Roo.extend(Roo.util.ClickRepeater, Roo.util.Observable, {
15320     interval : 20,
15321     delay: 250,
15322     preventDefault : true,
15323     stopDefault : false,
15324     timer : 0,
15325
15326     // private
15327     handleMouseDown : function(){
15328         clearTimeout(this.timer);
15329         this.el.blur();
15330         if(this.pressClass){
15331             this.el.addClass(this.pressClass);
15332         }
15333         this.mousedownTime = new Date();
15334
15335         Roo.get(document).on("mouseup", this.handleMouseUp, this);
15336         this.el.on("mouseout", this.handleMouseOut, this);
15337
15338         this.fireEvent("mousedown", this);
15339         this.fireEvent("click", this);
15340         
15341         this.timer = this.click.defer(this.delay || this.interval, this);
15342     },
15343
15344     // private
15345     click : function(){
15346         this.fireEvent("click", this);
15347         this.timer = this.click.defer(this.getInterval(), this);
15348     },
15349
15350     // private
15351     getInterval: function(){
15352         if(!this.accelerate){
15353             return this.interval;
15354         }
15355         var pressTime = this.mousedownTime.getElapsed();
15356         if(pressTime < 500){
15357             return 400;
15358         }else if(pressTime < 1700){
15359             return 320;
15360         }else if(pressTime < 2600){
15361             return 250;
15362         }else if(pressTime < 3500){
15363             return 180;
15364         }else if(pressTime < 4400){
15365             return 140;
15366         }else if(pressTime < 5300){
15367             return 80;
15368         }else if(pressTime < 6200){
15369             return 50;
15370         }else{
15371             return 10;
15372         }
15373     },
15374
15375     // private
15376     handleMouseOut : function(){
15377         clearTimeout(this.timer);
15378         if(this.pressClass){
15379             this.el.removeClass(this.pressClass);
15380         }
15381         this.el.on("mouseover", this.handleMouseReturn, this);
15382     },
15383
15384     // private
15385     handleMouseReturn : function(){
15386         this.el.un("mouseover", this.handleMouseReturn);
15387         if(this.pressClass){
15388             this.el.addClass(this.pressClass);
15389         }
15390         this.click();
15391     },
15392
15393     // private
15394     handleMouseUp : function(){
15395         clearTimeout(this.timer);
15396         this.el.un("mouseover", this.handleMouseReturn);
15397         this.el.un("mouseout", this.handleMouseOut);
15398         Roo.get(document).un("mouseup", this.handleMouseUp);
15399         this.el.removeClass(this.pressClass);
15400         this.fireEvent("mouseup", this);
15401     }
15402 });/**
15403  * @class Roo.util.Clipboard
15404  * @static
15405  * 
15406  * Clipboard UTILS
15407  * 
15408  **/
15409 Roo.util.Clipboard = {
15410     /**
15411      * Writes a string to the clipboard - using the Clipboard API if https, otherwise using text area.
15412      * @param {String} text to copy to clipboard
15413      */
15414     write : function(text) {
15415         // navigator clipboard api needs a secure context (https)
15416         if (navigator.clipboard && window.isSecureContext) {
15417             // navigator clipboard api method'
15418             navigator.clipboard.writeText(text);
15419             return ;
15420         } 
15421         // text area method
15422         var ta = document.createElement("textarea");
15423         ta.value = text;
15424         // make the textarea out of viewport
15425         ta.style.position = "fixed";
15426         ta.style.left = "-999999px";
15427         ta.style.top = "-999999px";
15428         document.body.appendChild(ta);
15429         ta.focus();
15430         ta.select();
15431         document.execCommand('copy');
15432         (function() {
15433             ta.remove();
15434         }).defer(100);
15435         
15436     }
15437         
15438 }
15439     /*
15440  * Based on:
15441  * Ext JS Library 1.1.1
15442  * Copyright(c) 2006-2007, Ext JS, LLC.
15443  *
15444  * Originally Released Under LGPL - original licence link has changed is not relivant.
15445  *
15446  * Fork - LGPL
15447  * <script type="text/javascript">
15448  */
15449
15450  
15451 /**
15452  * @class Roo.KeyNav
15453  * <p>Provides a convenient wrapper for normalized keyboard navigation.  KeyNav allows you to bind
15454  * navigation keys to function calls that will get called when the keys are pressed, providing an easy
15455  * way to implement custom navigation schemes for any UI component.</p>
15456  * <p>The following are all of the possible keys that can be implemented: enter, left, right, up, down, tab, esc,
15457  * pageUp, pageDown, del, home, end.  Usage:</p>
15458  <pre><code>
15459 var nav = new Roo.KeyNav("my-element", {
15460     "left" : function(e){
15461         this.moveLeft(e.ctrlKey);
15462     },
15463     "right" : function(e){
15464         this.moveRight(e.ctrlKey);
15465     },
15466     "enter" : function(e){
15467         this.save();
15468     },
15469     scope : this
15470 });
15471 </code></pre>
15472  * @constructor
15473  * @param {String/HTMLElement/Roo.Element} el The element to bind to
15474  * @param {Object} config The config
15475  */
15476 Roo.KeyNav = function(el, config){
15477     this.el = Roo.get(el);
15478     Roo.apply(this, config);
15479     if(!this.disabled){
15480         this.disabled = true;
15481         this.enable();
15482     }
15483 };
15484
15485 Roo.KeyNav.prototype = {
15486     /**
15487      * @cfg {Boolean} disabled
15488      * True to disable this KeyNav instance (defaults to false)
15489      */
15490     disabled : false,
15491     /**
15492      * @cfg {String} defaultEventAction
15493      * The method to call on the {@link Roo.EventObject} after this KeyNav intercepts a key.  Valid values are
15494      * {@link Roo.EventObject#stopEvent}, {@link Roo.EventObject#preventDefault} and
15495      * {@link Roo.EventObject#stopPropagation} (defaults to 'stopEvent')
15496      */
15497     defaultEventAction: "stopEvent",
15498     /**
15499      * @cfg {Boolean} forceKeyDown
15500      * Handle the keydown event instead of keypress (defaults to false).  KeyNav automatically does this for IE since
15501      * IE does not propagate special keys on keypress, but setting this to true will force other browsers to also
15502      * handle keydown instead of keypress.
15503      */
15504     forceKeyDown : false,
15505
15506     // private
15507     prepareEvent : function(e){
15508         var k = e.getKey();
15509         var h = this.keyToHandler[k];
15510         //if(h && this[h]){
15511         //    e.stopPropagation();
15512         //}
15513         if(Roo.isSafari && h && k >= 37 && k <= 40){
15514             e.stopEvent();
15515         }
15516     },
15517
15518     // private
15519     relay : function(e){
15520         var k = e.getKey();
15521         var h = this.keyToHandler[k];
15522         if(h && this[h]){
15523             if(this.doRelay(e, this[h], h) !== true){
15524                 e[this.defaultEventAction]();
15525             }
15526         }
15527     },
15528
15529     // private
15530     doRelay : function(e, h, hname){
15531         return h.call(this.scope || this, e);
15532     },
15533
15534     // possible handlers
15535     enter : false,
15536     left : false,
15537     right : false,
15538     up : false,
15539     down : false,
15540     tab : false,
15541     esc : false,
15542     pageUp : false,
15543     pageDown : false,
15544     del : false,
15545     home : false,
15546     end : false,
15547
15548     // quick lookup hash
15549     keyToHandler : {
15550         37 : "left",
15551         39 : "right",
15552         38 : "up",
15553         40 : "down",
15554         33 : "pageUp",
15555         34 : "pageDown",
15556         46 : "del",
15557         36 : "home",
15558         35 : "end",
15559         13 : "enter",
15560         27 : "esc",
15561         9  : "tab"
15562     },
15563
15564         /**
15565          * Enable this KeyNav
15566          */
15567         enable: function(){
15568                 if(this.disabled){
15569             // ie won't do special keys on keypress, no one else will repeat keys with keydown
15570             // the EventObject will normalize Safari automatically
15571             if(this.forceKeyDown || Roo.isIE || Roo.isAir){
15572                 this.el.on("keydown", this.relay,  this);
15573             }else{
15574                 this.el.on("keydown", this.prepareEvent,  this);
15575                 this.el.on("keypress", this.relay,  this);
15576             }
15577                     this.disabled = false;
15578                 }
15579         },
15580
15581         /**
15582          * Disable this KeyNav
15583          */
15584         disable: function(){
15585                 if(!this.disabled){
15586                     if(this.forceKeyDown || Roo.isIE || Roo.isAir){
15587                 this.el.un("keydown", this.relay);
15588             }else{
15589                 this.el.un("keydown", this.prepareEvent);
15590                 this.el.un("keypress", this.relay);
15591             }
15592                     this.disabled = true;
15593                 }
15594         }
15595 };/*
15596  * Based on:
15597  * Ext JS Library 1.1.1
15598  * Copyright(c) 2006-2007, Ext JS, LLC.
15599  *
15600  * Originally Released Under LGPL - original licence link has changed is not relivant.
15601  *
15602  * Fork - LGPL
15603  * <script type="text/javascript">
15604  */
15605
15606  
15607 /**
15608  * @class Roo.KeyMap
15609  * Handles mapping keys to actions for an element. One key map can be used for multiple actions.
15610  * The constructor accepts the same config object as defined by {@link #addBinding}.
15611  * If you bind a callback function to a KeyMap, anytime the KeyMap handles an expected key
15612  * combination it will call the function with this signature (if the match is a multi-key
15613  * combination the callback will still be called only once): (String key, Roo.EventObject e)
15614  * A KeyMap can also handle a string representation of keys.<br />
15615  * Usage:
15616  <pre><code>
15617 // map one key by key code
15618 var map = new Roo.KeyMap("my-element", {
15619     key: 13, // or Roo.EventObject.ENTER
15620     fn: myHandler,
15621     scope: myObject
15622 });
15623
15624 // map multiple keys to one action by string
15625 var map = new Roo.KeyMap("my-element", {
15626     key: "a\r\n\t",
15627     fn: myHandler,
15628     scope: myObject
15629 });
15630
15631 // map multiple keys to multiple actions by strings and array of codes
15632 var map = new Roo.KeyMap("my-element", [
15633     {
15634         key: [10,13],
15635         fn: function(){ alert("Return was pressed"); }
15636     }, {
15637         key: "abc",
15638         fn: function(){ alert('a, b or c was pressed'); }
15639     }, {
15640         key: "\t",
15641         ctrl:true,
15642         shift:true,
15643         fn: function(){ alert('Control + shift + tab was pressed.'); }
15644     }
15645 ]);
15646 </code></pre>
15647  * <b>Note: A KeyMap starts enabled</b>
15648  * @constructor
15649  * @param {String/HTMLElement/Roo.Element} el The element to bind to
15650  * @param {Object} config The config (see {@link #addBinding})
15651  * @param {String} eventName (optional) The event to bind to (defaults to "keydown")
15652  */
15653 Roo.KeyMap = function(el, config, eventName){
15654     this.el  = Roo.get(el);
15655     this.eventName = eventName || "keydown";
15656     this.bindings = [];
15657     if(config){
15658         this.addBinding(config);
15659     }
15660     this.enable();
15661 };
15662
15663 Roo.KeyMap.prototype = {
15664     /**
15665      * True to stop the event from bubbling and prevent the default browser action if the
15666      * key was handled by the KeyMap (defaults to false)
15667      * @type Boolean
15668      */
15669     stopEvent : false,
15670
15671     /**
15672      * Add a new binding to this KeyMap. The following config object properties are supported:
15673      * <pre>
15674 Property    Type             Description
15675 ----------  ---------------  ----------------------------------------------------------------------
15676 key         String/Array     A single keycode or an array of keycodes to handle
15677 shift       Boolean          True to handle key only when shift is pressed (defaults to false)
15678 ctrl        Boolean          True to handle key only when ctrl is pressed (defaults to false)
15679 alt         Boolean          True to handle key only when alt is pressed (defaults to false)
15680 fn          Function         The function to call when KeyMap finds the expected key combination
15681 scope       Object           The scope of the callback function
15682 </pre>
15683      *
15684      * Usage:
15685      * <pre><code>
15686 // Create a KeyMap
15687 var map = new Roo.KeyMap(document, {
15688     key: Roo.EventObject.ENTER,
15689     fn: handleKey,
15690     scope: this
15691 });
15692
15693 //Add a new binding to the existing KeyMap later
15694 map.addBinding({
15695     key: 'abc',
15696     shift: true,
15697     fn: handleKey,
15698     scope: this
15699 });
15700 </code></pre>
15701      * @param {Object/Array} config A single KeyMap config or an array of configs
15702      */
15703         addBinding : function(config){
15704         if(config instanceof Array){
15705             for(var i = 0, len = config.length; i < len; i++){
15706                 this.addBinding(config[i]);
15707             }
15708             return;
15709         }
15710         var keyCode = config.key,
15711             shift = config.shift, 
15712             ctrl = config.ctrl, 
15713             alt = config.alt,
15714             fn = config.fn,
15715             scope = config.scope;
15716         if(typeof keyCode == "string"){
15717             var ks = [];
15718             var keyString = keyCode.toUpperCase();
15719             for(var j = 0, len = keyString.length; j < len; j++){
15720                 ks.push(keyString.charCodeAt(j));
15721             }
15722             keyCode = ks;
15723         }
15724         var keyArray = keyCode instanceof Array;
15725         var handler = function(e){
15726             if((!shift || e.shiftKey) && (!ctrl || e.ctrlKey) &&  (!alt || e.altKey)){
15727                 var k = e.getKey();
15728                 if(keyArray){
15729                     for(var i = 0, len = keyCode.length; i < len; i++){
15730                         if(keyCode[i] == k){
15731                           if(this.stopEvent){
15732                               e.stopEvent();
15733                           }
15734                           fn.call(scope || window, k, e);
15735                           return;
15736                         }
15737                     }
15738                 }else{
15739                     if(k == keyCode){
15740                         if(this.stopEvent){
15741                            e.stopEvent();
15742                         }
15743                         fn.call(scope || window, k, e);
15744                     }
15745                 }
15746             }
15747         };
15748         this.bindings.push(handler);  
15749         },
15750
15751     /**
15752      * Shorthand for adding a single key listener
15753      * @param {Number/Array/Object} key Either the numeric key code, array of key codes or an object with the
15754      * following options:
15755      * {key: (number or array), shift: (true/false), ctrl: (true/false), alt: (true/false)}
15756      * @param {Function} fn The function to call
15757      * @param {Object} scope (optional) The scope of the function
15758      */
15759     on : function(key, fn, scope){
15760         var keyCode, shift, ctrl, alt;
15761         if(typeof key == "object" && !(key instanceof Array)){
15762             keyCode = key.key;
15763             shift = key.shift;
15764             ctrl = key.ctrl;
15765             alt = key.alt;
15766         }else{
15767             keyCode = key;
15768         }
15769         this.addBinding({
15770             key: keyCode,
15771             shift: shift,
15772             ctrl: ctrl,
15773             alt: alt,
15774             fn: fn,
15775             scope: scope
15776         })
15777     },
15778
15779     // private
15780     handleKeyDown : function(e){
15781             if(this.enabled){ //just in case
15782             var b = this.bindings;
15783             for(var i = 0, len = b.length; i < len; i++){
15784                 b[i].call(this, e);
15785             }
15786             }
15787         },
15788         
15789         /**
15790          * Returns true if this KeyMap is enabled
15791          * @return {Boolean} 
15792          */
15793         isEnabled : function(){
15794             return this.enabled;  
15795         },
15796         
15797         /**
15798          * Enables this KeyMap
15799          */
15800         enable: function(){
15801                 if(!this.enabled){
15802                     this.el.on(this.eventName, this.handleKeyDown, this);
15803                     this.enabled = true;
15804                 }
15805         },
15806
15807         /**
15808          * Disable this KeyMap
15809          */
15810         disable: function(){
15811                 if(this.enabled){
15812                     this.el.removeListener(this.eventName, this.handleKeyDown, this);
15813                     this.enabled = false;
15814                 }
15815         }
15816 };/*
15817  * Based on:
15818  * Ext JS Library 1.1.1
15819  * Copyright(c) 2006-2007, Ext JS, LLC.
15820  *
15821  * Originally Released Under LGPL - original licence link has changed is not relivant.
15822  *
15823  * Fork - LGPL
15824  * <script type="text/javascript">
15825  */
15826
15827  
15828 /**
15829  * @class Roo.util.TextMetrics
15830  * Provides precise pixel measurements for blocks of text so that you can determine exactly how high and
15831  * wide, in pixels, a given block of text will be.
15832  * @static
15833  */
15834 Roo.util.TextMetrics = function(){
15835     var shared;
15836     return {
15837         /**
15838          * Measures the size of the specified text
15839          * @param {String/HTMLElement} el The element, dom node or id from which to copy existing CSS styles
15840          * that can affect the size of the rendered text
15841          * @param {String} text The text to measure
15842          * @param {Number} fixedWidth (optional) If the text will be multiline, you have to set a fixed width
15843          * in order to accurately measure the text height
15844          * @return {Object} An object containing the text's size {width: (width), height: (height)}
15845          */
15846         measure : function(el, text, fixedWidth){
15847             if(!shared){
15848                 shared = Roo.util.TextMetrics.Instance(el, fixedWidth);
15849             }
15850             shared.bind(el);
15851             shared.setFixedWidth(fixedWidth || 'auto');
15852             return shared.getSize(text);
15853         },
15854
15855         /**
15856          * Return a unique TextMetrics instance that can be bound directly to an element and reused.  This reduces
15857          * the overhead of multiple calls to initialize the style properties on each measurement.
15858          * @param {String/HTMLElement} el The element, dom node or id that the instance will be bound to
15859          * @param {Number} fixedWidth (optional) If the text will be multiline, you have to set a fixed width
15860          * in order to accurately measure the text height
15861          * @return {Roo.util.TextMetrics.Instance} instance The new instance
15862          */
15863         createInstance : function(el, fixedWidth){
15864             return Roo.util.TextMetrics.Instance(el, fixedWidth);
15865         }
15866     };
15867 }();
15868
15869 /**
15870  * @class Roo.util.TextMetrics.Instance
15871  * Instance of  TextMetrics Calcuation
15872  * @constructor
15873  * Create a new TextMetrics Instance
15874  * @param {Object} bindto
15875  * @param {Boolean} fixedWidth
15876  */
15877
15878 Roo.util.TextMetrics.Instance = function(bindTo, fixedWidth)
15879 {
15880     var ml = new Roo.Element(document.createElement('div'));
15881     document.body.appendChild(ml.dom);
15882     ml.position('absolute');
15883     ml.setLeftTop(-1000, -1000);
15884     ml.hide();
15885
15886     if(fixedWidth){
15887         ml.setWidth(fixedWidth);
15888     }
15889      
15890     var instance = {
15891         /**
15892          * Returns the size of the specified text based on the internal element's style and width properties
15893          * @param {String} text The text to measure
15894          * @return {Object} An object containing the text's size {width: (width), height: (height)}
15895          */
15896         getSize : function(text){
15897             ml.update(text);
15898             var s = ml.getSize();
15899             ml.update('');
15900             return s;
15901         },
15902
15903         /**
15904          * Binds this TextMetrics instance to an element from which to copy existing CSS styles
15905          * that can affect the size of the rendered text
15906          * @param {String/HTMLElement} el The element, dom node or id
15907          */
15908         bind : function(el){
15909             ml.setStyle(
15910                 Roo.fly(el).getStyles('font-size','font-style', 'font-weight', 'font-family','line-height')
15911             );
15912         },
15913
15914         /**
15915          * Sets a fixed width on the internal measurement element.  If the text will be multiline, you have
15916          * to set a fixed width in order to accurately measure the text height.
15917          * @param {Number} width The width to set on the element
15918          */
15919         setFixedWidth : function(width){
15920             ml.setWidth(width);
15921         },
15922
15923         /**
15924          * Returns the measured width of the specified text
15925          * @param {String} text The text to measure
15926          * @return {Number} width The width in pixels
15927          */
15928         getWidth : function(text){
15929             ml.dom.style.width = 'auto';
15930             return this.getSize(text).width;
15931         },
15932
15933         /**
15934          * Returns the measured height of the specified text.  For multiline text, be sure to call
15935          * {@link #setFixedWidth} if necessary.
15936          * @param {String} text The text to measure
15937          * @return {Number} height The height in pixels
15938          */
15939         getHeight : function(text){
15940             return this.getSize(text).height;
15941         }
15942     };
15943
15944     instance.bind(bindTo);
15945
15946     return instance;
15947 };
15948
15949 // backwards compat
15950 Roo.Element.measureText = Roo.util.TextMetrics.measure;/*
15951  * Based on:
15952  * Ext JS Library 1.1.1
15953  * Copyright(c) 2006-2007, Ext JS, LLC.
15954  *
15955  * Originally Released Under LGPL - original licence link has changed is not relivant.
15956  *
15957  * Fork - LGPL
15958  * <script type="text/javascript">
15959  */
15960
15961 /**
15962  * @class Roo.state.Provider
15963  * Abstract base class for state provider implementations. This class provides methods
15964  * for encoding and decoding <b>typed</b> variables including dates and defines the 
15965  * Provider interface.
15966  */
15967 Roo.state.Provider = function(){
15968     /**
15969      * @event statechange
15970      * Fires when a state change occurs.
15971      * @param {Provider} this This state provider
15972      * @param {String} key The state key which was changed
15973      * @param {String} value The encoded value for the state
15974      */
15975     this.addEvents({
15976         "statechange": true
15977     });
15978     this.state = {};
15979     Roo.state.Provider.superclass.constructor.call(this);
15980 };
15981 Roo.extend(Roo.state.Provider, Roo.util.Observable, {
15982     /**
15983      * Returns the current value for a key
15984      * @param {String} name The key name
15985      * @param {Mixed} defaultValue A default value to return if the key's value is not found
15986      * @return {Mixed} The state data
15987      */
15988     get : function(name, defaultValue){
15989         return typeof this.state[name] == "undefined" ?
15990             defaultValue : this.state[name];
15991     },
15992     
15993     /**
15994      * Clears a value from the state
15995      * @param {String} name The key name
15996      */
15997     clear : function(name){
15998         delete this.state[name];
15999         this.fireEvent("statechange", this, name, null);
16000     },
16001     
16002     /**
16003      * Sets the value for a key
16004      * @param {String} name The key name
16005      * @param {Mixed} value The value to set
16006      */
16007     set : function(name, value){
16008         this.state[name] = value;
16009         this.fireEvent("statechange", this, name, value);
16010     },
16011     
16012     /**
16013      * Decodes a string previously encoded with {@link #encodeValue}.
16014      * @param {String} value The value to decode
16015      * @return {Mixed} The decoded value
16016      */
16017     decodeValue : function(cookie){
16018         var re = /^(a|n|d|b|s|o)\:(.*)$/;
16019         var matches = re.exec(unescape(cookie));
16020         if(!matches || !matches[1]) {
16021             return; // non state cookie
16022         }
16023         var type = matches[1];
16024         var v = matches[2];
16025         switch(type){
16026             case "n":
16027                 return parseFloat(v);
16028             case "d":
16029                 return new Date(Date.parse(v));
16030             case "b":
16031                 return (v == "1");
16032             case "a":
16033                 var all = [];
16034                 var values = v.split("^");
16035                 for(var i = 0, len = values.length; i < len; i++){
16036                     all.push(this.decodeValue(values[i]));
16037                 }
16038                 return all;
16039            case "o":
16040                 var all = {};
16041                 var values = v.split("^");
16042                 for(var i = 0, len = values.length; i < len; i++){
16043                     var kv = values[i].split("=");
16044                     all[kv[0]] = this.decodeValue(kv[1]);
16045                 }
16046                 return all;
16047            default:
16048                 return v;
16049         }
16050     },
16051     
16052     /**
16053      * Encodes a value including type information.  Decode with {@link #decodeValue}.
16054      * @param {Mixed} value The value to encode
16055      * @return {String} The encoded value
16056      */
16057     encodeValue : function(v){
16058         var enc;
16059         if(typeof v == "number"){
16060             enc = "n:" + v;
16061         }else if(typeof v == "boolean"){
16062             enc = "b:" + (v ? "1" : "0");
16063         }else if(v instanceof Date){
16064             enc = "d:" + v.toGMTString();
16065         }else if(v instanceof Array){
16066             var flat = "";
16067             for(var i = 0, len = v.length; i < len; i++){
16068                 flat += this.encodeValue(v[i]);
16069                 if(i != len-1) {
16070                     flat += "^";
16071                 }
16072             }
16073             enc = "a:" + flat;
16074         }else if(typeof v == "object"){
16075             var flat = "";
16076             for(var key in v){
16077                 if(typeof v[key] != "function"){
16078                     flat += key + "=" + this.encodeValue(v[key]) + "^";
16079                 }
16080             }
16081             enc = "o:" + flat.substring(0, flat.length-1);
16082         }else{
16083             enc = "s:" + v;
16084         }
16085         return escape(enc);        
16086     }
16087 });
16088
16089 /*
16090  * Based on:
16091  * Ext JS Library 1.1.1
16092  * Copyright(c) 2006-2007, Ext JS, LLC.
16093  *
16094  * Originally Released Under LGPL - original licence link has changed is not relivant.
16095  *
16096  * Fork - LGPL
16097  * <script type="text/javascript">
16098  */
16099 /**
16100  * @class Roo.state.Manager
16101  * This is the global state manager. By default all components that are "state aware" check this class
16102  * for state information if you don't pass them a custom state provider. In order for this class
16103  * to be useful, it must be initialized with a provider when your application initializes.
16104  <pre><code>
16105 // in your initialization function
16106 init : function(){
16107    Roo.state.Manager.setProvider(new Roo.state.CookieProvider());
16108    ...
16109    // supposed you have a {@link Roo.BorderLayout}
16110    var layout = new Roo.BorderLayout(...);
16111    layout.restoreState();
16112    // or a {Roo.BasicDialog}
16113    var dialog = new Roo.BasicDialog(...);
16114    dialog.restoreState();
16115  </code></pre>
16116  * @static
16117  */
16118 Roo.state.Manager = function(){
16119     var provider = new Roo.state.Provider();
16120     
16121     return {
16122         /**
16123          * Configures the default state provider for your application
16124          * @param {Provider} stateProvider The state provider to set
16125          */
16126         setProvider : function(stateProvider){
16127             provider = stateProvider;
16128         },
16129         
16130         /**
16131          * Returns the current value for a key
16132          * @param {String} name The key name
16133          * @param {Mixed} defaultValue The default value to return if the key lookup does not match
16134          * @return {Mixed} The state data
16135          */
16136         get : function(key, defaultValue){
16137             return provider.get(key, defaultValue);
16138         },
16139         
16140         /**
16141          * Sets the value for a key
16142          * @param {String} name The key name
16143          * @param {Mixed} value The state data
16144          */
16145          set : function(key, value){
16146             provider.set(key, value);
16147         },
16148         
16149         /**
16150          * Clears a value from the state
16151          * @param {String} name The key name
16152          */
16153         clear : function(key){
16154             provider.clear(key);
16155         },
16156         
16157         /**
16158          * Gets the currently configured state provider
16159          * @return {Provider} The state provider
16160          */
16161         getProvider : function(){
16162             return provider;
16163         }
16164     };
16165 }();
16166 /*
16167  * Based on:
16168  * Ext JS Library 1.1.1
16169  * Copyright(c) 2006-2007, Ext JS, LLC.
16170  *
16171  * Originally Released Under LGPL - original licence link has changed is not relivant.
16172  *
16173  * Fork - LGPL
16174  * <script type="text/javascript">
16175  */
16176 /**
16177  * @class Roo.state.CookieProvider
16178  * @extends Roo.state.Provider
16179  * The default Provider implementation which saves state via cookies.
16180  * <br />Usage:
16181  <pre><code>
16182    var cp = new Roo.state.CookieProvider({
16183        path: "/cgi-bin/",
16184        expires: new Date(new Date().getTime()+(1000*60*60*24*30)); //30 days
16185        domain: "roojs.com"
16186    })
16187    Roo.state.Manager.setProvider(cp);
16188  </code></pre>
16189  * @cfg {String} path The path for which the cookie is active (defaults to root '/' which makes it active for all pages in the site)
16190  * @cfg {Date} expires The cookie expiration date (defaults to 7 days from now)
16191  * @cfg {String} domain The domain to save the cookie for.  Note that you cannot specify a different domain than
16192  * your page is on, but you can specify a sub-domain, or simply the domain itself like 'roojs.com' to include
16193  * all sub-domains if you need to access cookies across different sub-domains (defaults to null which uses the same
16194  * domain the page is running on including the 'www' like 'www.roojs.com')
16195  * @cfg {Boolean} secure True if the site is using SSL (defaults to false)
16196  * @constructor
16197  * Create a new CookieProvider
16198  * @param {Object} config The configuration object
16199  */
16200 Roo.state.CookieProvider = function(config){
16201     Roo.state.CookieProvider.superclass.constructor.call(this);
16202     this.path = "/";
16203     this.expires = new Date(new Date().getTime()+(1000*60*60*24*7)); //7 days
16204     this.domain = null;
16205     this.secure = false;
16206     Roo.apply(this, config);
16207     this.state = this.readCookies();
16208 };
16209
16210 Roo.extend(Roo.state.CookieProvider, Roo.state.Provider, {
16211     // private
16212     set : function(name, value){
16213         if(typeof value == "undefined" || value === null){
16214             this.clear(name);
16215             return;
16216         }
16217         this.setCookie(name, value);
16218         Roo.state.CookieProvider.superclass.set.call(this, name, value);
16219     },
16220
16221     // private
16222     clear : function(name){
16223         this.clearCookie(name);
16224         Roo.state.CookieProvider.superclass.clear.call(this, name);
16225     },
16226
16227     // private
16228     readCookies : function(){
16229         var cookies = {};
16230         var c = document.cookie + ";";
16231         var re = /\s?(.*?)=(.*?);/g;
16232         var matches;
16233         while((matches = re.exec(c)) != null){
16234             var name = matches[1];
16235             var value = matches[2];
16236             if(name && name.substring(0,3) == "ys-"){
16237                 cookies[name.substr(3)] = this.decodeValue(value);
16238             }
16239         }
16240         return cookies;
16241     },
16242
16243     // private
16244     setCookie : function(name, value){
16245         document.cookie = "ys-"+ name + "=" + this.encodeValue(value) +
16246            ((this.expires == null) ? "" : ("; expires=" + this.expires.toGMTString())) +
16247            ((this.path == null) ? "" : ("; path=" + this.path)) +
16248            ((this.domain == null) ? "" : ("; domain=" + this.domain)) +
16249            ((this.secure == true) ? "; secure" : "");
16250     },
16251
16252     // private
16253     clearCookie : function(name){
16254         document.cookie = "ys-" + name + "=null; expires=Thu, 01-Jan-70 00:00:01 GMT" +
16255            ((this.path == null) ? "" : ("; path=" + this.path)) +
16256            ((this.domain == null) ? "" : ("; domain=" + this.domain)) +
16257            ((this.secure == true) ? "; secure" : "");
16258     }
16259 });/*
16260  * Based on:
16261  * Ext JS Library 1.1.1
16262  * Copyright(c) 2006-2007, Ext JS, LLC.
16263  *
16264  * Originally Released Under LGPL - original licence link has changed is not relivant.
16265  *
16266  * Fork - LGPL
16267  * <script type="text/javascript">
16268  */
16269  
16270
16271 /**
16272  * @class Roo.ComponentMgr
16273  * Provides a common registry of all components on a page so that they can be easily accessed by component id (see {@link Roo.getCmp}).
16274  * @static
16275  */
16276 Roo.ComponentMgr = function(){
16277     var all = new Roo.util.MixedCollection();
16278
16279     return {
16280         /**
16281          * Registers a component.
16282          * @param {Roo.Component} c The component
16283          */
16284         register : function(c){
16285             all.add(c);
16286         },
16287
16288         /**
16289          * Unregisters a component.
16290          * @param {Roo.Component} c The component
16291          */
16292         unregister : function(c){
16293             all.remove(c);
16294         },
16295
16296         /**
16297          * Returns a component by id
16298          * @param {String} id The component id
16299          */
16300         get : function(id){
16301             return all.get(id);
16302         },
16303
16304         /**
16305          * Registers a function that will be called when a specified component is added to ComponentMgr
16306          * @param {String} id The component id
16307          * @param {Funtction} fn The callback function
16308          * @param {Object} scope The scope of the callback
16309          */
16310         onAvailable : function(id, fn, scope){
16311             all.on("add", function(index, o){
16312                 if(o.id == id){
16313                     fn.call(scope || o, o);
16314                     all.un("add", fn, scope);
16315                 }
16316             });
16317         }
16318     };
16319 }();/*
16320  * Based on:
16321  * Ext JS Library 1.1.1
16322  * Copyright(c) 2006-2007, Ext JS, LLC.
16323  *
16324  * Originally Released Under LGPL - original licence link has changed is not relivant.
16325  *
16326  * Fork - LGPL
16327  * <script type="text/javascript">
16328  */
16329  
16330 /**
16331  * @class Roo.Component
16332  * @extends Roo.util.Observable
16333  * Base class for all major Roo components.  All subclasses of Component can automatically participate in the standard
16334  * Roo component lifecycle of creation, rendering and destruction.  They also have automatic support for basic hide/show
16335  * and enable/disable behavior.  Component allows any subclass to be lazy-rendered into any {@link Roo.Container} and
16336  * to be automatically registered with the {@link Roo.ComponentMgr} so that it can be referenced at any time via {@link Roo.getCmp}.
16337  * All visual components (widgets) that require rendering into a layout should subclass Component.
16338  * @constructor
16339  * @param {Roo.Element/String/Object} config The configuration options.  If an element is passed, it is set as the internal
16340  * 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
16341  * and is used as the component id.  Otherwise, it is assumed to be a standard config object and is applied to the component.
16342  */
16343 Roo.Component = function(config){
16344     config = config || {};
16345     if(config.tagName || config.dom || typeof config == "string"){ // element object
16346         config = {el: config, id: config.id || config};
16347     }
16348     this.initialConfig = config;
16349
16350     Roo.apply(this, config);
16351     this.addEvents({
16352         /**
16353          * @event disable
16354          * Fires after the component is disabled.
16355              * @param {Roo.Component} this
16356              */
16357         disable : true,
16358         /**
16359          * @event enable
16360          * Fires after the component is enabled.
16361              * @param {Roo.Component} this
16362              */
16363         enable : true,
16364         /**
16365          * @event beforeshow
16366          * Fires before the component is shown.  Return false to stop the show.
16367              * @param {Roo.Component} this
16368              */
16369         beforeshow : true,
16370         /**
16371          * @event show
16372          * Fires after the component is shown.
16373              * @param {Roo.Component} this
16374              */
16375         show : true,
16376         /**
16377          * @event beforehide
16378          * Fires before the component is hidden. Return false to stop the hide.
16379              * @param {Roo.Component} this
16380              */
16381         beforehide : true,
16382         /**
16383          * @event hide
16384          * Fires after the component is hidden.
16385              * @param {Roo.Component} this
16386              */
16387         hide : true,
16388         /**
16389          * @event beforerender
16390          * Fires before the component is rendered. Return false to stop the render.
16391              * @param {Roo.Component} this
16392              */
16393         beforerender : true,
16394         /**
16395          * @event render
16396          * Fires after the component is rendered.
16397              * @param {Roo.Component} this
16398              */
16399         render : true,
16400         /**
16401          * @event beforedestroy
16402          * Fires before the component is destroyed. Return false to stop the destroy.
16403              * @param {Roo.Component} this
16404              */
16405         beforedestroy : true,
16406         /**
16407          * @event destroy
16408          * Fires after the component is destroyed.
16409              * @param {Roo.Component} this
16410              */
16411         destroy : true
16412     });
16413     if(!this.id){
16414         this.id = "roo-comp-" + (++Roo.Component.AUTO_ID);
16415     }
16416     Roo.ComponentMgr.register(this);
16417     Roo.Component.superclass.constructor.call(this);
16418     this.initComponent();
16419     if(this.renderTo){ // not supported by all components yet. use at your own risk!
16420         this.render(this.renderTo);
16421         delete this.renderTo;
16422     }
16423 };
16424
16425 /** @private */
16426 Roo.Component.AUTO_ID = 1000;
16427
16428 Roo.extend(Roo.Component, Roo.util.Observable, {
16429     /**
16430      * @scope Roo.Component.prototype
16431      * @type {Boolean}
16432      * true if this component is hidden. Read-only.
16433      */
16434     hidden : false,
16435     /**
16436      * @type {Boolean}
16437      * true if this component is disabled. Read-only.
16438      */
16439     disabled : false,
16440     /**
16441      * @type {Boolean}
16442      * true if this component has been rendered. Read-only.
16443      */
16444     rendered : false,
16445     
16446     /** @cfg {String} disableClass
16447      * CSS class added to the component when it is disabled (defaults to "x-item-disabled").
16448      */
16449     disabledClass : "x-item-disabled",
16450         /** @cfg {Boolean} allowDomMove
16451          * Whether the component can move the Dom node when rendering (defaults to true).
16452          */
16453     allowDomMove : true,
16454     /** @cfg {String} hideMode (display|visibility)
16455      * How this component should hidden. Supported values are
16456      * "visibility" (css visibility), "offsets" (negative offset position) and
16457      * "display" (css display) - defaults to "display".
16458      */
16459     hideMode: 'display',
16460
16461     /** @private */
16462     ctype : "Roo.Component",
16463
16464     /**
16465      * @cfg {String} actionMode 
16466      * which property holds the element that used for  hide() / show() / disable() / enable()
16467      * default is 'el' for forms you probably want to set this to fieldEl 
16468      */
16469     actionMode : "el",
16470
16471     /** @private */
16472     getActionEl : function(){
16473         return this[this.actionMode];
16474     },
16475
16476     initComponent : Roo.emptyFn,
16477     /**
16478      * If this is a lazy rendering component, render it to its container element.
16479      * @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.
16480      */
16481     render : function(container, position){
16482         
16483         if(this.rendered){
16484             return this;
16485         }
16486         
16487         if(this.fireEvent("beforerender", this) === false){
16488             return false;
16489         }
16490         
16491         if(!container && this.el){
16492             this.el = Roo.get(this.el);
16493             container = this.el.dom.parentNode;
16494             this.allowDomMove = false;
16495         }
16496         this.container = Roo.get(container);
16497         this.rendered = true;
16498         if(position !== undefined){
16499             if(typeof position == 'number'){
16500                 position = this.container.dom.childNodes[position];
16501             }else{
16502                 position = Roo.getDom(position);
16503             }
16504         }
16505         this.onRender(this.container, position || null);
16506         if(this.cls){
16507             this.el.addClass(this.cls);
16508             delete this.cls;
16509         }
16510         if(this.style){
16511             this.el.applyStyles(this.style);
16512             delete this.style;
16513         }
16514         this.fireEvent("render", this);
16515         this.afterRender(this.container);
16516         if(this.hidden){
16517             this.hide();
16518         }
16519         if(this.disabled){
16520             this.disable();
16521         }
16522
16523         return this;
16524         
16525     },
16526
16527     /** @private */
16528     // default function is not really useful
16529     onRender : function(ct, position){
16530         if(this.el){
16531             this.el = Roo.get(this.el);
16532             if(this.allowDomMove !== false){
16533                 ct.dom.insertBefore(this.el.dom, position);
16534             }
16535         }
16536     },
16537
16538     /** @private */
16539     getAutoCreate : function(){
16540         var cfg = typeof this.autoCreate == "object" ?
16541                       this.autoCreate : Roo.apply({}, this.defaultAutoCreate);
16542         if(this.id && !cfg.id){
16543             cfg.id = this.id;
16544         }
16545         return cfg;
16546     },
16547
16548     /** @private */
16549     afterRender : Roo.emptyFn,
16550
16551     /**
16552      * Destroys this component by purging any event listeners, removing the component's element from the DOM,
16553      * removing the component from its {@link Roo.Container} (if applicable) and unregistering it from {@link Roo.ComponentMgr}.
16554      */
16555     destroy : function(){
16556         if(this.fireEvent("beforedestroy", this) !== false){
16557             this.purgeListeners();
16558             this.beforeDestroy();
16559             if(this.rendered){
16560                 this.el.removeAllListeners();
16561                 this.el.remove();
16562                 if(this.actionMode == "container"){
16563                     this.container.remove();
16564                 }
16565             }
16566             this.onDestroy();
16567             Roo.ComponentMgr.unregister(this);
16568             this.fireEvent("destroy", this);
16569         }
16570     },
16571
16572         /** @private */
16573     beforeDestroy : function(){
16574
16575     },
16576
16577         /** @private */
16578         onDestroy : function(){
16579
16580     },
16581
16582     /**
16583      * Returns the underlying {@link Roo.Element}.
16584      * @return {Roo.Element} The element
16585      */
16586     getEl : function(){
16587         return this.el;
16588     },
16589
16590     /**
16591      * Returns the id of this component.
16592      * @return {String}
16593      */
16594     getId : function(){
16595         return this.id;
16596     },
16597
16598     /**
16599      * Try to focus this component.
16600      * @param {Boolean} selectText True to also select the text in this component (if applicable)
16601      * @return {Roo.Component} this
16602      */
16603     focus : function(selectText){
16604         if(this.rendered){
16605             this.el.focus();
16606             if(selectText === true){
16607                 this.el.dom.select();
16608             }
16609         }
16610         return this;
16611     },
16612
16613     /** @private */
16614     blur : function(){
16615         if(this.rendered){
16616             this.el.blur();
16617         }
16618         return this;
16619     },
16620
16621     /**
16622      * Disable this component.
16623      * @return {Roo.Component} this
16624      */
16625     disable : function(){
16626         if(this.rendered){
16627             this.onDisable();
16628         }
16629         this.disabled = true;
16630         this.fireEvent("disable", this);
16631         return this;
16632     },
16633
16634         // private
16635     onDisable : function(){
16636         this.getActionEl().addClass(this.disabledClass);
16637         this.el.dom.disabled = true;
16638     },
16639
16640     /**
16641      * Enable this component.
16642      * @return {Roo.Component} this
16643      */
16644     enable : function(){
16645         if(this.rendered){
16646             this.onEnable();
16647         }
16648         this.disabled = false;
16649         this.fireEvent("enable", this);
16650         return this;
16651     },
16652
16653         // private
16654     onEnable : function(){
16655         this.getActionEl().removeClass(this.disabledClass);
16656         this.el.dom.disabled = false;
16657     },
16658
16659     /**
16660      * Convenience function for setting disabled/enabled by boolean.
16661      * @param {Boolean} disabled
16662      */
16663     setDisabled : function(disabled){
16664         this[disabled ? "disable" : "enable"]();
16665     },
16666
16667     /**
16668      * Show this component.
16669      * @return {Roo.Component} this
16670      */
16671     show: function(){
16672         if(this.fireEvent("beforeshow", this) !== false){
16673             this.hidden = false;
16674             if(this.rendered){
16675                 this.onShow();
16676             }
16677             this.fireEvent("show", this);
16678         }
16679         return this;
16680     },
16681
16682     // private
16683     onShow : function(){
16684         var ae = this.getActionEl();
16685         if(this.hideMode == 'visibility'){
16686             ae.dom.style.visibility = "visible";
16687         }else if(this.hideMode == 'offsets'){
16688             ae.removeClass('x-hidden');
16689         }else{
16690             ae.dom.style.display = "";
16691         }
16692     },
16693
16694     /**
16695      * Hide this component.
16696      * @return {Roo.Component} this
16697      */
16698     hide: function(){
16699         if(this.fireEvent("beforehide", this) !== false){
16700             this.hidden = true;
16701             if(this.rendered){
16702                 this.onHide();
16703             }
16704             this.fireEvent("hide", this);
16705         }
16706         return this;
16707     },
16708
16709     // private
16710     onHide : function(){
16711         var ae = this.getActionEl();
16712         if(this.hideMode == 'visibility'){
16713             ae.dom.style.visibility = "hidden";
16714         }else if(this.hideMode == 'offsets'){
16715             ae.addClass('x-hidden');
16716         }else{
16717             ae.dom.style.display = "none";
16718         }
16719     },
16720
16721     /**
16722      * Convenience function to hide or show this component by boolean.
16723      * @param {Boolean} visible True to show, false to hide
16724      * @return {Roo.Component} this
16725      */
16726     setVisible: function(visible){
16727         if(visible) {
16728             this.show();
16729         }else{
16730             this.hide();
16731         }
16732         return this;
16733     },
16734
16735     /**
16736      * Returns true if this component is visible.
16737      */
16738     isVisible : function(){
16739         return this.getActionEl().isVisible();
16740     },
16741
16742     cloneConfig : function(overrides){
16743         overrides = overrides || {};
16744         var id = overrides.id || Roo.id();
16745         var cfg = Roo.applyIf(overrides, this.initialConfig);
16746         cfg.id = id; // prevent dup id
16747         return new this.constructor(cfg);
16748     }
16749 });/*
16750  * Based on:
16751  * Ext JS Library 1.1.1
16752  * Copyright(c) 2006-2007, Ext JS, LLC.
16753  *
16754  * Originally Released Under LGPL - original licence link has changed is not relivant.
16755  *
16756  * Fork - LGPL
16757  * <script type="text/javascript">
16758  */
16759
16760 /**
16761  * @class Roo.BoxComponent
16762  * @extends Roo.Component
16763  * Base class for any visual {@link Roo.Component} that uses a box container.  BoxComponent provides automatic box
16764  * model adjustments for sizing and positioning and will work correctly withnin the Component rendering model.  All
16765  * container classes should subclass BoxComponent so that they will work consistently when nested within other Roo
16766  * layout containers.
16767  * @constructor
16768  * @param {Roo.Element/String/Object} config The configuration options.
16769  */
16770 Roo.BoxComponent = function(config){
16771     Roo.Component.call(this, config);
16772     this.addEvents({
16773         /**
16774          * @event resize
16775          * Fires after the component is resized.
16776              * @param {Roo.Component} this
16777              * @param {Number} adjWidth The box-adjusted width that was set
16778              * @param {Number} adjHeight The box-adjusted height that was set
16779              * @param {Number} rawWidth The width that was originally specified
16780              * @param {Number} rawHeight The height that was originally specified
16781              */
16782         resize : true,
16783         /**
16784          * @event move
16785          * Fires after the component is moved.
16786              * @param {Roo.Component} this
16787              * @param {Number} x The new x position
16788              * @param {Number} y The new y position
16789              */
16790         move : true
16791     });
16792 };
16793
16794 Roo.extend(Roo.BoxComponent, Roo.Component, {
16795     // private, set in afterRender to signify that the component has been rendered
16796     boxReady : false,
16797     // private, used to defer height settings to subclasses
16798     deferHeight: false,
16799     /** @cfg {Number} width
16800      * width (optional) size of component
16801      */
16802      /** @cfg {Number} height
16803      * height (optional) size of component
16804      */
16805      
16806     /**
16807      * Sets the width and height of the component.  This method fires the resize event.  This method can accept
16808      * either width and height as separate numeric arguments, or you can pass a size object like {width:10, height:20}.
16809      * @param {Number/Object} width The new width to set, or a size object in the format {width, height}
16810      * @param {Number} height The new height to set (not required if a size object is passed as the first arg)
16811      * @return {Roo.BoxComponent} this
16812      */
16813     setSize : function(w, h){
16814         // support for standard size objects
16815         if(typeof w == 'object'){
16816             h = w.height;
16817             w = w.width;
16818         }
16819         // not rendered
16820         if(!this.boxReady){
16821             this.width = w;
16822             this.height = h;
16823             return this;
16824         }
16825
16826         // prevent recalcs when not needed
16827         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
16828             return this;
16829         }
16830         this.lastSize = {width: w, height: h};
16831
16832         var adj = this.adjustSize(w, h);
16833         var aw = adj.width, ah = adj.height;
16834         if(aw !== undefined || ah !== undefined){ // this code is nasty but performs better with floaters
16835             var rz = this.getResizeEl();
16836             if(!this.deferHeight && aw !== undefined && ah !== undefined){
16837                 rz.setSize(aw, ah);
16838             }else if(!this.deferHeight && ah !== undefined){
16839                 rz.setHeight(ah);
16840             }else if(aw !== undefined){
16841                 rz.setWidth(aw);
16842             }
16843             this.onResize(aw, ah, w, h);
16844             this.fireEvent('resize', this, aw, ah, w, h);
16845         }
16846         return this;
16847     },
16848
16849     /**
16850      * Gets the current size of the component's underlying element.
16851      * @return {Object} An object containing the element's size {width: (element width), height: (element height)}
16852      */
16853     getSize : function(){
16854         return this.el.getSize();
16855     },
16856
16857     /**
16858      * Gets the current XY position of the component's underlying element.
16859      * @param {Boolean} local (optional) If true the element's left and top are returned instead of page XY (defaults to false)
16860      * @return {Array} The XY position of the element (e.g., [100, 200])
16861      */
16862     getPosition : function(local){
16863         if(local === true){
16864             return [this.el.getLeft(true), this.el.getTop(true)];
16865         }
16866         return this.xy || this.el.getXY();
16867     },
16868
16869     /**
16870      * Gets the current box measurements of the component's underlying element.
16871      * @param {Boolean} local (optional) If true the element's left and top are returned instead of page XY (defaults to false)
16872      * @returns {Object} box An object in the format {x, y, width, height}
16873      */
16874     getBox : function(local){
16875         var s = this.el.getSize();
16876         if(local){
16877             s.x = this.el.getLeft(true);
16878             s.y = this.el.getTop(true);
16879         }else{
16880             var xy = this.xy || this.el.getXY();
16881             s.x = xy[0];
16882             s.y = xy[1];
16883         }
16884         return s;
16885     },
16886
16887     /**
16888      * Sets the current box measurements of the component's underlying element.
16889      * @param {Object} box An object in the format {x, y, width, height}
16890      * @returns {Roo.BoxComponent} this
16891      */
16892     updateBox : function(box){
16893         this.setSize(box.width, box.height);
16894         this.setPagePosition(box.x, box.y);
16895         return this;
16896     },
16897
16898     // protected
16899     getResizeEl : function(){
16900         return this.resizeEl || this.el;
16901     },
16902
16903     // protected
16904     getPositionEl : function(){
16905         return this.positionEl || this.el;
16906     },
16907
16908     /**
16909      * Sets the left and top of the component.  To set the page XY position instead, use {@link #setPagePosition}.
16910      * This method fires the move event.
16911      * @param {Number} left The new left
16912      * @param {Number} top The new top
16913      * @returns {Roo.BoxComponent} this
16914      */
16915     setPosition : function(x, y){
16916         this.x = x;
16917         this.y = y;
16918         if(!this.boxReady){
16919             return this;
16920         }
16921         var adj = this.adjustPosition(x, y);
16922         var ax = adj.x, ay = adj.y;
16923
16924         var el = this.getPositionEl();
16925         if(ax !== undefined || ay !== undefined){
16926             if(ax !== undefined && ay !== undefined){
16927                 el.setLeftTop(ax, ay);
16928             }else if(ax !== undefined){
16929                 el.setLeft(ax);
16930             }else if(ay !== undefined){
16931                 el.setTop(ay);
16932             }
16933             this.onPosition(ax, ay);
16934             this.fireEvent('move', this, ax, ay);
16935         }
16936         return this;
16937     },
16938
16939     /**
16940      * Sets the page XY position of the component.  To set the left and top instead, use {@link #setPosition}.
16941      * This method fires the move event.
16942      * @param {Number} x The new x position
16943      * @param {Number} y The new y position
16944      * @returns {Roo.BoxComponent} this
16945      */
16946     setPagePosition : function(x, y){
16947         this.pageX = x;
16948         this.pageY = y;
16949         if(!this.boxReady){
16950             return;
16951         }
16952         if(x === undefined || y === undefined){ // cannot translate undefined points
16953             return;
16954         }
16955         var p = this.el.translatePoints(x, y);
16956         this.setPosition(p.left, p.top);
16957         return this;
16958     },
16959
16960     // private
16961     onRender : function(ct, position){
16962         Roo.BoxComponent.superclass.onRender.call(this, ct, position);
16963         if(this.resizeEl){
16964             this.resizeEl = Roo.get(this.resizeEl);
16965         }
16966         if(this.positionEl){
16967             this.positionEl = Roo.get(this.positionEl);
16968         }
16969     },
16970
16971     // private
16972     afterRender : function(){
16973         Roo.BoxComponent.superclass.afterRender.call(this);
16974         this.boxReady = true;
16975         this.setSize(this.width, this.height);
16976         if(this.x || this.y){
16977             this.setPosition(this.x, this.y);
16978         }
16979         if(this.pageX || this.pageY){
16980             this.setPagePosition(this.pageX, this.pageY);
16981         }
16982     },
16983
16984     /**
16985      * Force the component's size to recalculate based on the underlying element's current height and width.
16986      * @returns {Roo.BoxComponent} this
16987      */
16988     syncSize : function(){
16989         delete this.lastSize;
16990         this.setSize(this.el.getWidth(), this.el.getHeight());
16991         return this;
16992     },
16993
16994     /**
16995      * Called after the component is resized, this method is empty by default but can be implemented by any
16996      * subclass that needs to perform custom logic after a resize occurs.
16997      * @param {Number} adjWidth The box-adjusted width that was set
16998      * @param {Number} adjHeight The box-adjusted height that was set
16999      * @param {Number} rawWidth The width that was originally specified
17000      * @param {Number} rawHeight The height that was originally specified
17001      */
17002     onResize : function(adjWidth, adjHeight, rawWidth, rawHeight){
17003
17004     },
17005
17006     /**
17007      * Called after the component is moved, this method is empty by default but can be implemented by any
17008      * subclass that needs to perform custom logic after a move occurs.
17009      * @param {Number} x The new x position
17010      * @param {Number} y The new y position
17011      */
17012     onPosition : function(x, y){
17013
17014     },
17015
17016     // private
17017     adjustSize : function(w, h){
17018         if(this.autoWidth){
17019             w = 'auto';
17020         }
17021         if(this.autoHeight){
17022             h = 'auto';
17023         }
17024         return {width : w, height: h};
17025     },
17026
17027     // private
17028     adjustPosition : function(x, y){
17029         return {x : x, y: y};
17030     }
17031 });/*
17032  * Based on:
17033  * Ext JS Library 1.1.1
17034  * Copyright(c) 2006-2007, Ext JS, LLC.
17035  *
17036  * Originally Released Under LGPL - original licence link has changed is not relivant.
17037  *
17038  * Fork - LGPL
17039  * <script type="text/javascript">
17040  */
17041  (function(){ 
17042 /**
17043  * @class Roo.Layer
17044  * @extends Roo.Element
17045  * An extended {@link Roo.Element} object that supports a shadow and shim, constrain to viewport and
17046  * automatic maintaining of shadow/shim positions.
17047  * @cfg {Boolean} shim False to disable the iframe shim in browsers which need one (defaults to true)
17048  * @cfg {String/Boolean} shadow True to create a shadow element with default class "x-layer-shadow", or
17049  * you can pass a string with a CSS class name. False turns off the shadow.
17050  * @cfg {Object} dh DomHelper object config to create element with (defaults to {tag: "div", cls: "x-layer"}).
17051  * @cfg {Boolean} constrain False to disable constrain to viewport (defaults to true)
17052  * @cfg {String} cls CSS class to add to the element
17053  * @cfg {Number} zindex Starting z-index (defaults to 11000)
17054  * @cfg {Number} shadowOffset Number of pixels to offset the shadow (defaults to 3)
17055  * @constructor
17056  * @param {Object} config An object with config options.
17057  * @param {String/HTMLElement} existingEl (optional) Uses an existing DOM element. If the element is not found it creates it.
17058  */
17059
17060 Roo.Layer = function(config, existingEl){
17061     config = config || {};
17062     var dh = Roo.DomHelper;
17063     var cp = config.parentEl, pel = cp ? Roo.getDom(cp) : document.body;
17064     if(existingEl){
17065         this.dom = Roo.getDom(existingEl);
17066     }
17067     if(!this.dom){
17068         var o = config.dh || {tag: "div", cls: "x-layer"};
17069         this.dom = dh.append(pel, o);
17070     }
17071     if(config.cls){
17072         this.addClass(config.cls);
17073     }
17074     this.constrain = config.constrain !== false;
17075     this.visibilityMode = Roo.Element.VISIBILITY;
17076     if(config.id){
17077         this.id = this.dom.id = config.id;
17078     }else{
17079         this.id = Roo.id(this.dom);
17080     }
17081     this.zindex = config.zindex || this.getZIndex();
17082     this.position("absolute", this.zindex);
17083     if(config.shadow){
17084         this.shadowOffset = config.shadowOffset || 4;
17085         this.shadow = new Roo.Shadow({
17086             offset : this.shadowOffset,
17087             mode : config.shadow
17088         });
17089     }else{
17090         this.shadowOffset = 0;
17091     }
17092     this.useShim = config.shim !== false && Roo.useShims;
17093     this.useDisplay = config.useDisplay;
17094     this.hide();
17095 };
17096
17097 var supr = Roo.Element.prototype;
17098
17099 // shims are shared among layer to keep from having 100 iframes
17100 var shims = [];
17101
17102 Roo.extend(Roo.Layer, Roo.Element, {
17103
17104     getZIndex : function(){
17105         return this.zindex || parseInt(this.getStyle("z-index"), 10) || 11000;
17106     },
17107
17108     getShim : function(){
17109         if(!this.useShim){
17110             return null;
17111         }
17112         if(this.shim){
17113             return this.shim;
17114         }
17115         var shim = shims.shift();
17116         if(!shim){
17117             shim = this.createShim();
17118             shim.enableDisplayMode('block');
17119             shim.dom.style.display = 'none';
17120             shim.dom.style.visibility = 'visible';
17121         }
17122         var pn = this.dom.parentNode;
17123         if(shim.dom.parentNode != pn){
17124             pn.insertBefore(shim.dom, this.dom);
17125         }
17126         shim.setStyle('z-index', this.getZIndex()-2);
17127         this.shim = shim;
17128         return shim;
17129     },
17130
17131     hideShim : function(){
17132         if(this.shim){
17133             this.shim.setDisplayed(false);
17134             shims.push(this.shim);
17135             delete this.shim;
17136         }
17137     },
17138
17139     disableShadow : function(){
17140         if(this.shadow){
17141             this.shadowDisabled = true;
17142             this.shadow.hide();
17143             this.lastShadowOffset = this.shadowOffset;
17144             this.shadowOffset = 0;
17145         }
17146     },
17147
17148     enableShadow : function(show){
17149         if(this.shadow){
17150             this.shadowDisabled = false;
17151             this.shadowOffset = this.lastShadowOffset;
17152             delete this.lastShadowOffset;
17153             if(show){
17154                 this.sync(true);
17155             }
17156         }
17157     },
17158
17159     // private
17160     // this code can execute repeatedly in milliseconds (i.e. during a drag) so
17161     // code size was sacrificed for effeciency (e.g. no getBox/setBox, no XY calls)
17162     sync : function(doShow){
17163         var sw = this.shadow;
17164         if(!this.updating && this.isVisible() && (sw || this.useShim)){
17165             var sh = this.getShim();
17166
17167             var w = this.getWidth(),
17168                 h = this.getHeight();
17169
17170             var l = this.getLeft(true),
17171                 t = this.getTop(true);
17172
17173             if(sw && !this.shadowDisabled){
17174                 if(doShow && !sw.isVisible()){
17175                     sw.show(this);
17176                 }else{
17177                     sw.realign(l, t, w, h);
17178                 }
17179                 if(sh){
17180                     if(doShow){
17181                        sh.show();
17182                     }
17183                     // fit the shim behind the shadow, so it is shimmed too
17184                     var a = sw.adjusts, s = sh.dom.style;
17185                     s.left = (Math.min(l, l+a.l))+"px";
17186                     s.top = (Math.min(t, t+a.t))+"px";
17187                     s.width = (w+a.w)+"px";
17188                     s.height = (h+a.h)+"px";
17189                 }
17190             }else if(sh){
17191                 if(doShow){
17192                    sh.show();
17193                 }
17194                 sh.setSize(w, h);
17195                 sh.setLeftTop(l, t);
17196             }
17197             
17198         }
17199     },
17200
17201     // private
17202     destroy : function(){
17203         this.hideShim();
17204         if(this.shadow){
17205             this.shadow.hide();
17206         }
17207         this.removeAllListeners();
17208         var pn = this.dom.parentNode;
17209         if(pn){
17210             pn.removeChild(this.dom);
17211         }
17212         Roo.Element.uncache(this.id);
17213     },
17214
17215     remove : function(){
17216         this.destroy();
17217     },
17218
17219     // private
17220     beginUpdate : function(){
17221         this.updating = true;
17222     },
17223
17224     // private
17225     endUpdate : function(){
17226         this.updating = false;
17227         this.sync(true);
17228     },
17229
17230     // private
17231     hideUnders : function(negOffset){
17232         if(this.shadow){
17233             this.shadow.hide();
17234         }
17235         this.hideShim();
17236     },
17237
17238     // private
17239     constrainXY : function(){
17240         if(this.constrain){
17241             var vw = Roo.lib.Dom.getViewWidth(),
17242                 vh = Roo.lib.Dom.getViewHeight();
17243             var s = Roo.get(document).getScroll();
17244
17245             var xy = this.getXY();
17246             var x = xy[0], y = xy[1];   
17247             var w = this.dom.offsetWidth+this.shadowOffset, h = this.dom.offsetHeight+this.shadowOffset;
17248             // only move it if it needs it
17249             var moved = false;
17250             // first validate right/bottom
17251             if((x + w) > vw+s.left){
17252                 x = vw - w - this.shadowOffset;
17253                 moved = true;
17254             }
17255             if((y + h) > vh+s.top){
17256                 y = vh - h - this.shadowOffset;
17257                 moved = true;
17258             }
17259             // then make sure top/left isn't negative
17260             if(x < s.left){
17261                 x = s.left;
17262                 moved = true;
17263             }
17264             if(y < s.top){
17265                 y = s.top;
17266                 moved = true;
17267             }
17268             if(moved){
17269                 if(this.avoidY){
17270                     var ay = this.avoidY;
17271                     if(y <= ay && (y+h) >= ay){
17272                         y = ay-h-5;   
17273                     }
17274                 }
17275                 xy = [x, y];
17276                 this.storeXY(xy);
17277                 supr.setXY.call(this, xy);
17278                 this.sync();
17279             }
17280         }
17281     },
17282
17283     isVisible : function(){
17284         return this.visible;    
17285     },
17286
17287     // private
17288     showAction : function(){
17289         this.visible = true; // track visibility to prevent getStyle calls
17290         if(this.useDisplay === true){
17291             this.setDisplayed("");
17292         }else if(this.lastXY){
17293             supr.setXY.call(this, this.lastXY);
17294         }else if(this.lastLT){
17295             supr.setLeftTop.call(this, this.lastLT[0], this.lastLT[1]);
17296         }
17297     },
17298
17299     // private
17300     hideAction : function(){
17301         this.visible = false;
17302         if(this.useDisplay === true){
17303             this.setDisplayed(false);
17304         }else{
17305             this.setLeftTop(-10000,-10000);
17306         }
17307     },
17308
17309     // overridden Element method
17310     setVisible : function(v, a, d, c, e){
17311         if(v){
17312             this.showAction();
17313         }
17314         if(a && v){
17315             var cb = function(){
17316                 this.sync(true);
17317                 if(c){
17318                     c();
17319                 }
17320             }.createDelegate(this);
17321             supr.setVisible.call(this, true, true, d, cb, e);
17322         }else{
17323             if(!v){
17324                 this.hideUnders(true);
17325             }
17326             var cb = c;
17327             if(a){
17328                 cb = function(){
17329                     this.hideAction();
17330                     if(c){
17331                         c();
17332                     }
17333                 }.createDelegate(this);
17334             }
17335             supr.setVisible.call(this, v, a, d, cb, e);
17336             if(v){
17337                 this.sync(true);
17338             }else if(!a){
17339                 this.hideAction();
17340             }
17341         }
17342     },
17343
17344     storeXY : function(xy){
17345         delete this.lastLT;
17346         this.lastXY = xy;
17347     },
17348
17349     storeLeftTop : function(left, top){
17350         delete this.lastXY;
17351         this.lastLT = [left, top];
17352     },
17353
17354     // private
17355     beforeFx : function(){
17356         this.beforeAction();
17357         return Roo.Layer.superclass.beforeFx.apply(this, arguments);
17358     },
17359
17360     // private
17361     afterFx : function(){
17362         Roo.Layer.superclass.afterFx.apply(this, arguments);
17363         this.sync(this.isVisible());
17364     },
17365
17366     // private
17367     beforeAction : function(){
17368         if(!this.updating && this.shadow){
17369             this.shadow.hide();
17370         }
17371     },
17372
17373     // overridden Element method
17374     setLeft : function(left){
17375         this.storeLeftTop(left, this.getTop(true));
17376         supr.setLeft.apply(this, arguments);
17377         this.sync();
17378     },
17379
17380     setTop : function(top){
17381         this.storeLeftTop(this.getLeft(true), top);
17382         supr.setTop.apply(this, arguments);
17383         this.sync();
17384     },
17385
17386     setLeftTop : function(left, top){
17387         this.storeLeftTop(left, top);
17388         supr.setLeftTop.apply(this, arguments);
17389         this.sync();
17390     },
17391
17392     setXY : function(xy, a, d, c, e){
17393         this.fixDisplay();
17394         this.beforeAction();
17395         this.storeXY(xy);
17396         var cb = this.createCB(c);
17397         supr.setXY.call(this, xy, a, d, cb, e);
17398         if(!a){
17399             cb();
17400         }
17401     },
17402
17403     // private
17404     createCB : function(c){
17405         var el = this;
17406         return function(){
17407             el.constrainXY();
17408             el.sync(true);
17409             if(c){
17410                 c();
17411             }
17412         };
17413     },
17414
17415     // overridden Element method
17416     setX : function(x, a, d, c, e){
17417         this.setXY([x, this.getY()], a, d, c, e);
17418     },
17419
17420     // overridden Element method
17421     setY : function(y, a, d, c, e){
17422         this.setXY([this.getX(), y], a, d, c, e);
17423     },
17424
17425     // overridden Element method
17426     setSize : function(w, h, a, d, c, e){
17427         this.beforeAction();
17428         var cb = this.createCB(c);
17429         supr.setSize.call(this, w, h, a, d, cb, e);
17430         if(!a){
17431             cb();
17432         }
17433     },
17434
17435     // overridden Element method
17436     setWidth : function(w, a, d, c, e){
17437         this.beforeAction();
17438         var cb = this.createCB(c);
17439         supr.setWidth.call(this, w, a, d, cb, e);
17440         if(!a){
17441             cb();
17442         }
17443     },
17444
17445     // overridden Element method
17446     setHeight : function(h, a, d, c, e){
17447         this.beforeAction();
17448         var cb = this.createCB(c);
17449         supr.setHeight.call(this, h, a, d, cb, e);
17450         if(!a){
17451             cb();
17452         }
17453     },
17454
17455     // overridden Element method
17456     setBounds : function(x, y, w, h, a, d, c, e){
17457         this.beforeAction();
17458         var cb = this.createCB(c);
17459         if(!a){
17460             this.storeXY([x, y]);
17461             supr.setXY.call(this, [x, y]);
17462             supr.setSize.call(this, w, h, a, d, cb, e);
17463             cb();
17464         }else{
17465             supr.setBounds.call(this, x, y, w, h, a, d, cb, e);
17466         }
17467         return this;
17468     },
17469     
17470     /**
17471      * Sets the z-index of this layer and adjusts any shadow and shim z-indexes. The layer z-index is automatically
17472      * incremented by two more than the value passed in so that it always shows above any shadow or shim (the shadow
17473      * element, if any, will be assigned z-index + 1, and the shim element, if any, will be assigned the unmodified z-index).
17474      * @param {Number} zindex The new z-index to set
17475      * @return {this} The Layer
17476      */
17477     setZIndex : function(zindex){
17478         this.zindex = zindex;
17479         this.setStyle("z-index", zindex + 2);
17480         if(this.shadow){
17481             this.shadow.setZIndex(zindex + 1);
17482         }
17483         if(this.shim){
17484             this.shim.setStyle("z-index", zindex);
17485         }
17486     }
17487 });
17488 })();/*
17489  * Original code for Roojs - LGPL
17490  * <script type="text/javascript">
17491  */
17492  
17493 /**
17494  * @class Roo.XComponent
17495  * A delayed Element creator...
17496  * Or a way to group chunks of interface together.
17497  * technically this is a wrapper around a tree of Roo elements (which defines a 'module'),
17498  *  used in conjunction with XComponent.build() it will create an instance of each element,
17499  *  then call addxtype() to build the User interface.
17500  * 
17501  * Mypart.xyx = new Roo.XComponent({
17502
17503     parent : 'Mypart.xyz', // empty == document.element.!!
17504     order : '001',
17505     name : 'xxxx'
17506     region : 'xxxx'
17507     disabled : function() {} 
17508      
17509     tree : function() { // return an tree of xtype declared components
17510         var MODULE = this;
17511         return 
17512         {
17513             xtype : 'NestedLayoutPanel',
17514             // technicall
17515         }
17516      ]
17517  *})
17518  *
17519  *
17520  * It can be used to build a big heiracy, with parent etc.
17521  * or you can just use this to render a single compoent to a dom element
17522  * MYPART.render(Roo.Element | String(id) | dom_element )
17523  *
17524  *
17525  * Usage patterns.
17526  *
17527  * Classic Roo
17528  *
17529  * Roo is designed primarily as a single page application, so the UI build for a standard interface will
17530  * expect a single 'TOP' level module normally indicated by the 'parent' of the XComponent definition being defined as false.
17531  *
17532  * Each sub module is expected to have a parent pointing to the class name of it's parent module.
17533  *
17534  * When the top level is false, a 'Roo.BorderLayout' is created and the element is flagged as 'topModule'
17535  * - if mulitple topModules exist, the last one is defined as the top module.
17536  *
17537  * Embeded Roo
17538  * 
17539  * When the top level or multiple modules are to embedded into a existing HTML page,
17540  * the parent element can container '#id' of the element where the module will be drawn.
17541  *
17542  * Bootstrap Roo
17543  *
17544  * Unlike classic Roo, the bootstrap tends not to be used as a single page.
17545  * it relies more on a include mechanism, where sub modules are included into an outer page.
17546  * This is normally managed by the builder tools using Roo.apply( options, Included.Sub.Module )
17547  * 
17548  * Bootstrap Roo Included elements
17549  *
17550  * Our builder application needs the ability to preview these sub compoennts. They will normally have parent=false set,
17551  * hence confusing the component builder as it thinks there are multiple top level elements. 
17552  *
17553  * String Over-ride & Translations
17554  *
17555  * Our builder application writes all the strings as _strings and _named_strings. This is to enable the translation of elements,
17556  * and also the 'overlaying of string values - needed when different versions of the same application with different text content
17557  * are needed. @see Roo.XComponent.overlayString  
17558  * 
17559  * 
17560  * 
17561  * @extends Roo.util.Observable
17562  * @constructor
17563  * @param cfg {Object} configuration of component
17564  * 
17565  */
17566 Roo.XComponent = function(cfg) {
17567     Roo.apply(this, cfg);
17568     this.addEvents({ 
17569         /**
17570              * @event built
17571              * Fires when this the componnt is built
17572              * @param {Roo.XComponent} c the component
17573              */
17574         'built' : true
17575         
17576     });
17577     this.region = this.region || 'center'; // default..
17578     Roo.XComponent.register(this);
17579     this.modules = false;
17580     this.el = false; // where the layout goes..
17581     
17582     
17583 }
17584 Roo.extend(Roo.XComponent, Roo.util.Observable, {
17585     /**
17586      * @property el
17587      * The created element (with Roo.factory())
17588      * @type {Roo.Layout}
17589      */
17590     el  : false,
17591     
17592     /**
17593      * @property el
17594      * for BC  - use el in new code
17595      * @type {Roo.Layout}
17596      */
17597     panel : false,
17598     
17599     /**
17600      * @property layout
17601      * for BC  - use el in new code
17602      * @type {Roo.Layout}
17603      */
17604     layout : false,
17605     
17606      /**
17607      * @cfg {Function|boolean} disabled
17608      * If this module is disabled by some rule, return true from the funtion
17609      */
17610     disabled : false,
17611     
17612     /**
17613      * @cfg {String} parent 
17614      * Name of parent element which it get xtype added to..
17615      */
17616     parent: false,
17617     
17618     /**
17619      * @cfg {String} order
17620      * Used to set the order in which elements are created (usefull for multiple tabs)
17621      */
17622     
17623     order : false,
17624     /**
17625      * @cfg {String} name
17626      * String to display while loading.
17627      */
17628     name : false,
17629     /**
17630      * @cfg {String} region
17631      * Region to render component to (defaults to center)
17632      */
17633     region : 'center',
17634     
17635     /**
17636      * @cfg {Array} items
17637      * A single item array - the first element is the root of the tree..
17638      * It's done this way to stay compatible with the Xtype system...
17639      */
17640     items : false,
17641     
17642     /**
17643      * @property _tree
17644      * The method that retuns the tree of parts that make up this compoennt 
17645      * @type {function}
17646      */
17647     _tree  : false,
17648     
17649      /**
17650      * render
17651      * render element to dom or tree
17652      * @param {Roo.Element|String|DomElement} optional render to if parent is not set.
17653      */
17654     
17655     render : function(el)
17656     {
17657         
17658         el = el || false;
17659         var hp = this.parent ? 1 : 0;
17660         Roo.debug &&  Roo.log(this);
17661         
17662         var tree = this._tree ? this._tree() : this.tree();
17663
17664         
17665         if (!el && typeof(this.parent) == 'string' && this.parent.substring(0,1) == '#') {
17666             // if parent is a '#.....' string, then let's use that..
17667             var ename = this.parent.substr(1);
17668             this.parent = false;
17669             Roo.debug && Roo.log(ename);
17670             switch (ename) {
17671                 case 'bootstrap-body':
17672                     if (typeof(tree.el) != 'undefined' && tree.el == document.body)  {
17673                         // this is the BorderLayout standard?
17674                        this.parent = { el : true };
17675                        break;
17676                     }
17677                     if (["Nest", "Content", "Grid", "Tree"].indexOf(tree.xtype)  > -1)  {
17678                         // need to insert stuff...
17679                         this.parent =  {
17680                              el : new Roo.bootstrap.layout.Border({
17681                                  el : document.body, 
17682                      
17683                                  center: {
17684                                     titlebar: false,
17685                                     autoScroll:false,
17686                                     closeOnTab: true,
17687                                     tabPosition: 'top',
17688                                       //resizeTabs: true,
17689                                     alwaysShowTabs: true,
17690                                     hideTabs: false
17691                                      //minTabWidth: 140
17692                                  }
17693                              })
17694                         
17695                          };
17696                          break;
17697                     }
17698                          
17699                     if (typeof(Roo.bootstrap.Body) != 'undefined' ) {
17700                         this.parent = { el :  new  Roo.bootstrap.Body() };
17701                         Roo.debug && Roo.log("setting el to doc body");
17702                          
17703                     } else {
17704                         throw "Container is bootstrap body, but Roo.bootstrap.Body is not defined";
17705                     }
17706                     break;
17707                 case 'bootstrap':
17708                     this.parent = { el : true};
17709                     // fall through
17710                 default:
17711                     el = Roo.get(ename);
17712                     if (typeof(Roo.bootstrap) != 'undefined' && tree['|xns'] == 'Roo.bootstrap') {
17713                         this.parent = { el : true};
17714                     }
17715                     
17716                     break;
17717             }
17718                 
17719             
17720             if (!el && !this.parent) {
17721                 Roo.debug && Roo.log("Warning - element can not be found :#" + ename );
17722                 return;
17723             }
17724         }
17725         
17726         Roo.debug && Roo.log("EL:");
17727         Roo.debug && Roo.log(el);
17728         Roo.debug && Roo.log("this.parent.el:");
17729         Roo.debug && Roo.log(this.parent.el);
17730         
17731
17732         // altertive root elements ??? - we need a better way to indicate these.
17733         var is_alt = Roo.XComponent.is_alt ||
17734                     (typeof(tree.el) != 'undefined' && tree.el == document.body) ||
17735                     (typeof(Roo.bootstrap) != 'undefined' && tree.xns == Roo.bootstrap) ||
17736                     (typeof(Roo.mailer) != 'undefined' && tree.xns == Roo.mailer) ;
17737         
17738         
17739         
17740         if (!this.parent && is_alt) {
17741             //el = Roo.get(document.body);
17742             this.parent = { el : true };
17743         }
17744             
17745             
17746         
17747         if (!this.parent) {
17748             
17749             Roo.debug && Roo.log("no parent - creating one");
17750             
17751             el = el ? Roo.get(el) : false;      
17752             
17753             if (typeof(Roo.BorderLayout) == 'undefined' ) {
17754                 
17755                 this.parent =  {
17756                     el : new Roo.bootstrap.layout.Border({
17757                         el: el || document.body,
17758                     
17759                         center: {
17760                             titlebar: false,
17761                             autoScroll:false,
17762                             closeOnTab: true,
17763                             tabPosition: 'top',
17764                              //resizeTabs: true,
17765                             alwaysShowTabs: false,
17766                             hideTabs: true,
17767                             minTabWidth: 140,
17768                             overflow: 'visible'
17769                          }
17770                      })
17771                 };
17772             } else {
17773             
17774                 // it's a top level one..
17775                 this.parent =  {
17776                     el : new Roo.BorderLayout(el || document.body, {
17777                         center: {
17778                             titlebar: false,
17779                             autoScroll:false,
17780                             closeOnTab: true,
17781                             tabPosition: 'top',
17782                              //resizeTabs: true,
17783                             alwaysShowTabs: el && hp? false :  true,
17784                             hideTabs: el || !hp ? true :  false,
17785                             minTabWidth: 140
17786                          }
17787                     })
17788                 };
17789             }
17790         }
17791         
17792         if (!this.parent.el) {
17793                 // probably an old style ctor, which has been disabled.
17794                 return;
17795
17796         }
17797                 // The 'tree' method is  '_tree now' 
17798             
17799         tree.region = tree.region || this.region;
17800         var is_body = false;
17801         if (this.parent.el === true) {
17802             // bootstrap... - body..
17803             if (el) {
17804                 tree.el = el;
17805             }
17806             this.parent.el = Roo.factory(tree);
17807             is_body = true;
17808         }
17809         
17810         this.el = this.parent.el.addxtype(tree, undefined, is_body);
17811         this.fireEvent('built', this);
17812         
17813         this.panel = this.el;
17814         this.layout = this.panel.layout;
17815         this.parentLayout = this.parent.layout  || false;  
17816          
17817     }
17818     
17819 });
17820
17821 Roo.apply(Roo.XComponent, {
17822     /**
17823      * @property  hideProgress
17824      * true to disable the building progress bar.. usefull on single page renders.
17825      * @type Boolean
17826      */
17827     hideProgress : false,
17828     /**
17829      * @property  buildCompleted
17830      * True when the builder has completed building the interface.
17831      * @type Boolean
17832      */
17833     buildCompleted : false,
17834      
17835     /**
17836      * @property  topModule
17837      * the upper most module - uses document.element as it's constructor.
17838      * @type Object
17839      */
17840      
17841     topModule  : false,
17842       
17843     /**
17844      * @property  modules
17845      * array of modules to be created by registration system.
17846      * @type {Array} of Roo.XComponent
17847      */
17848     
17849     modules : [],
17850     /**
17851      * @property  elmodules
17852      * array of modules to be created by which use #ID 
17853      * @type {Array} of Roo.XComponent
17854      */
17855      
17856     elmodules : [],
17857
17858      /**
17859      * @property  is_alt
17860      * Is an alternative Root - normally used by bootstrap or other systems,
17861      *    where the top element in the tree can wrap 'body' 
17862      * @type {boolean}  (default false)
17863      */
17864      
17865     is_alt : false,
17866     /**
17867      * @property  build_from_html
17868      * Build elements from html - used by bootstrap HTML stuff 
17869      *    - this is cleared after build is completed
17870      * @type {boolean}    (default false)
17871      */
17872      
17873     build_from_html : false,
17874     /**
17875      * Register components to be built later.
17876      *
17877      * This solves the following issues
17878      * - Building is not done on page load, but after an authentication process has occured.
17879      * - Interface elements are registered on page load
17880      * - Parent Interface elements may not be loaded before child, so this handles that..
17881      * 
17882      *
17883      * example:
17884      * 
17885      * MyApp.register({
17886           order : '000001',
17887           module : 'Pman.Tab.projectMgr',
17888           region : 'center',
17889           parent : 'Pman.layout',
17890           disabled : false,  // or use a function..
17891         })
17892      
17893      * * @param {Object} details about module
17894      */
17895     register : function(obj) {
17896                 
17897         Roo.XComponent.event.fireEvent('register', obj);
17898         switch(typeof(obj.disabled) ) {
17899                 
17900             case 'undefined':
17901                 break;
17902             
17903             case 'function':
17904                 if ( obj.disabled() ) {
17905                         return;
17906                 }
17907                 break;
17908             
17909             default:
17910                 if (obj.disabled || obj.region == '#disabled') {
17911                         return;
17912                 }
17913                 break;
17914         }
17915                 
17916         this.modules.push(obj);
17917          
17918     },
17919     /**
17920      * convert a string to an object..
17921      * eg. 'AAA.BBB' -> finds AAA.BBB
17922
17923      */
17924     
17925     toObject : function(str)
17926     {
17927         if (!str || typeof(str) == 'object') {
17928             return str;
17929         }
17930         if (str.substring(0,1) == '#') {
17931             return str;
17932         }
17933
17934         var ar = str.split('.');
17935         var rt, o;
17936         rt = ar.shift();
17937             /** eval:var:o */
17938         try {
17939             eval('if (typeof ' + rt + ' == "undefined"){ o = false;} o = ' + rt + ';');
17940         } catch (e) {
17941             throw "Module not found : " + str;
17942         }
17943         
17944         if (o === false) {
17945             throw "Module not found : " + str;
17946         }
17947         Roo.each(ar, function(e) {
17948             if (typeof(o[e]) == 'undefined') {
17949                 throw "Module not found : " + str;
17950             }
17951             o = o[e];
17952         });
17953         
17954         return o;
17955         
17956     },
17957     
17958     
17959     /**
17960      * move modules into their correct place in the tree..
17961      * 
17962      */
17963     preBuild : function ()
17964     {
17965         var _t = this;
17966         Roo.each(this.modules , function (obj)
17967         {
17968             Roo.XComponent.event.fireEvent('beforebuild', obj);
17969             
17970             var opar = obj.parent;
17971             try { 
17972                 obj.parent = this.toObject(opar);
17973             } catch(e) {
17974                 Roo.debug && Roo.log("parent:toObject failed: " + e.toString());
17975                 return;
17976             }
17977             
17978             if (!obj.parent) {
17979                 Roo.debug && Roo.log("GOT top level module");
17980                 Roo.debug && Roo.log(obj);
17981                 obj.modules = new Roo.util.MixedCollection(false, 
17982                     function(o) { return o.order + '' }
17983                 );
17984                 this.topModule = obj;
17985                 return;
17986             }
17987                         // parent is a string (usually a dom element name..)
17988             if (typeof(obj.parent) == 'string') {
17989                 this.elmodules.push(obj);
17990                 return;
17991             }
17992             if (obj.parent.constructor != Roo.XComponent) {
17993                 Roo.debug && Roo.log("Warning : Object Parent is not instance of XComponent:" + obj.name)
17994             }
17995             if (!obj.parent.modules) {
17996                 obj.parent.modules = new Roo.util.MixedCollection(false, 
17997                     function(o) { return o.order + '' }
17998                 );
17999             }
18000             if (obj.parent.disabled) {
18001                 obj.disabled = true;
18002             }
18003             obj.parent.modules.add(obj);
18004         }, this);
18005     },
18006     
18007      /**
18008      * make a list of modules to build.
18009      * @return {Array} list of modules. 
18010      */ 
18011     
18012     buildOrder : function()
18013     {
18014         var _this = this;
18015         var cmp = function(a,b) {   
18016             return String(a).toUpperCase() > String(b).toUpperCase() ? 1 : -1;
18017         };
18018         if ((!this.topModule || !this.topModule.modules) && !this.elmodules.length) {
18019             throw "No top level modules to build";
18020         }
18021         
18022         // make a flat list in order of modules to build.
18023         var mods = this.topModule ? [ this.topModule ] : [];
18024                 
18025         
18026         // elmodules (is a list of DOM based modules )
18027         Roo.each(this.elmodules, function(e) {
18028             mods.push(e);
18029             if (!this.topModule &&
18030                 typeof(e.parent) == 'string' &&
18031                 e.parent.substring(0,1) == '#' &&
18032                 Roo.get(e.parent.substr(1))
18033                ) {
18034                 
18035                 _this.topModule = e;
18036             }
18037             
18038         });
18039
18040         
18041         // add modules to their parents..
18042         var addMod = function(m) {
18043             Roo.debug && Roo.log("build Order: add: " + m.name);
18044                 
18045             mods.push(m);
18046             if (m.modules && !m.disabled) {
18047                 Roo.debug && Roo.log("build Order: " + m.modules.length + " child modules");
18048                 m.modules.keySort('ASC',  cmp );
18049                 Roo.debug && Roo.log("build Order: " + m.modules.length + " child modules (after sort)");
18050     
18051                 m.modules.each(addMod);
18052             } else {
18053                 Roo.debug && Roo.log("build Order: no child modules");
18054             }
18055             // not sure if this is used any more..
18056             if (m.finalize) {
18057                 m.finalize.name = m.name + " (clean up) ";
18058                 mods.push(m.finalize);
18059             }
18060             
18061         }
18062         if (this.topModule && this.topModule.modules) { 
18063             this.topModule.modules.keySort('ASC',  cmp );
18064             this.topModule.modules.each(addMod);
18065         } 
18066         return mods;
18067     },
18068     
18069      /**
18070      * Build the registered modules.
18071      * @param {Object} parent element.
18072      * @param {Function} optional method to call after module has been added.
18073      * 
18074      */ 
18075    
18076     build : function(opts) 
18077     {
18078         
18079         if (typeof(opts) != 'undefined') {
18080             Roo.apply(this,opts);
18081         }
18082         
18083         this.preBuild();
18084         var mods = this.buildOrder();
18085       
18086         //this.allmods = mods;
18087         //Roo.debug && Roo.log(mods);
18088         //return;
18089         if (!mods.length) { // should not happen
18090             throw "NO modules!!!";
18091         }
18092         
18093         
18094         var msg = "Building Interface...";
18095         // flash it up as modal - so we store the mask!?
18096         if (!this.hideProgress && Roo.MessageBox) {
18097             Roo.MessageBox.show({ title: 'loading' });
18098             Roo.MessageBox.show({
18099                title: "Please wait...",
18100                msg: msg,
18101                width:450,
18102                progress:true,
18103                buttons : false,
18104                closable:false,
18105                modal: false
18106               
18107             });
18108         }
18109         var total = mods.length;
18110         
18111         var _this = this;
18112         var progressRun = function() {
18113             if (!mods.length) {
18114                 Roo.debug && Roo.log('hide?');
18115                 if (!this.hideProgress && Roo.MessageBox) {
18116                     Roo.MessageBox.hide();
18117                 }
18118                 Roo.XComponent.build_from_html = false; // reset, so dialogs will be build from javascript
18119                 
18120                 Roo.XComponent.event.fireEvent('buildcomplete', _this.topModule);
18121                 
18122                 // THE END...
18123                 return false;   
18124             }
18125             
18126             var m = mods.shift();
18127             
18128             
18129             Roo.debug && Roo.log(m);
18130             // not sure if this is supported any more.. - modules that are are just function
18131             if (typeof(m) == 'function') { 
18132                 m.call(this);
18133                 return progressRun.defer(10, _this);
18134             } 
18135             
18136             
18137             msg = "Building Interface " + (total  - mods.length) + 
18138                     " of " + total + 
18139                     (m.name ? (' - ' + m.name) : '');
18140                         Roo.debug && Roo.log(msg);
18141             if (!_this.hideProgress &&  Roo.MessageBox) { 
18142                 Roo.MessageBox.updateProgress(  (total  - mods.length)/total, msg  );
18143             }
18144             
18145          
18146             // is the module disabled?
18147             var disabled = (typeof(m.disabled) == 'function') ?
18148                 m.disabled.call(m.module.disabled) : m.disabled;    
18149             
18150             
18151             if (disabled) {
18152                 return progressRun(); // we do not update the display!
18153             }
18154             
18155             // now build 
18156             
18157                         
18158                         
18159             m.render();
18160             // it's 10 on top level, and 1 on others??? why...
18161             return progressRun.defer(10, _this);
18162              
18163         }
18164         progressRun.defer(1, _this);
18165      
18166         
18167         
18168     },
18169     /**
18170      * Overlay a set of modified strings onto a component
18171      * This is dependant on our builder exporting the strings and 'named strings' elements.
18172      * 
18173      * @param {Object} element to overlay on - eg. Pman.Dialog.Login
18174      * @param {Object} associative array of 'named' string and it's new value.
18175      * 
18176      */
18177         overlayStrings : function( component, strings )
18178     {
18179         if (typeof(component['_named_strings']) == 'undefined') {
18180             throw "ERROR: component does not have _named_strings";
18181         }
18182         for ( var k in strings ) {
18183             var md = typeof(component['_named_strings'][k]) == 'undefined' ? false : component['_named_strings'][k];
18184             if (md !== false) {
18185                 component['_strings'][md] = strings[k];
18186             } else {
18187                 Roo.log('could not find named string: ' + k + ' in');
18188                 Roo.log(component);
18189             }
18190             
18191         }
18192         
18193     },
18194     
18195         
18196         /**
18197          * Event Object.
18198          *
18199          *
18200          */
18201         event: false, 
18202     /**
18203          * wrapper for event.on - aliased later..  
18204          * Typically use to register a event handler for register:
18205          *
18206          * eg. Roo.XComponent.on('register', function(comp) { comp.disable = true } );
18207          *
18208          */
18209     on : false
18210    
18211     
18212     
18213 });
18214
18215 Roo.XComponent.event = new Roo.util.Observable({
18216                 events : { 
18217                         /**
18218                          * @event register
18219                          * Fires when an Component is registered,
18220                          * set the disable property on the Component to stop registration.
18221                          * @param {Roo.XComponent} c the component being registerd.
18222                          * 
18223                          */
18224                         'register' : true,
18225             /**
18226                          * @event beforebuild
18227                          * Fires before each Component is built
18228                          * can be used to apply permissions.
18229                          * @param {Roo.XComponent} c the component being registerd.
18230                          * 
18231                          */
18232                         'beforebuild' : true,
18233                         /**
18234                          * @event buildcomplete
18235                          * Fires on the top level element when all elements have been built
18236                          * @param {Roo.XComponent} the top level component.
18237                          */
18238                         'buildcomplete' : true
18239                         
18240                 }
18241 });
18242
18243 Roo.XComponent.on = Roo.XComponent.event.on.createDelegate(Roo.XComponent.event); 
18244  //
18245  /**
18246  * marked - a markdown parser
18247  * Copyright (c) 2011-2014, Christopher Jeffrey. (MIT Licensed)
18248  * https://github.com/chjj/marked
18249  */
18250
18251
18252 /**
18253  *
18254  * Roo.Markdown - is a very crude wrapper around marked..
18255  *
18256  * usage:
18257  * 
18258  * alert( Roo.Markdown.toHtml("Markdown *rocks*.") );
18259  * 
18260  * Note: move the sample code to the bottom of this
18261  * file before uncommenting it.
18262  *
18263  */
18264
18265 Roo.Markdown = {};
18266 Roo.Markdown.toHtml = function(text) {
18267     
18268     var c = new Roo.Markdown.marked.setOptions({
18269             renderer: new Roo.Markdown.marked.Renderer(),
18270             gfm: true,
18271             tables: true,
18272             breaks: false,
18273             pedantic: false,
18274             sanitize: false,
18275             smartLists: true,
18276             smartypants: false
18277           });
18278     // A FEW HACKS!!?
18279     
18280     text = text.replace(/\\\n/g,' ');
18281     return Roo.Markdown.marked(text);
18282 };
18283 //
18284 // converter
18285 //
18286 // Wraps all "globals" so that the only thing
18287 // exposed is makeHtml().
18288 //
18289 (function() {
18290     
18291      /**
18292          * eval:var:escape
18293          * eval:var:unescape
18294          * eval:var:replace
18295          */
18296       
18297     /**
18298      * Helpers
18299      */
18300     
18301     var escape = function (html, encode) {
18302       return html
18303         .replace(!encode ? /&(?!#?\w+;)/g : /&/g, '&amp;')
18304         .replace(/</g, '&lt;')
18305         .replace(/>/g, '&gt;')
18306         .replace(/"/g, '&quot;')
18307         .replace(/'/g, '&#39;');
18308     }
18309     
18310     var unescape = function (html) {
18311         // explicitly match decimal, hex, and named HTML entities 
18312       return html.replace(/&(#(?:\d+)|(?:#x[0-9A-Fa-f]+)|(?:\w+));?/g, function(_, n) {
18313         n = n.toLowerCase();
18314         if (n === 'colon') { return ':'; }
18315         if (n.charAt(0) === '#') {
18316           return n.charAt(1) === 'x'
18317             ? String.fromCharCode(parseInt(n.substring(2), 16))
18318             : String.fromCharCode(+n.substring(1));
18319         }
18320         return '';
18321       });
18322     }
18323     
18324     var replace = function (regex, opt) {
18325       regex = regex.source;
18326       opt = opt || '';
18327       return function self(name, val) {
18328         if (!name) { return new RegExp(regex, opt); }
18329         val = val.source || val;
18330         val = val.replace(/(^|[^\[])\^/g, '$1');
18331         regex = regex.replace(name, val);
18332         return self;
18333       };
18334     }
18335
18336
18337          /**
18338          * eval:var:noop
18339     */
18340     var noop = function () {}
18341     noop.exec = noop;
18342     
18343          /**
18344          * eval:var:merge
18345     */
18346     var merge = function (obj) {
18347       var i = 1
18348         , target
18349         , key;
18350     
18351       for (; i < arguments.length; i++) {
18352         target = arguments[i];
18353         for (key in target) {
18354           if (Object.prototype.hasOwnProperty.call(target, key)) {
18355             obj[key] = target[key];
18356           }
18357         }
18358       }
18359     
18360       return obj;
18361     }
18362     
18363     
18364     /**
18365      * Block-Level Grammar
18366      */
18367     
18368     
18369     
18370     
18371     var block = {
18372       newline: /^\n+/,
18373       code: /^( {4}[^\n]+\n*)+/,
18374       fences: noop,
18375       hr: /^( *[-*_]){3,} *(?:\n+|$)/,
18376       heading: /^ *(#{1,6}) *([^\n]+?) *#* *(?:\n+|$)/,
18377       nptable: noop,
18378       lheading: /^([^\n]+)\n *(=|-){2,} *(?:\n+|$)/,
18379       blockquote: /^( *>[^\n]+(\n(?!def)[^\n]+)*\n*)+/,
18380       list: /^( *)(bull) [\s\S]+?(?:hr|def|\n{2,}(?! )(?!\1bull )\n*|\s*$)/,
18381       html: /^ *(?:comment *(?:\n|\s*$)|closed *(?:\n{2,}|\s*$)|closing *(?:\n{2,}|\s*$))/,
18382       def: /^ *\[([^\]]+)\]: *<?([^\s>]+)>?(?: +["(]([^\n]+)[")])? *(?:\n+|$)/,
18383       table: noop,
18384       paragraph: /^((?:[^\n]+\n?(?!hr|heading|lheading|blockquote|tag|def))+)\n*/,
18385       text: /^[^\n]+/
18386     };
18387     
18388     block.bullet = /(?:[*+-]|\d+\.)/;
18389     block.item = /^( *)(bull) [^\n]*(?:\n(?!\1bull )[^\n]*)*/;
18390     block.item = replace(block.item, 'gm')
18391       (/bull/g, block.bullet)
18392       ();
18393     
18394     block.list = replace(block.list)
18395       (/bull/g, block.bullet)
18396       ('hr', '\\n+(?=\\1?(?:[-*_] *){3,}(?:\\n+|$))')
18397       ('def', '\\n+(?=' + block.def.source + ')')
18398       ();
18399     
18400     block.blockquote = replace(block.blockquote)
18401       ('def', block.def)
18402       ();
18403     
18404     block._tag = '(?!(?:'
18405       + 'a|em|strong|small|s|cite|q|dfn|abbr|data|time|code'
18406       + '|var|samp|kbd|sub|sup|i|b|u|mark|ruby|rt|rp|bdi|bdo'
18407       + '|span|br|wbr|ins|del|img)\\b)\\w+(?!:/|[^\\w\\s@]*@)\\b';
18408     
18409     block.html = replace(block.html)
18410       ('comment', /<!--[\s\S]*?-->/)
18411       ('closed', /<(tag)[\s\S]+?<\/\1>/)
18412       ('closing', /<tag(?:"[^"]*"|'[^']*'|[^'">])*?>/)
18413       (/tag/g, block._tag)
18414       ();
18415     
18416     block.paragraph = replace(block.paragraph)
18417       ('hr', block.hr)
18418       ('heading', block.heading)
18419       ('lheading', block.lheading)
18420       ('blockquote', block.blockquote)
18421       ('tag', '<' + block._tag)
18422       ('def', block.def)
18423       ();
18424     
18425     /**
18426      * Normal Block Grammar
18427      */
18428     
18429     block.normal = merge({}, block);
18430     
18431     /**
18432      * GFM Block Grammar
18433      */
18434     
18435     block.gfm = merge({}, block.normal, {
18436       fences: /^ *(`{3,}|~{3,})[ \.]*(\S+)? *\n([\s\S]*?)\s*\1 *(?:\n+|$)/,
18437       paragraph: /^/,
18438       heading: /^ *(#{1,6}) +([^\n]+?) *#* *(?:\n+|$)/
18439     });
18440     
18441     block.gfm.paragraph = replace(block.paragraph)
18442       ('(?!', '(?!'
18443         + block.gfm.fences.source.replace('\\1', '\\2') + '|'
18444         + block.list.source.replace('\\1', '\\3') + '|')
18445       ();
18446     
18447     /**
18448      * GFM + Tables Block Grammar
18449      */
18450     
18451     block.tables = merge({}, block.gfm, {
18452       nptable: /^ *(\S.*\|.*)\n *([-:]+ *\|[-| :]*)\n((?:.*\|.*(?:\n|$))*)\n*/,
18453       table: /^ *\|(.+)\n *\|( *[-:]+[-| :]*)\n((?: *\|.*(?:\n|$))*)\n*/
18454     });
18455     
18456     /**
18457      * Block Lexer
18458      */
18459     
18460     var Lexer = function (options) {
18461       this.tokens = [];
18462       this.tokens.links = {};
18463       this.options = options || marked.defaults;
18464       this.rules = block.normal;
18465     
18466       if (this.options.gfm) {
18467         if (this.options.tables) {
18468           this.rules = block.tables;
18469         } else {
18470           this.rules = block.gfm;
18471         }
18472       }
18473     }
18474     
18475     /**
18476      * Expose Block Rules
18477      */
18478     
18479     Lexer.rules = block;
18480     
18481     /**
18482      * Static Lex Method
18483      */
18484     
18485     Lexer.lex = function(src, options) {
18486       var lexer = new Lexer(options);
18487       return lexer.lex(src);
18488     };
18489     
18490     /**
18491      * Preprocessing
18492      */
18493     
18494     Lexer.prototype.lex = function(src) {
18495       src = src
18496         .replace(/\r\n|\r/g, '\n')
18497         .replace(/\t/g, '    ')
18498         .replace(/\u00a0/g, ' ')
18499         .replace(/\u2424/g, '\n');
18500     
18501       return this.token(src, true);
18502     };
18503     
18504     /**
18505      * Lexing
18506      */
18507     
18508     Lexer.prototype.token = function(src, top, bq) {
18509       var src = src.replace(/^ +$/gm, '')
18510         , next
18511         , loose
18512         , cap
18513         , bull
18514         , b
18515         , item
18516         , space
18517         , i
18518         , l;
18519     
18520       while (src) {
18521         // newline
18522         if (cap = this.rules.newline.exec(src)) {
18523           src = src.substring(cap[0].length);
18524           if (cap[0].length > 1) {
18525             this.tokens.push({
18526               type: 'space'
18527             });
18528           }
18529         }
18530     
18531         // code
18532         if (cap = this.rules.code.exec(src)) {
18533           src = src.substring(cap[0].length);
18534           cap = cap[0].replace(/^ {4}/gm, '');
18535           this.tokens.push({
18536             type: 'code',
18537             text: !this.options.pedantic
18538               ? cap.replace(/\n+$/, '')
18539               : cap
18540           });
18541           continue;
18542         }
18543     
18544         // fences (gfm)
18545         if (cap = this.rules.fences.exec(src)) {
18546           src = src.substring(cap[0].length);
18547           this.tokens.push({
18548             type: 'code',
18549             lang: cap[2],
18550             text: cap[3] || ''
18551           });
18552           continue;
18553         }
18554     
18555         // heading
18556         if (cap = this.rules.heading.exec(src)) {
18557           src = src.substring(cap[0].length);
18558           this.tokens.push({
18559             type: 'heading',
18560             depth: cap[1].length,
18561             text: cap[2]
18562           });
18563           continue;
18564         }
18565     
18566         // table no leading pipe (gfm)
18567         if (top && (cap = this.rules.nptable.exec(src))) {
18568           src = src.substring(cap[0].length);
18569     
18570           item = {
18571             type: 'table',
18572             header: cap[1].replace(/^ *| *\| *$/g, '').split(/ *\| */),
18573             align: cap[2].replace(/^ *|\| *$/g, '').split(/ *\| */),
18574             cells: cap[3].replace(/\n$/, '').split('\n')
18575           };
18576     
18577           for (i = 0; i < item.align.length; i++) {
18578             if (/^ *-+: *$/.test(item.align[i])) {
18579               item.align[i] = 'right';
18580             } else if (/^ *:-+: *$/.test(item.align[i])) {
18581               item.align[i] = 'center';
18582             } else if (/^ *:-+ *$/.test(item.align[i])) {
18583               item.align[i] = 'left';
18584             } else {
18585               item.align[i] = null;
18586             }
18587           }
18588     
18589           for (i = 0; i < item.cells.length; i++) {
18590             item.cells[i] = item.cells[i].split(/ *\| */);
18591           }
18592     
18593           this.tokens.push(item);
18594     
18595           continue;
18596         }
18597     
18598         // lheading
18599         if (cap = this.rules.lheading.exec(src)) {
18600           src = src.substring(cap[0].length);
18601           this.tokens.push({
18602             type: 'heading',
18603             depth: cap[2] === '=' ? 1 : 2,
18604             text: cap[1]
18605           });
18606           continue;
18607         }
18608     
18609         // hr
18610         if (cap = this.rules.hr.exec(src)) {
18611           src = src.substring(cap[0].length);
18612           this.tokens.push({
18613             type: 'hr'
18614           });
18615           continue;
18616         }
18617     
18618         // blockquote
18619         if (cap = this.rules.blockquote.exec(src)) {
18620           src = src.substring(cap[0].length);
18621     
18622           this.tokens.push({
18623             type: 'blockquote_start'
18624           });
18625     
18626           cap = cap[0].replace(/^ *> ?/gm, '');
18627     
18628           // Pass `top` to keep the current
18629           // "toplevel" state. This is exactly
18630           // how markdown.pl works.
18631           this.token(cap, top, true);
18632     
18633           this.tokens.push({
18634             type: 'blockquote_end'
18635           });
18636     
18637           continue;
18638         }
18639     
18640         // list
18641         if (cap = this.rules.list.exec(src)) {
18642           src = src.substring(cap[0].length);
18643           bull = cap[2];
18644     
18645           this.tokens.push({
18646             type: 'list_start',
18647             ordered: bull.length > 1
18648           });
18649     
18650           // Get each top-level item.
18651           cap = cap[0].match(this.rules.item);
18652     
18653           next = false;
18654           l = cap.length;
18655           i = 0;
18656     
18657           for (; i < l; i++) {
18658             item = cap[i];
18659     
18660             // Remove the list item's bullet
18661             // so it is seen as the next token.
18662             space = item.length;
18663             item = item.replace(/^ *([*+-]|\d+\.) +/, '');
18664     
18665             // Outdent whatever the
18666             // list item contains. Hacky.
18667             if (~item.indexOf('\n ')) {
18668               space -= item.length;
18669               item = !this.options.pedantic
18670                 ? item.replace(new RegExp('^ {1,' + space + '}', 'gm'), '')
18671                 : item.replace(/^ {1,4}/gm, '');
18672             }
18673     
18674             // Determine whether the next list item belongs here.
18675             // Backpedal if it does not belong in this list.
18676             if (this.options.smartLists && i !== l - 1) {
18677               b = block.bullet.exec(cap[i + 1])[0];
18678               if (bull !== b && !(bull.length > 1 && b.length > 1)) {
18679                 src = cap.slice(i + 1).join('\n') + src;
18680                 i = l - 1;
18681               }
18682             }
18683     
18684             // Determine whether item is loose or not.
18685             // Use: /(^|\n)(?! )[^\n]+\n\n(?!\s*$)/
18686             // for discount behavior.
18687             loose = next || /\n\n(?!\s*$)/.test(item);
18688             if (i !== l - 1) {
18689               next = item.charAt(item.length - 1) === '\n';
18690               if (!loose) { loose = next; }
18691             }
18692     
18693             this.tokens.push({
18694               type: loose
18695                 ? 'loose_item_start'
18696                 : 'list_item_start'
18697             });
18698     
18699             // Recurse.
18700             this.token(item, false, bq);
18701     
18702             this.tokens.push({
18703               type: 'list_item_end'
18704             });
18705           }
18706     
18707           this.tokens.push({
18708             type: 'list_end'
18709           });
18710     
18711           continue;
18712         }
18713     
18714         // html
18715         if (cap = this.rules.html.exec(src)) {
18716           src = src.substring(cap[0].length);
18717           this.tokens.push({
18718             type: this.options.sanitize
18719               ? 'paragraph'
18720               : 'html',
18721             pre: !this.options.sanitizer
18722               && (cap[1] === 'pre' || cap[1] === 'script' || cap[1] === 'style'),
18723             text: cap[0]
18724           });
18725           continue;
18726         }
18727     
18728         // def
18729         if ((!bq && top) && (cap = this.rules.def.exec(src))) {
18730           src = src.substring(cap[0].length);
18731           this.tokens.links[cap[1].toLowerCase()] = {
18732             href: cap[2],
18733             title: cap[3]
18734           };
18735           continue;
18736         }
18737     
18738         // table (gfm)
18739         if (top && (cap = this.rules.table.exec(src))) {
18740           src = src.substring(cap[0].length);
18741     
18742           item = {
18743             type: 'table',
18744             header: cap[1].replace(/^ *| *\| *$/g, '').split(/ *\| */),
18745             align: cap[2].replace(/^ *|\| *$/g, '').split(/ *\| */),
18746             cells: cap[3].replace(/(?: *\| *)?\n$/, '').split('\n')
18747           };
18748     
18749           for (i = 0; i < item.align.length; i++) {
18750             if (/^ *-+: *$/.test(item.align[i])) {
18751               item.align[i] = 'right';
18752             } else if (/^ *:-+: *$/.test(item.align[i])) {
18753               item.align[i] = 'center';
18754             } else if (/^ *:-+ *$/.test(item.align[i])) {
18755               item.align[i] = 'left';
18756             } else {
18757               item.align[i] = null;
18758             }
18759           }
18760     
18761           for (i = 0; i < item.cells.length; i++) {
18762             item.cells[i] = item.cells[i]
18763               .replace(/^ *\| *| *\| *$/g, '')
18764               .split(/ *\| */);
18765           }
18766     
18767           this.tokens.push(item);
18768     
18769           continue;
18770         }
18771     
18772         // top-level paragraph
18773         if (top && (cap = this.rules.paragraph.exec(src))) {
18774           src = src.substring(cap[0].length);
18775           this.tokens.push({
18776             type: 'paragraph',
18777             text: cap[1].charAt(cap[1].length - 1) === '\n'
18778               ? cap[1].slice(0, -1)
18779               : cap[1]
18780           });
18781           continue;
18782         }
18783     
18784         // text
18785         if (cap = this.rules.text.exec(src)) {
18786           // Top-level should never reach here.
18787           src = src.substring(cap[0].length);
18788           this.tokens.push({
18789             type: 'text',
18790             text: cap[0]
18791           });
18792           continue;
18793         }
18794     
18795         if (src) {
18796           throw new
18797             Error('Infinite loop on byte: ' + src.charCodeAt(0));
18798         }
18799       }
18800     
18801       return this.tokens;
18802     };
18803     
18804     /**
18805      * Inline-Level Grammar
18806      */
18807     
18808     var inline = {
18809       escape: /^\\([\\`*{}\[\]()#+\-.!_>])/,
18810       autolink: /^<([^ >]+(@|:\/)[^ >]+)>/,
18811       url: noop,
18812       tag: /^<!--[\s\S]*?-->|^<\/?\w+(?:"[^"]*"|'[^']*'|[^'">])*?>/,
18813       link: /^!?\[(inside)\]\(href\)/,
18814       reflink: /^!?\[(inside)\]\s*\[([^\]]*)\]/,
18815       nolink: /^!?\[((?:\[[^\]]*\]|[^\[\]])*)\]/,
18816       strong: /^__([\s\S]+?)__(?!_)|^\*\*([\s\S]+?)\*\*(?!\*)/,
18817       em: /^\b_((?:[^_]|__)+?)_\b|^\*((?:\*\*|[\s\S])+?)\*(?!\*)/,
18818       code: /^(`+)\s*([\s\S]*?[^`])\s*\1(?!`)/,
18819       br: /^ {2,}\n(?!\s*$)/,
18820       del: noop,
18821       text: /^[\s\S]+?(?=[\\<!\[_*`]| {2,}\n|$)/
18822     };
18823     
18824     inline._inside = /(?:\[[^\]]*\]|[^\[\]]|\](?=[^\[]*\]))*/;
18825     inline._href = /\s*<?([\s\S]*?)>?(?:\s+['"]([\s\S]*?)['"])?\s*/;
18826     
18827     inline.link = replace(inline.link)
18828       ('inside', inline._inside)
18829       ('href', inline._href)
18830       ();
18831     
18832     inline.reflink = replace(inline.reflink)
18833       ('inside', inline._inside)
18834       ();
18835     
18836     /**
18837      * Normal Inline Grammar
18838      */
18839     
18840     inline.normal = merge({}, inline);
18841     
18842     /**
18843      * Pedantic Inline Grammar
18844      */
18845     
18846     inline.pedantic = merge({}, inline.normal, {
18847       strong: /^__(?=\S)([\s\S]*?\S)__(?!_)|^\*\*(?=\S)([\s\S]*?\S)\*\*(?!\*)/,
18848       em: /^_(?=\S)([\s\S]*?\S)_(?!_)|^\*(?=\S)([\s\S]*?\S)\*(?!\*)/
18849     });
18850     
18851     /**
18852      * GFM Inline Grammar
18853      */
18854     
18855     inline.gfm = merge({}, inline.normal, {
18856       escape: replace(inline.escape)('])', '~|])')(),
18857       url: /^(https?:\/\/[^\s<]+[^<.,:;"')\]\s])/,
18858       del: /^~~(?=\S)([\s\S]*?\S)~~/,
18859       text: replace(inline.text)
18860         (']|', '~]|')
18861         ('|', '|https?://|')
18862         ()
18863     });
18864     
18865     /**
18866      * GFM + Line Breaks Inline Grammar
18867      */
18868     
18869     inline.breaks = merge({}, inline.gfm, {
18870       br: replace(inline.br)('{2,}', '*')(),
18871       text: replace(inline.gfm.text)('{2,}', '*')()
18872     });
18873     
18874     /**
18875      * Inline Lexer & Compiler
18876      */
18877     
18878     var InlineLexer  = function (links, options) {
18879       this.options = options || marked.defaults;
18880       this.links = links;
18881       this.rules = inline.normal;
18882       this.renderer = this.options.renderer || new Renderer;
18883       this.renderer.options = this.options;
18884     
18885       if (!this.links) {
18886         throw new
18887           Error('Tokens array requires a `links` property.');
18888       }
18889     
18890       if (this.options.gfm) {
18891         if (this.options.breaks) {
18892           this.rules = inline.breaks;
18893         } else {
18894           this.rules = inline.gfm;
18895         }
18896       } else if (this.options.pedantic) {
18897         this.rules = inline.pedantic;
18898       }
18899     }
18900     
18901     /**
18902      * Expose Inline Rules
18903      */
18904     
18905     InlineLexer.rules = inline;
18906     
18907     /**
18908      * Static Lexing/Compiling Method
18909      */
18910     
18911     InlineLexer.output = function(src, links, options) {
18912       var inline = new InlineLexer(links, options);
18913       return inline.output(src);
18914     };
18915     
18916     /**
18917      * Lexing/Compiling
18918      */
18919     
18920     InlineLexer.prototype.output = function(src) {
18921       var out = ''
18922         , link
18923         , text
18924         , href
18925         , cap;
18926     
18927       while (src) {
18928         // escape
18929         if (cap = this.rules.escape.exec(src)) {
18930           src = src.substring(cap[0].length);
18931           out += cap[1];
18932           continue;
18933         }
18934     
18935         // autolink
18936         if (cap = this.rules.autolink.exec(src)) {
18937           src = src.substring(cap[0].length);
18938           if (cap[2] === '@') {
18939             text = cap[1].charAt(6) === ':'
18940               ? this.mangle(cap[1].substring(7))
18941               : this.mangle(cap[1]);
18942             href = this.mangle('mailto:') + text;
18943           } else {
18944             text = escape(cap[1]);
18945             href = text;
18946           }
18947           out += this.renderer.link(href, null, text);
18948           continue;
18949         }
18950     
18951         // url (gfm)
18952         if (!this.inLink && (cap = this.rules.url.exec(src))) {
18953           src = src.substring(cap[0].length);
18954           text = escape(cap[1]);
18955           href = text;
18956           out += this.renderer.link(href, null, text);
18957           continue;
18958         }
18959     
18960         // tag
18961         if (cap = this.rules.tag.exec(src)) {
18962           if (!this.inLink && /^<a /i.test(cap[0])) {
18963             this.inLink = true;
18964           } else if (this.inLink && /^<\/a>/i.test(cap[0])) {
18965             this.inLink = false;
18966           }
18967           src = src.substring(cap[0].length);
18968           out += this.options.sanitize
18969             ? this.options.sanitizer
18970               ? this.options.sanitizer(cap[0])
18971               : escape(cap[0])
18972             : cap[0];
18973           continue;
18974         }
18975     
18976         // link
18977         if (cap = this.rules.link.exec(src)) {
18978           src = src.substring(cap[0].length);
18979           this.inLink = true;
18980           out += this.outputLink(cap, {
18981             href: cap[2],
18982             title: cap[3]
18983           });
18984           this.inLink = false;
18985           continue;
18986         }
18987     
18988         // reflink, nolink
18989         if ((cap = this.rules.reflink.exec(src))
18990             || (cap = this.rules.nolink.exec(src))) {
18991           src = src.substring(cap[0].length);
18992           link = (cap[2] || cap[1]).replace(/\s+/g, ' ');
18993           link = this.links[link.toLowerCase()];
18994           if (!link || !link.href) {
18995             out += cap[0].charAt(0);
18996             src = cap[0].substring(1) + src;
18997             continue;
18998           }
18999           this.inLink = true;
19000           out += this.outputLink(cap, link);
19001           this.inLink = false;
19002           continue;
19003         }
19004     
19005         // strong
19006         if (cap = this.rules.strong.exec(src)) {
19007           src = src.substring(cap[0].length);
19008           out += this.renderer.strong(this.output(cap[2] || cap[1]));
19009           continue;
19010         }
19011     
19012         // em
19013         if (cap = this.rules.em.exec(src)) {
19014           src = src.substring(cap[0].length);
19015           out += this.renderer.em(this.output(cap[2] || cap[1]));
19016           continue;
19017         }
19018     
19019         // code
19020         if (cap = this.rules.code.exec(src)) {
19021           src = src.substring(cap[0].length);
19022           out += this.renderer.codespan(escape(cap[2], true));
19023           continue;
19024         }
19025     
19026         // br
19027         if (cap = this.rules.br.exec(src)) {
19028           src = src.substring(cap[0].length);
19029           out += this.renderer.br();
19030           continue;
19031         }
19032     
19033         // del (gfm)
19034         if (cap = this.rules.del.exec(src)) {
19035           src = src.substring(cap[0].length);
19036           out += this.renderer.del(this.output(cap[1]));
19037           continue;
19038         }
19039     
19040         // text
19041         if (cap = this.rules.text.exec(src)) {
19042           src = src.substring(cap[0].length);
19043           out += this.renderer.text(escape(this.smartypants(cap[0])));
19044           continue;
19045         }
19046     
19047         if (src) {
19048           throw new
19049             Error('Infinite loop on byte: ' + src.charCodeAt(0));
19050         }
19051       }
19052     
19053       return out;
19054     };
19055     
19056     /**
19057      * Compile Link
19058      */
19059     
19060     InlineLexer.prototype.outputLink = function(cap, link) {
19061       var href = escape(link.href)
19062         , title = link.title ? escape(link.title) : null;
19063     
19064       return cap[0].charAt(0) !== '!'
19065         ? this.renderer.link(href, title, this.output(cap[1]))
19066         : this.renderer.image(href, title, escape(cap[1]));
19067     };
19068     
19069     /**
19070      * Smartypants Transformations
19071      */
19072     
19073     InlineLexer.prototype.smartypants = function(text) {
19074       if (!this.options.smartypants)  { return text; }
19075       return text
19076         // em-dashes
19077         .replace(/---/g, '\u2014')
19078         // en-dashes
19079         .replace(/--/g, '\u2013')
19080         // opening singles
19081         .replace(/(^|[-\u2014/(\[{"\s])'/g, '$1\u2018')
19082         // closing singles & apostrophes
19083         .replace(/'/g, '\u2019')
19084         // opening doubles
19085         .replace(/(^|[-\u2014/(\[{\u2018\s])"/g, '$1\u201c')
19086         // closing doubles
19087         .replace(/"/g, '\u201d')
19088         // ellipses
19089         .replace(/\.{3}/g, '\u2026');
19090     };
19091     
19092     /**
19093      * Mangle Links
19094      */
19095     
19096     InlineLexer.prototype.mangle = function(text) {
19097       if (!this.options.mangle) { return text; }
19098       var out = ''
19099         , l = text.length
19100         , i = 0
19101         , ch;
19102     
19103       for (; i < l; i++) {
19104         ch = text.charCodeAt(i);
19105         if (Math.random() > 0.5) {
19106           ch = 'x' + ch.toString(16);
19107         }
19108         out += '&#' + ch + ';';
19109       }
19110     
19111       return out;
19112     };
19113     
19114     /**
19115      * Renderer
19116      */
19117     
19118      /**
19119          * eval:var:Renderer
19120     */
19121     
19122     var Renderer   = function (options) {
19123       this.options = options || {};
19124     }
19125     
19126     Renderer.prototype.code = function(code, lang, escaped) {
19127       if (this.options.highlight) {
19128         var out = this.options.highlight(code, lang);
19129         if (out != null && out !== code) {
19130           escaped = true;
19131           code = out;
19132         }
19133       } else {
19134             // hack!!! - it's already escapeD?
19135             escaped = true;
19136       }
19137     
19138       if (!lang) {
19139         return '<pre><code>'
19140           + (escaped ? code : escape(code, true))
19141           + '\n</code></pre>';
19142       }
19143     
19144       return '<pre><code class="'
19145         + this.options.langPrefix
19146         + escape(lang, true)
19147         + '">'
19148         + (escaped ? code : escape(code, true))
19149         + '\n</code></pre>\n';
19150     };
19151     
19152     Renderer.prototype.blockquote = function(quote) {
19153       return '<blockquote>\n' + quote + '</blockquote>\n';
19154     };
19155     
19156     Renderer.prototype.html = function(html) {
19157       return html;
19158     };
19159     
19160     Renderer.prototype.heading = function(text, level, raw) {
19161       return '<h'
19162         + level
19163         + ' id="'
19164         + this.options.headerPrefix
19165         + raw.toLowerCase().replace(/[^\w]+/g, '-')
19166         + '">'
19167         + text
19168         + '</h'
19169         + level
19170         + '>\n';
19171     };
19172     
19173     Renderer.prototype.hr = function() {
19174       return this.options.xhtml ? '<hr/>\n' : '<hr>\n';
19175     };
19176     
19177     Renderer.prototype.list = function(body, ordered) {
19178       var type = ordered ? 'ol' : 'ul';
19179       return '<' + type + '>\n' + body + '</' + type + '>\n';
19180     };
19181     
19182     Renderer.prototype.listitem = function(text) {
19183       return '<li>' + text + '</li>\n';
19184     };
19185     
19186     Renderer.prototype.paragraph = function(text) {
19187       return '<p>' + text + '</p>\n';
19188     };
19189     
19190     Renderer.prototype.table = function(header, body) {
19191       return '<table class="table table-striped">\n'
19192         + '<thead>\n'
19193         + header
19194         + '</thead>\n'
19195         + '<tbody>\n'
19196         + body
19197         + '</tbody>\n'
19198         + '</table>\n';
19199     };
19200     
19201     Renderer.prototype.tablerow = function(content) {
19202       return '<tr>\n' + content + '</tr>\n';
19203     };
19204     
19205     Renderer.prototype.tablecell = function(content, flags) {
19206       var type = flags.header ? 'th' : 'td';
19207       var tag = flags.align
19208         ? '<' + type + ' style="text-align:' + flags.align + '">'
19209         : '<' + type + '>';
19210       return tag + content + '</' + type + '>\n';
19211     };
19212     
19213     // span level renderer
19214     Renderer.prototype.strong = function(text) {
19215       return '<strong>' + text + '</strong>';
19216     };
19217     
19218     Renderer.prototype.em = function(text) {
19219       return '<em>' + text + '</em>';
19220     };
19221     
19222     Renderer.prototype.codespan = function(text) {
19223       return '<code>' + text + '</code>';
19224     };
19225     
19226     Renderer.prototype.br = function() {
19227       return this.options.xhtml ? '<br/>' : '<br>';
19228     };
19229     
19230     Renderer.prototype.del = function(text) {
19231       return '<del>' + text + '</del>';
19232     };
19233     
19234     Renderer.prototype.link = function(href, title, text) {
19235       if (this.options.sanitize) {
19236         try {
19237           var prot = decodeURIComponent(unescape(href))
19238             .replace(/[^\w:]/g, '')
19239             .toLowerCase();
19240         } catch (e) {
19241           return '';
19242         }
19243         if (prot.indexOf('javascript:') === 0 || prot.indexOf('vbscript:') === 0) {
19244           return '';
19245         }
19246       }
19247       var out = '<a href="' + href + '"';
19248       if (title) {
19249         out += ' title="' + title + '"';
19250       }
19251       out += '>' + text + '</a>';
19252       return out;
19253     };
19254     
19255     Renderer.prototype.image = function(href, title, text) {
19256       var out = '<img src="' + href + '" alt="' + text + '"';
19257       if (title) {
19258         out += ' title="' + title + '"';
19259       }
19260       out += this.options.xhtml ? '/>' : '>';
19261       return out;
19262     };
19263     
19264     Renderer.prototype.text = function(text) {
19265       return text;
19266     };
19267     
19268     /**
19269      * Parsing & Compiling
19270      */
19271          /**
19272          * eval:var:Parser
19273     */
19274     
19275     var Parser= function (options) {
19276       this.tokens = [];
19277       this.token = null;
19278       this.options = options || marked.defaults;
19279       this.options.renderer = this.options.renderer || new Renderer;
19280       this.renderer = this.options.renderer;
19281       this.renderer.options = this.options;
19282     }
19283     
19284     /**
19285      * Static Parse Method
19286      */
19287     
19288     Parser.parse = function(src, options, renderer) {
19289       var parser = new Parser(options, renderer);
19290       return parser.parse(src);
19291     };
19292     
19293     /**
19294      * Parse Loop
19295      */
19296     
19297     Parser.prototype.parse = function(src) {
19298       this.inline = new InlineLexer(src.links, this.options, this.renderer);
19299       this.tokens = src.reverse();
19300     
19301       var out = '';
19302       while (this.next()) {
19303         out += this.tok();
19304       }
19305     
19306       return out;
19307     };
19308     
19309     /**
19310      * Next Token
19311      */
19312     
19313     Parser.prototype.next = function() {
19314       return this.token = this.tokens.pop();
19315     };
19316     
19317     /**
19318      * Preview Next Token
19319      */
19320     
19321     Parser.prototype.peek = function() {
19322       return this.tokens[this.tokens.length - 1] || 0;
19323     };
19324     
19325     /**
19326      * Parse Text Tokens
19327      */
19328     
19329     Parser.prototype.parseText = function() {
19330       var body = this.token.text;
19331     
19332       while (this.peek().type === 'text') {
19333         body += '\n' + this.next().text;
19334       }
19335     
19336       return this.inline.output(body);
19337     };
19338     
19339     /**
19340      * Parse Current Token
19341      */
19342     
19343     Parser.prototype.tok = function() {
19344       switch (this.token.type) {
19345         case 'space': {
19346           return '';
19347         }
19348         case 'hr': {
19349           return this.renderer.hr();
19350         }
19351         case 'heading': {
19352           return this.renderer.heading(
19353             this.inline.output(this.token.text),
19354             this.token.depth,
19355             this.token.text);
19356         }
19357         case 'code': {
19358           return this.renderer.code(this.token.text,
19359             this.token.lang,
19360             this.token.escaped);
19361         }
19362         case 'table': {
19363           var header = ''
19364             , body = ''
19365             , i
19366             , row
19367             , cell
19368             , flags
19369             , j;
19370     
19371           // header
19372           cell = '';
19373           for (i = 0; i < this.token.header.length; i++) {
19374             flags = { header: true, align: this.token.align[i] };
19375             cell += this.renderer.tablecell(
19376               this.inline.output(this.token.header[i]),
19377               { header: true, align: this.token.align[i] }
19378             );
19379           }
19380           header += this.renderer.tablerow(cell);
19381     
19382           for (i = 0; i < this.token.cells.length; i++) {
19383             row = this.token.cells[i];
19384     
19385             cell = '';
19386             for (j = 0; j < row.length; j++) {
19387               cell += this.renderer.tablecell(
19388                 this.inline.output(row[j]),
19389                 { header: false, align: this.token.align[j] }
19390               );
19391             }
19392     
19393             body += this.renderer.tablerow(cell);
19394           }
19395           return this.renderer.table(header, body);
19396         }
19397         case 'blockquote_start': {
19398           var body = '';
19399     
19400           while (this.next().type !== 'blockquote_end') {
19401             body += this.tok();
19402           }
19403     
19404           return this.renderer.blockquote(body);
19405         }
19406         case 'list_start': {
19407           var body = ''
19408             , ordered = this.token.ordered;
19409     
19410           while (this.next().type !== 'list_end') {
19411             body += this.tok();
19412           }
19413     
19414           return this.renderer.list(body, ordered);
19415         }
19416         case 'list_item_start': {
19417           var body = '';
19418     
19419           while (this.next().type !== 'list_item_end') {
19420             body += this.token.type === 'text'
19421               ? this.parseText()
19422               : this.tok();
19423           }
19424     
19425           return this.renderer.listitem(body);
19426         }
19427         case 'loose_item_start': {
19428           var body = '';
19429     
19430           while (this.next().type !== 'list_item_end') {
19431             body += this.tok();
19432           }
19433     
19434           return this.renderer.listitem(body);
19435         }
19436         case 'html': {
19437           var html = !this.token.pre && !this.options.pedantic
19438             ? this.inline.output(this.token.text)
19439             : this.token.text;
19440           return this.renderer.html(html);
19441         }
19442         case 'paragraph': {
19443           return this.renderer.paragraph(this.inline.output(this.token.text));
19444         }
19445         case 'text': {
19446           return this.renderer.paragraph(this.parseText());
19447         }
19448       }
19449     };
19450   
19451     
19452     /**
19453      * Marked
19454      */
19455          /**
19456          * eval:var:marked
19457     */
19458     var marked = function (src, opt, callback) {
19459       if (callback || typeof opt === 'function') {
19460         if (!callback) {
19461           callback = opt;
19462           opt = null;
19463         }
19464     
19465         opt = merge({}, marked.defaults, opt || {});
19466     
19467         var highlight = opt.highlight
19468           , tokens
19469           , pending
19470           , i = 0;
19471     
19472         try {
19473           tokens = Lexer.lex(src, opt)
19474         } catch (e) {
19475           return callback(e);
19476         }
19477     
19478         pending = tokens.length;
19479          /**
19480          * eval:var:done
19481     */
19482         var done = function(err) {
19483           if (err) {
19484             opt.highlight = highlight;
19485             return callback(err);
19486           }
19487     
19488           var out;
19489     
19490           try {
19491             out = Parser.parse(tokens, opt);
19492           } catch (e) {
19493             err = e;
19494           }
19495     
19496           opt.highlight = highlight;
19497     
19498           return err
19499             ? callback(err)
19500             : callback(null, out);
19501         };
19502     
19503         if (!highlight || highlight.length < 3) {
19504           return done();
19505         }
19506     
19507         delete opt.highlight;
19508     
19509         if (!pending) { return done(); }
19510     
19511         for (; i < tokens.length; i++) {
19512           (function(token) {
19513             if (token.type !== 'code') {
19514               return --pending || done();
19515             }
19516             return highlight(token.text, token.lang, function(err, code) {
19517               if (err) { return done(err); }
19518               if (code == null || code === token.text) {
19519                 return --pending || done();
19520               }
19521               token.text = code;
19522               token.escaped = true;
19523               --pending || done();
19524             });
19525           })(tokens[i]);
19526         }
19527     
19528         return;
19529       }
19530       try {
19531         if (opt) { opt = merge({}, marked.defaults, opt); }
19532         return Parser.parse(Lexer.lex(src, opt), opt);
19533       } catch (e) {
19534         e.message += '\nPlease report this to https://github.com/chjj/marked.';
19535         if ((opt || marked.defaults).silent) {
19536           return '<p>An error occured:</p><pre>'
19537             + escape(e.message + '', true)
19538             + '</pre>';
19539         }
19540         throw e;
19541       }
19542     }
19543     
19544     /**
19545      * Options
19546      */
19547     
19548     marked.options =
19549     marked.setOptions = function(opt) {
19550       merge(marked.defaults, opt);
19551       return marked;
19552     };
19553     
19554     marked.defaults = {
19555       gfm: true,
19556       tables: true,
19557       breaks: false,
19558       pedantic: false,
19559       sanitize: false,
19560       sanitizer: null,
19561       mangle: true,
19562       smartLists: false,
19563       silent: false,
19564       highlight: null,
19565       langPrefix: 'lang-',
19566       smartypants: false,
19567       headerPrefix: '',
19568       renderer: new Renderer,
19569       xhtml: false
19570     };
19571     
19572     /**
19573      * Expose
19574      */
19575     
19576     marked.Parser = Parser;
19577     marked.parser = Parser.parse;
19578     
19579     marked.Renderer = Renderer;
19580     
19581     marked.Lexer = Lexer;
19582     marked.lexer = Lexer.lex;
19583     
19584     marked.InlineLexer = InlineLexer;
19585     marked.inlineLexer = InlineLexer.output;
19586     
19587     marked.parse = marked;
19588     
19589     Roo.Markdown.marked = marked;
19590
19591 })();/*
19592  * Based on:
19593  * Ext JS Library 1.1.1
19594  * Copyright(c) 2006-2007, Ext JS, LLC.
19595  *
19596  * Originally Released Under LGPL - original licence link has changed is not relivant.
19597  *
19598  * Fork - LGPL
19599  * <script type="text/javascript">
19600  */
19601
19602
19603
19604 /*
19605  * These classes are derivatives of the similarly named classes in the YUI Library.
19606  * The original license:
19607  * Copyright (c) 2006, Yahoo! Inc. All rights reserved.
19608  * Code licensed under the BSD License:
19609  * http://developer.yahoo.net/yui/license.txt
19610  */
19611
19612 (function() {
19613
19614 var Event=Roo.EventManager;
19615 var Dom=Roo.lib.Dom;
19616
19617 /**
19618  * @class Roo.dd.DragDrop
19619  * @extends Roo.util.Observable
19620  * Defines the interface and base operation of items that that can be
19621  * dragged or can be drop targets.  It was designed to be extended, overriding
19622  * the event handlers for startDrag, onDrag, onDragOver and onDragOut.
19623  * Up to three html elements can be associated with a DragDrop instance:
19624  * <ul>
19625  * <li>linked element: the element that is passed into the constructor.
19626  * This is the element which defines the boundaries for interaction with
19627  * other DragDrop objects.</li>
19628  * <li>handle element(s): The drag operation only occurs if the element that
19629  * was clicked matches a handle element.  By default this is the linked
19630  * element, but there are times that you will want only a portion of the
19631  * linked element to initiate the drag operation, and the setHandleElId()
19632  * method provides a way to define this.</li>
19633  * <li>drag element: this represents the element that would be moved along
19634  * with the cursor during a drag operation.  By default, this is the linked
19635  * element itself as in {@link Roo.dd.DD}.  setDragElId() lets you define
19636  * a separate element that would be moved, as in {@link Roo.dd.DDProxy}.
19637  * </li>
19638  * </ul>
19639  * This class should not be instantiated until the onload event to ensure that
19640  * the associated elements are available.
19641  * The following would define a DragDrop obj that would interact with any
19642  * other DragDrop obj in the "group1" group:
19643  * <pre>
19644  *  dd = new Roo.dd.DragDrop("div1", "group1");
19645  * </pre>
19646  * Since none of the event handlers have been implemented, nothing would
19647  * actually happen if you were to run the code above.  Normally you would
19648  * override this class or one of the default implementations, but you can
19649  * also override the methods you want on an instance of the class...
19650  * <pre>
19651  *  dd.onDragDrop = function(e, id) {
19652  *  &nbsp;&nbsp;alert("dd was dropped on " + id);
19653  *  }
19654  * </pre>
19655  * @constructor
19656  * @param {String} id of the element that is linked to this instance
19657  * @param {String} sGroup the group of related DragDrop objects
19658  * @param {object} config an object containing configurable attributes
19659  *                Valid properties for DragDrop:
19660  *                    padding, isTarget, maintainOffset, primaryButtonOnly
19661  */
19662 Roo.dd.DragDrop = function(id, sGroup, config) {
19663     if (id) {
19664         this.init(id, sGroup, config);
19665     }
19666     
19667 };
19668
19669 Roo.extend(Roo.dd.DragDrop, Roo.util.Observable , {
19670
19671     /**
19672      * The id of the element associated with this object.  This is what we
19673      * refer to as the "linked element" because the size and position of
19674      * this element is used to determine when the drag and drop objects have
19675      * interacted.
19676      * @property id
19677      * @type String
19678      */
19679     id: null,
19680
19681     /**
19682      * Configuration attributes passed into the constructor
19683      * @property config
19684      * @type object
19685      */
19686     config: null,
19687
19688     /**
19689      * The id of the element that will be dragged.  By default this is same
19690      * as the linked element , but could be changed to another element. Ex:
19691      * Roo.dd.DDProxy
19692      * @property dragElId
19693      * @type String
19694      * @private
19695      */
19696     dragElId: null,
19697
19698     /**
19699      * the id of the element that initiates the drag operation.  By default
19700      * this is the linked element, but could be changed to be a child of this
19701      * element.  This lets us do things like only starting the drag when the
19702      * header element within the linked html element is clicked.
19703      * @property handleElId
19704      * @type String
19705      * @private
19706      */
19707     handleElId: null,
19708
19709     /**
19710      * An associative array of HTML tags that will be ignored if clicked.
19711      * @property invalidHandleTypes
19712      * @type {string: string}
19713      */
19714     invalidHandleTypes: null,
19715
19716     /**
19717      * An associative array of ids for elements that will be ignored if clicked
19718      * @property invalidHandleIds
19719      * @type {string: string}
19720      */
19721     invalidHandleIds: null,
19722
19723     /**
19724      * An indexted array of css class names for elements that will be ignored
19725      * if clicked.
19726      * @property invalidHandleClasses
19727      * @type string[]
19728      */
19729     invalidHandleClasses: null,
19730
19731     /**
19732      * The linked element's absolute X position at the time the drag was
19733      * started
19734      * @property startPageX
19735      * @type int
19736      * @private
19737      */
19738     startPageX: 0,
19739
19740     /**
19741      * The linked element's absolute X position at the time the drag was
19742      * started
19743      * @property startPageY
19744      * @type int
19745      * @private
19746      */
19747     startPageY: 0,
19748
19749     /**
19750      * The group defines a logical collection of DragDrop objects that are
19751      * related.  Instances only get events when interacting with other
19752      * DragDrop object in the same group.  This lets us define multiple
19753      * groups using a single DragDrop subclass if we want.
19754      * @property groups
19755      * @type {string: string}
19756      */
19757     groups: null,
19758
19759     /**
19760      * Individual drag/drop instances can be locked.  This will prevent
19761      * onmousedown start drag.
19762      * @property locked
19763      * @type boolean
19764      * @private
19765      */
19766     locked: false,
19767
19768     /**
19769      * Lock this instance
19770      * @method lock
19771      */
19772     lock: function() { this.locked = true; },
19773
19774     /**
19775      * Unlock this instace
19776      * @method unlock
19777      */
19778     unlock: function() { this.locked = false; },
19779
19780     /**
19781      * By default, all insances can be a drop target.  This can be disabled by
19782      * setting isTarget to false.
19783      * @method isTarget
19784      * @type boolean
19785      */
19786     isTarget: true,
19787
19788     /**
19789      * The padding configured for this drag and drop object for calculating
19790      * the drop zone intersection with this object.
19791      * @method padding
19792      * @type int[]
19793      */
19794     padding: null,
19795
19796     /**
19797      * Cached reference to the linked element
19798      * @property _domRef
19799      * @private
19800      */
19801     _domRef: null,
19802
19803     /**
19804      * Internal typeof flag
19805      * @property __ygDragDrop
19806      * @private
19807      */
19808     __ygDragDrop: true,
19809
19810     /**
19811      * Set to true when horizontal contraints are applied
19812      * @property constrainX
19813      * @type boolean
19814      * @private
19815      */
19816     constrainX: false,
19817
19818     /**
19819      * Set to true when vertical contraints are applied
19820      * @property constrainY
19821      * @type boolean
19822      * @private
19823      */
19824     constrainY: false,
19825
19826     /**
19827      * The left constraint
19828      * @property minX
19829      * @type int
19830      * @private
19831      */
19832     minX: 0,
19833
19834     /**
19835      * The right constraint
19836      * @property maxX
19837      * @type int
19838      * @private
19839      */
19840     maxX: 0,
19841
19842     /**
19843      * The up constraint
19844      * @property minY
19845      * @type int
19846      * @type int
19847      * @private
19848      */
19849     minY: 0,
19850
19851     /**
19852      * The down constraint
19853      * @property maxY
19854      * @type int
19855      * @private
19856      */
19857     maxY: 0,
19858
19859     /**
19860      * Maintain offsets when we resetconstraints.  Set to true when you want
19861      * the position of the element relative to its parent to stay the same
19862      * when the page changes
19863      *
19864      * @property maintainOffset
19865      * @type boolean
19866      */
19867     maintainOffset: false,
19868
19869     /**
19870      * Array of pixel locations the element will snap to if we specified a
19871      * horizontal graduation/interval.  This array is generated automatically
19872      * when you define a tick interval.
19873      * @property xTicks
19874      * @type int[]
19875      */
19876     xTicks: null,
19877
19878     /**
19879      * Array of pixel locations the element will snap to if we specified a
19880      * vertical graduation/interval.  This array is generated automatically
19881      * when you define a tick interval.
19882      * @property yTicks
19883      * @type int[]
19884      */
19885     yTicks: null,
19886
19887     /**
19888      * By default the drag and drop instance will only respond to the primary
19889      * button click (left button for a right-handed mouse).  Set to true to
19890      * allow drag and drop to start with any mouse click that is propogated
19891      * by the browser
19892      * @property primaryButtonOnly
19893      * @type boolean
19894      */
19895     primaryButtonOnly: true,
19896
19897     /**
19898      * The availabe property is false until the linked dom element is accessible.
19899      * @property available
19900      * @type boolean
19901      */
19902     available: false,
19903
19904     /**
19905      * By default, drags can only be initiated if the mousedown occurs in the
19906      * region the linked element is.  This is done in part to work around a
19907      * bug in some browsers that mis-report the mousedown if the previous
19908      * mouseup happened outside of the window.  This property is set to true
19909      * if outer handles are defined.
19910      *
19911      * @property hasOuterHandles
19912      * @type boolean
19913      * @default false
19914      */
19915     hasOuterHandles: false,
19916
19917     /**
19918      * Code that executes immediately before the startDrag event
19919      * @method b4StartDrag
19920      * @private
19921      */
19922     b4StartDrag: function(x, y) { },
19923
19924     /**
19925      * Abstract method called after a drag/drop object is clicked
19926      * and the drag or mousedown time thresholds have beeen met.
19927      * @method startDrag
19928      * @param {int} X click location
19929      * @param {int} Y click location
19930      */
19931     startDrag: function(x, y) { /* override this */ },
19932
19933     /**
19934      * Code that executes immediately before the onDrag event
19935      * @method b4Drag
19936      * @private
19937      */
19938     b4Drag: function(e) { },
19939
19940     /**
19941      * Abstract method called during the onMouseMove event while dragging an
19942      * object.
19943      * @method onDrag
19944      * @param {Event} e the mousemove event
19945      */
19946     onDrag: function(e) { /* override this */ },
19947
19948     /**
19949      * Abstract method called when this element fist begins hovering over
19950      * another DragDrop obj
19951      * @method onDragEnter
19952      * @param {Event} e the mousemove event
19953      * @param {String|DragDrop[]} id In POINT mode, the element
19954      * id this is hovering over.  In INTERSECT mode, an array of one or more
19955      * dragdrop items being hovered over.
19956      */
19957     onDragEnter: function(e, id) { /* override this */ },
19958
19959     /**
19960      * Code that executes immediately before the onDragOver event
19961      * @method b4DragOver
19962      * @private
19963      */
19964     b4DragOver: function(e) { },
19965
19966     /**
19967      * Abstract method called when this element is hovering over another
19968      * DragDrop obj
19969      * @method onDragOver
19970      * @param {Event} e the mousemove event
19971      * @param {String|DragDrop[]} id In POINT mode, the element
19972      * id this is hovering over.  In INTERSECT mode, an array of dd items
19973      * being hovered over.
19974      */
19975     onDragOver: function(e, id) { /* override this */ },
19976
19977     /**
19978      * Code that executes immediately before the onDragOut event
19979      * @method b4DragOut
19980      * @private
19981      */
19982     b4DragOut: function(e) { },
19983
19984     /**
19985      * Abstract method called when we are no longer hovering over an element
19986      * @method onDragOut
19987      * @param {Event} e the mousemove event
19988      * @param {String|DragDrop[]} id In POINT mode, the element
19989      * id this was hovering over.  In INTERSECT mode, an array of dd items
19990      * that the mouse is no longer over.
19991      */
19992     onDragOut: function(e, id) { /* override this */ },
19993
19994     /**
19995      * Code that executes immediately before the onDragDrop event
19996      * @method b4DragDrop
19997      * @private
19998      */
19999     b4DragDrop: function(e) { },
20000
20001     /**
20002      * Abstract method called when this item is dropped on another DragDrop
20003      * obj
20004      * @method onDragDrop
20005      * @param {Event} e the mouseup event
20006      * @param {String|DragDrop[]} id In POINT mode, the element
20007      * id this was dropped on.  In INTERSECT mode, an array of dd items this
20008      * was dropped on.
20009      */
20010     onDragDrop: function(e, id) { /* override this */ },
20011
20012     /**
20013      * Abstract method called when this item is dropped on an area with no
20014      * drop target
20015      * @method onInvalidDrop
20016      * @param {Event} e the mouseup event
20017      */
20018     onInvalidDrop: function(e) { /* override this */ },
20019
20020     /**
20021      * Code that executes immediately before the endDrag event
20022      * @method b4EndDrag
20023      * @private
20024      */
20025     b4EndDrag: function(e) { },
20026
20027     /**
20028      * Fired when we are done dragging the object
20029      * @method endDrag
20030      * @param {Event} e the mouseup event
20031      */
20032     endDrag: function(e) { /* override this */ },
20033
20034     /**
20035      * Code executed immediately before the onMouseDown event
20036      * @method b4MouseDown
20037      * @param {Event} e the mousedown event
20038      * @private
20039      */
20040     b4MouseDown: function(e) {  },
20041
20042     /**
20043      * Event handler that fires when a drag/drop obj gets a mousedown
20044      * @method onMouseDown
20045      * @param {Event} e the mousedown event
20046      */
20047     onMouseDown: function(e) { /* override this */ },
20048
20049     /**
20050      * Event handler that fires when a drag/drop obj gets a mouseup
20051      * @method onMouseUp
20052      * @param {Event} e the mouseup event
20053      */
20054     onMouseUp: function(e) { /* override this */ },
20055
20056     /**
20057      * Override the onAvailable method to do what is needed after the initial
20058      * position was determined.
20059      * @method onAvailable
20060      */
20061     onAvailable: function () {
20062     },
20063
20064     /*
20065      * Provides default constraint padding to "constrainTo" elements (defaults to {left: 0, right:0, top:0, bottom:0}).
20066      * @type Object
20067      */
20068     defaultPadding : {left:0, right:0, top:0, bottom:0},
20069
20070     /*
20071      * Initializes the drag drop object's constraints to restrict movement to a certain element.
20072  *
20073  * Usage:
20074  <pre><code>
20075  var dd = new Roo.dd.DDProxy("dragDiv1", "proxytest",
20076                 { dragElId: "existingProxyDiv" });
20077  dd.startDrag = function(){
20078      this.constrainTo("parent-id");
20079  };
20080  </code></pre>
20081  * Or you can initalize it using the {@link Roo.Element} object:
20082  <pre><code>
20083  Roo.get("dragDiv1").initDDProxy("proxytest", {dragElId: "existingProxyDiv"}, {
20084      startDrag : function(){
20085          this.constrainTo("parent-id");
20086      }
20087  });
20088  </code></pre>
20089      * @param {String/HTMLElement/Element} constrainTo The element to constrain to.
20090      * @param {Object/Number} pad (optional) Pad provides a way to specify "padding" of the constraints,
20091      * and can be either a number for symmetrical padding (4 would be equal to {left:4, right:4, top:4, bottom:4}) or
20092      * an object containing the sides to pad. For example: {right:10, bottom:10}
20093      * @param {Boolean} inContent (optional) Constrain the draggable in the content box of the element (inside padding and borders)
20094      */
20095     constrainTo : function(constrainTo, pad, inContent){
20096         if(typeof pad == "number"){
20097             pad = {left: pad, right:pad, top:pad, bottom:pad};
20098         }
20099         pad = pad || this.defaultPadding;
20100         var b = Roo.get(this.getEl()).getBox();
20101         var ce = Roo.get(constrainTo);
20102         var s = ce.getScroll();
20103         var c, cd = ce.dom;
20104         if(cd == document.body){
20105             c = { x: s.left, y: s.top, width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
20106         }else{
20107             xy = ce.getXY();
20108             c = {x : xy[0]+s.left, y: xy[1]+s.top, width: cd.clientWidth, height: cd.clientHeight};
20109         }
20110
20111
20112         var topSpace = b.y - c.y;
20113         var leftSpace = b.x - c.x;
20114
20115         this.resetConstraints();
20116         this.setXConstraint(leftSpace - (pad.left||0), // left
20117                 c.width - leftSpace - b.width - (pad.right||0) //right
20118         );
20119         this.setYConstraint(topSpace - (pad.top||0), //top
20120                 c.height - topSpace - b.height - (pad.bottom||0) //bottom
20121         );
20122     },
20123
20124     /**
20125      * Returns a reference to the linked element
20126      * @method getEl
20127      * @return {HTMLElement} the html element
20128      */
20129     getEl: function() {
20130         if (!this._domRef) {
20131             this._domRef = Roo.getDom(this.id);
20132         }
20133
20134         return this._domRef;
20135     },
20136
20137     /**
20138      * Returns a reference to the actual element to drag.  By default this is
20139      * the same as the html element, but it can be assigned to another
20140      * element. An example of this can be found in Roo.dd.DDProxy
20141      * @method getDragEl
20142      * @return {HTMLElement} the html element
20143      */
20144     getDragEl: function() {
20145         return Roo.getDom(this.dragElId);
20146     },
20147
20148     /**
20149      * Sets up the DragDrop object.  Must be called in the constructor of any
20150      * Roo.dd.DragDrop subclass
20151      * @method init
20152      * @param id the id of the linked element
20153      * @param {String} sGroup the group of related items
20154      * @param {object} config configuration attributes
20155      */
20156     init: function(id, sGroup, config) {
20157         this.initTarget(id, sGroup, config);
20158         if (!Roo.isTouch) {
20159             Event.on(this.id, "mousedown", this.handleMouseDown, this);
20160         }
20161         Event.on(this.id, "touchstart", this.handleMouseDown, this);
20162         // Event.on(this.id, "selectstart", Event.preventDefault);
20163     },
20164
20165     /**
20166      * Initializes Targeting functionality only... the object does not
20167      * get a mousedown handler.
20168      * @method initTarget
20169      * @param id the id of the linked element
20170      * @param {String} sGroup the group of related items
20171      * @param {object} config configuration attributes
20172      */
20173     initTarget: function(id, sGroup, config) {
20174
20175         // configuration attributes
20176         this.config = config || {};
20177
20178         // create a local reference to the drag and drop manager
20179         this.DDM = Roo.dd.DDM;
20180         // initialize the groups array
20181         this.groups = {};
20182
20183         // assume that we have an element reference instead of an id if the
20184         // parameter is not a string
20185         if (typeof id !== "string") {
20186             id = Roo.id(id);
20187         }
20188
20189         // set the id
20190         this.id = id;
20191
20192         // add to an interaction group
20193         this.addToGroup((sGroup) ? sGroup : "default");
20194
20195         // We don't want to register this as the handle with the manager
20196         // so we just set the id rather than calling the setter.
20197         this.handleElId = id;
20198
20199         // the linked element is the element that gets dragged by default
20200         this.setDragElId(id);
20201
20202         // by default, clicked anchors will not start drag operations.
20203         this.invalidHandleTypes = { A: "A" };
20204         this.invalidHandleIds = {};
20205         this.invalidHandleClasses = [];
20206
20207         this.applyConfig();
20208
20209         this.handleOnAvailable();
20210     },
20211
20212     /**
20213      * Applies the configuration parameters that were passed into the constructor.
20214      * This is supposed to happen at each level through the inheritance chain.  So
20215      * a DDProxy implentation will execute apply config on DDProxy, DD, and
20216      * DragDrop in order to get all of the parameters that are available in
20217      * each object.
20218      * @method applyConfig
20219      */
20220     applyConfig: function() {
20221
20222         // configurable properties:
20223         //    padding, isTarget, maintainOffset, primaryButtonOnly
20224         this.padding           = this.config.padding || [0, 0, 0, 0];
20225         this.isTarget          = (this.config.isTarget !== false);
20226         this.maintainOffset    = (this.config.maintainOffset);
20227         this.primaryButtonOnly = (this.config.primaryButtonOnly !== false);
20228
20229     },
20230
20231     /**
20232      * Executed when the linked element is available
20233      * @method handleOnAvailable
20234      * @private
20235      */
20236     handleOnAvailable: function() {
20237         this.available = true;
20238         this.resetConstraints();
20239         this.onAvailable();
20240     },
20241
20242      /**
20243      * Configures the padding for the target zone in px.  Effectively expands
20244      * (or reduces) the virtual object size for targeting calculations.
20245      * Supports css-style shorthand; if only one parameter is passed, all sides
20246      * will have that padding, and if only two are passed, the top and bottom
20247      * will have the first param, the left and right the second.
20248      * @method setPadding
20249      * @param {int} iTop    Top pad
20250      * @param {int} iRight  Right pad
20251      * @param {int} iBot    Bot pad
20252      * @param {int} iLeft   Left pad
20253      */
20254     setPadding: function(iTop, iRight, iBot, iLeft) {
20255         // this.padding = [iLeft, iRight, iTop, iBot];
20256         if (!iRight && 0 !== iRight) {
20257             this.padding = [iTop, iTop, iTop, iTop];
20258         } else if (!iBot && 0 !== iBot) {
20259             this.padding = [iTop, iRight, iTop, iRight];
20260         } else {
20261             this.padding = [iTop, iRight, iBot, iLeft];
20262         }
20263     },
20264
20265     /**
20266      * Stores the initial placement of the linked element.
20267      * @method setInitialPosition
20268      * @param {int} diffX   the X offset, default 0
20269      * @param {int} diffY   the Y offset, default 0
20270      */
20271     setInitPosition: function(diffX, diffY) {
20272         var el = this.getEl();
20273
20274         if (!this.DDM.verifyEl(el)) {
20275             return;
20276         }
20277
20278         var dx = diffX || 0;
20279         var dy = diffY || 0;
20280
20281         var p = Dom.getXY( el );
20282
20283         this.initPageX = p[0] - dx;
20284         this.initPageY = p[1] - dy;
20285
20286         this.lastPageX = p[0];
20287         this.lastPageY = p[1];
20288
20289
20290         this.setStartPosition(p);
20291     },
20292
20293     /**
20294      * Sets the start position of the element.  This is set when the obj
20295      * is initialized, the reset when a drag is started.
20296      * @method setStartPosition
20297      * @param pos current position (from previous lookup)
20298      * @private
20299      */
20300     setStartPosition: function(pos) {
20301         var p = pos || Dom.getXY( this.getEl() );
20302         this.deltaSetXY = null;
20303
20304         this.startPageX = p[0];
20305         this.startPageY = p[1];
20306     },
20307
20308     /**
20309      * Add this instance to a group of related drag/drop objects.  All
20310      * instances belong to at least one group, and can belong to as many
20311      * groups as needed.
20312      * @method addToGroup
20313      * @param sGroup {string} the name of the group
20314      */
20315     addToGroup: function(sGroup) {
20316         this.groups[sGroup] = true;
20317         this.DDM.regDragDrop(this, sGroup);
20318     },
20319
20320     /**
20321      * Remove's this instance from the supplied interaction group
20322      * @method removeFromGroup
20323      * @param {string}  sGroup  The group to drop
20324      */
20325     removeFromGroup: function(sGroup) {
20326         if (this.groups[sGroup]) {
20327             delete this.groups[sGroup];
20328         }
20329
20330         this.DDM.removeDDFromGroup(this, sGroup);
20331     },
20332
20333     /**
20334      * Allows you to specify that an element other than the linked element
20335      * will be moved with the cursor during a drag
20336      * @method setDragElId
20337      * @param id {string} the id of the element that will be used to initiate the drag
20338      */
20339     setDragElId: function(id) {
20340         this.dragElId = id;
20341     },
20342
20343     /**
20344      * Allows you to specify a child of the linked element that should be
20345      * used to initiate the drag operation.  An example of this would be if
20346      * you have a content div with text and links.  Clicking anywhere in the
20347      * content area would normally start the drag operation.  Use this method
20348      * to specify that an element inside of the content div is the element
20349      * that starts the drag operation.
20350      * @method setHandleElId
20351      * @param id {string} the id of the element that will be used to
20352      * initiate the drag.
20353      */
20354     setHandleElId: function(id) {
20355         if (typeof id !== "string") {
20356             id = Roo.id(id);
20357         }
20358         this.handleElId = id;
20359         this.DDM.regHandle(this.id, id);
20360     },
20361
20362     /**
20363      * Allows you to set an element outside of the linked element as a drag
20364      * handle
20365      * @method setOuterHandleElId
20366      * @param id the id of the element that will be used to initiate the drag
20367      */
20368     setOuterHandleElId: function(id) {
20369         if (typeof id !== "string") {
20370             id = Roo.id(id);
20371         }
20372         Event.on(id, "mousedown",
20373                 this.handleMouseDown, this);
20374         this.setHandleElId(id);
20375
20376         this.hasOuterHandles = true;
20377     },
20378
20379     /**
20380      * Remove all drag and drop hooks for this element
20381      * @method unreg
20382      */
20383     unreg: function() {
20384         Event.un(this.id, "mousedown",
20385                 this.handleMouseDown);
20386         Event.un(this.id, "touchstart",
20387                 this.handleMouseDown);
20388         this._domRef = null;
20389         this.DDM._remove(this);
20390     },
20391
20392     destroy : function(){
20393         this.unreg();
20394     },
20395
20396     /**
20397      * Returns true if this instance is locked, or the drag drop mgr is locked
20398      * (meaning that all drag/drop is disabled on the page.)
20399      * @method isLocked
20400      * @return {boolean} true if this obj or all drag/drop is locked, else
20401      * false
20402      */
20403     isLocked: function() {
20404         return (this.DDM.isLocked() || this.locked);
20405     },
20406
20407     /**
20408      * Fired when this object is clicked
20409      * @method handleMouseDown
20410      * @param {Event} e
20411      * @param {Roo.dd.DragDrop} oDD the clicked dd object (this dd obj)
20412      * @private
20413      */
20414     handleMouseDown: function(e, oDD){
20415      
20416         if (!Roo.isTouch && this.primaryButtonOnly && e.button != 0) {
20417             //Roo.log('not touch/ button !=0');
20418             return;
20419         }
20420         if (e.browserEvent.touches && e.browserEvent.touches.length != 1) {
20421             return; // double touch..
20422         }
20423         
20424
20425         if (this.isLocked()) {
20426             //Roo.log('locked');
20427             return;
20428         }
20429
20430         this.DDM.refreshCache(this.groups);
20431 //        Roo.log([Roo.lib.Event.getPageX(e), Roo.lib.Event.getPageY(e)]);
20432         var pt = new Roo.lib.Point(Roo.lib.Event.getPageX(e), Roo.lib.Event.getPageY(e));
20433         if (!this.hasOuterHandles && !this.DDM.isOverTarget(pt, this) )  {
20434             //Roo.log('no outer handes or not over target');
20435                 // do nothing.
20436         } else {
20437 //            Roo.log('check validator');
20438             if (this.clickValidator(e)) {
20439 //                Roo.log('validate success');
20440                 // set the initial element position
20441                 this.setStartPosition();
20442
20443
20444                 this.b4MouseDown(e);
20445                 this.onMouseDown(e);
20446
20447                 this.DDM.handleMouseDown(e, this);
20448
20449                 this.DDM.stopEvent(e);
20450             } else {
20451
20452
20453             }
20454         }
20455     },
20456
20457     clickValidator: function(e) {
20458         var target = e.getTarget();
20459         return ( this.isValidHandleChild(target) &&
20460                     (this.id == this.handleElId ||
20461                         this.DDM.handleWasClicked(target, this.id)) );
20462     },
20463
20464     /**
20465      * Allows you to specify a tag name that should not start a drag operation
20466      * when clicked.  This is designed to facilitate embedding links within a
20467      * drag handle that do something other than start the drag.
20468      * @method addInvalidHandleType
20469      * @param {string} tagName the type of element to exclude
20470      */
20471     addInvalidHandleType: function(tagName) {
20472         var type = tagName.toUpperCase();
20473         this.invalidHandleTypes[type] = type;
20474     },
20475
20476     /**
20477      * Lets you to specify an element id for a child of a drag handle
20478      * that should not initiate a drag
20479      * @method addInvalidHandleId
20480      * @param {string} id the element id of the element you wish to ignore
20481      */
20482     addInvalidHandleId: function(id) {
20483         if (typeof id !== "string") {
20484             id = Roo.id(id);
20485         }
20486         this.invalidHandleIds[id] = id;
20487     },
20488
20489     /**
20490      * Lets you specify a css class of elements that will not initiate a drag
20491      * @method addInvalidHandleClass
20492      * @param {string} cssClass the class of the elements you wish to ignore
20493      */
20494     addInvalidHandleClass: function(cssClass) {
20495         this.invalidHandleClasses.push(cssClass);
20496     },
20497
20498     /**
20499      * Unsets an excluded tag name set by addInvalidHandleType
20500      * @method removeInvalidHandleType
20501      * @param {string} tagName the type of element to unexclude
20502      */
20503     removeInvalidHandleType: function(tagName) {
20504         var type = tagName.toUpperCase();
20505         // this.invalidHandleTypes[type] = null;
20506         delete this.invalidHandleTypes[type];
20507     },
20508
20509     /**
20510      * Unsets an invalid handle id
20511      * @method removeInvalidHandleId
20512      * @param {string} id the id of the element to re-enable
20513      */
20514     removeInvalidHandleId: function(id) {
20515         if (typeof id !== "string") {
20516             id = Roo.id(id);
20517         }
20518         delete this.invalidHandleIds[id];
20519     },
20520
20521     /**
20522      * Unsets an invalid css class
20523      * @method removeInvalidHandleClass
20524      * @param {string} cssClass the class of the element(s) you wish to
20525      * re-enable
20526      */
20527     removeInvalidHandleClass: function(cssClass) {
20528         for (var i=0, len=this.invalidHandleClasses.length; i<len; ++i) {
20529             if (this.invalidHandleClasses[i] == cssClass) {
20530                 delete this.invalidHandleClasses[i];
20531             }
20532         }
20533     },
20534
20535     /**
20536      * Checks the tag exclusion list to see if this click should be ignored
20537      * @method isValidHandleChild
20538      * @param {HTMLElement} node the HTMLElement to evaluate
20539      * @return {boolean} true if this is a valid tag type, false if not
20540      */
20541     isValidHandleChild: function(node) {
20542
20543         var valid = true;
20544         // var n = (node.nodeName == "#text") ? node.parentNode : node;
20545         var nodeName;
20546         try {
20547             nodeName = node.nodeName.toUpperCase();
20548         } catch(e) {
20549             nodeName = node.nodeName;
20550         }
20551         valid = valid && !this.invalidHandleTypes[nodeName];
20552         valid = valid && !this.invalidHandleIds[node.id];
20553
20554         for (var i=0, len=this.invalidHandleClasses.length; valid && i<len; ++i) {
20555             valid = !Dom.hasClass(node, this.invalidHandleClasses[i]);
20556         }
20557
20558
20559         return valid;
20560
20561     },
20562
20563     /**
20564      * Create the array of horizontal tick marks if an interval was specified
20565      * in setXConstraint().
20566      * @method setXTicks
20567      * @private
20568      */
20569     setXTicks: function(iStartX, iTickSize) {
20570         this.xTicks = [];
20571         this.xTickSize = iTickSize;
20572
20573         var tickMap = {};
20574
20575         for (var i = this.initPageX; i >= this.minX; i = i - iTickSize) {
20576             if (!tickMap[i]) {
20577                 this.xTicks[this.xTicks.length] = i;
20578                 tickMap[i] = true;
20579             }
20580         }
20581
20582         for (i = this.initPageX; i <= this.maxX; i = i + iTickSize) {
20583             if (!tickMap[i]) {
20584                 this.xTicks[this.xTicks.length] = i;
20585                 tickMap[i] = true;
20586             }
20587         }
20588
20589         this.xTicks.sort(this.DDM.numericSort) ;
20590     },
20591
20592     /**
20593      * Create the array of vertical tick marks if an interval was specified in
20594      * setYConstraint().
20595      * @method setYTicks
20596      * @private
20597      */
20598     setYTicks: function(iStartY, iTickSize) {
20599         this.yTicks = [];
20600         this.yTickSize = iTickSize;
20601
20602         var tickMap = {};
20603
20604         for (var i = this.initPageY; i >= this.minY; i = i - iTickSize) {
20605             if (!tickMap[i]) {
20606                 this.yTicks[this.yTicks.length] = i;
20607                 tickMap[i] = true;
20608             }
20609         }
20610
20611         for (i = this.initPageY; i <= this.maxY; i = i + iTickSize) {
20612             if (!tickMap[i]) {
20613                 this.yTicks[this.yTicks.length] = i;
20614                 tickMap[i] = true;
20615             }
20616         }
20617
20618         this.yTicks.sort(this.DDM.numericSort) ;
20619     },
20620
20621     /**
20622      * By default, the element can be dragged any place on the screen.  Use
20623      * this method to limit the horizontal travel of the element.  Pass in
20624      * 0,0 for the parameters if you want to lock the drag to the y axis.
20625      * @method setXConstraint
20626      * @param {int} iLeft the number of pixels the element can move to the left
20627      * @param {int} iRight the number of pixels the element can move to the
20628      * right
20629      * @param {int} iTickSize optional parameter for specifying that the
20630      * element
20631      * should move iTickSize pixels at a time.
20632      */
20633     setXConstraint: function(iLeft, iRight, iTickSize) {
20634         this.leftConstraint = iLeft;
20635         this.rightConstraint = iRight;
20636
20637         this.minX = this.initPageX - iLeft;
20638         this.maxX = this.initPageX + iRight;
20639         if (iTickSize) { this.setXTicks(this.initPageX, iTickSize); }
20640
20641         this.constrainX = true;
20642     },
20643
20644     /**
20645      * Clears any constraints applied to this instance.  Also clears ticks
20646      * since they can't exist independent of a constraint at this time.
20647      * @method clearConstraints
20648      */
20649     clearConstraints: function() {
20650         this.constrainX = false;
20651         this.constrainY = false;
20652         this.clearTicks();
20653     },
20654
20655     /**
20656      * Clears any tick interval defined for this instance
20657      * @method clearTicks
20658      */
20659     clearTicks: function() {
20660         this.xTicks = null;
20661         this.yTicks = null;
20662         this.xTickSize = 0;
20663         this.yTickSize = 0;
20664     },
20665
20666     /**
20667      * By default, the element can be dragged any place on the screen.  Set
20668      * this to limit the vertical travel of the element.  Pass in 0,0 for the
20669      * parameters if you want to lock the drag to the x axis.
20670      * @method setYConstraint
20671      * @param {int} iUp the number of pixels the element can move up
20672      * @param {int} iDown the number of pixels the element can move down
20673      * @param {int} iTickSize optional parameter for specifying that the
20674      * element should move iTickSize pixels at a time.
20675      */
20676     setYConstraint: function(iUp, iDown, iTickSize) {
20677         this.topConstraint = iUp;
20678         this.bottomConstraint = iDown;
20679
20680         this.minY = this.initPageY - iUp;
20681         this.maxY = this.initPageY + iDown;
20682         if (iTickSize) { this.setYTicks(this.initPageY, iTickSize); }
20683
20684         this.constrainY = true;
20685
20686     },
20687
20688     /**
20689      * resetConstraints must be called if you manually reposition a dd element.
20690      * @method resetConstraints
20691      * @param {boolean} maintainOffset
20692      */
20693     resetConstraints: function() {
20694
20695
20696         // Maintain offsets if necessary
20697         if (this.initPageX || this.initPageX === 0) {
20698             // figure out how much this thing has moved
20699             var dx = (this.maintainOffset) ? this.lastPageX - this.initPageX : 0;
20700             var dy = (this.maintainOffset) ? this.lastPageY - this.initPageY : 0;
20701
20702             this.setInitPosition(dx, dy);
20703
20704         // This is the first time we have detected the element's position
20705         } else {
20706             this.setInitPosition();
20707         }
20708
20709         if (this.constrainX) {
20710             this.setXConstraint( this.leftConstraint,
20711                                  this.rightConstraint,
20712                                  this.xTickSize        );
20713         }
20714
20715         if (this.constrainY) {
20716             this.setYConstraint( this.topConstraint,
20717                                  this.bottomConstraint,
20718                                  this.yTickSize         );
20719         }
20720     },
20721
20722     /**
20723      * Normally the drag element is moved pixel by pixel, but we can specify
20724      * that it move a number of pixels at a time.  This method resolves the
20725      * location when we have it set up like this.
20726      * @method getTick
20727      * @param {int} val where we want to place the object
20728      * @param {int[]} tickArray sorted array of valid points
20729      * @return {int} the closest tick
20730      * @private
20731      */
20732     getTick: function(val, tickArray) {
20733
20734         if (!tickArray) {
20735             // If tick interval is not defined, it is effectively 1 pixel,
20736             // so we return the value passed to us.
20737             return val;
20738         } else if (tickArray[0] >= val) {
20739             // The value is lower than the first tick, so we return the first
20740             // tick.
20741             return tickArray[0];
20742         } else {
20743             for (var i=0, len=tickArray.length; i<len; ++i) {
20744                 var next = i + 1;
20745                 if (tickArray[next] && tickArray[next] >= val) {
20746                     var diff1 = val - tickArray[i];
20747                     var diff2 = tickArray[next] - val;
20748                     return (diff2 > diff1) ? tickArray[i] : tickArray[next];
20749                 }
20750             }
20751
20752             // The value is larger than the last tick, so we return the last
20753             // tick.
20754             return tickArray[tickArray.length - 1];
20755         }
20756     },
20757
20758     /**
20759      * toString method
20760      * @method toString
20761      * @return {string} string representation of the dd obj
20762      */
20763     toString: function() {
20764         return ("DragDrop " + this.id);
20765     }
20766
20767 });
20768
20769 })();
20770 /*
20771  * Based on:
20772  * Ext JS Library 1.1.1
20773  * Copyright(c) 2006-2007, Ext JS, LLC.
20774  *
20775  * Originally Released Under LGPL - original licence link has changed is not relivant.
20776  *
20777  * Fork - LGPL
20778  * <script type="text/javascript">
20779  */
20780
20781
20782 /**
20783  * The drag and drop utility provides a framework for building drag and drop
20784  * applications.  In addition to enabling drag and drop for specific elements,
20785  * the drag and drop elements are tracked by the manager class, and the
20786  * interactions between the various elements are tracked during the drag and
20787  * the implementing code is notified about these important moments.
20788  */
20789
20790 // Only load the library once.  Rewriting the manager class would orphan
20791 // existing drag and drop instances.
20792 if (!Roo.dd.DragDropMgr) {
20793
20794 /**
20795  * @class Roo.dd.DragDropMgr
20796  * DragDropMgr is a singleton that tracks the element interaction for
20797  * all DragDrop items in the window.  Generally, you will not call
20798  * this class directly, but it does have helper methods that could
20799  * be useful in your DragDrop implementations.
20800  * @static
20801  */
20802 Roo.dd.DragDropMgr = function() {
20803
20804     var Event = Roo.EventManager;
20805
20806     return {
20807
20808         /**
20809          * Two dimensional Array of registered DragDrop objects.  The first
20810          * dimension is the DragDrop item group, the second the DragDrop
20811          * object.
20812          * @property ids
20813          * @type {string: string}
20814          * @private
20815          * @static
20816          */
20817         ids: {},
20818
20819         /**
20820          * Array of element ids defined as drag handles.  Used to determine
20821          * if the element that generated the mousedown event is actually the
20822          * handle and not the html element itself.
20823          * @property handleIds
20824          * @type {string: string}
20825          * @private
20826          * @static
20827          */
20828         handleIds: {},
20829
20830         /**
20831          * the DragDrop object that is currently being dragged
20832          * @property dragCurrent
20833          * @type DragDrop
20834          * @private
20835          * @static
20836          **/
20837         dragCurrent: null,
20838
20839         /**
20840          * the DragDrop object(s) that are being hovered over
20841          * @property dragOvers
20842          * @type Array
20843          * @private
20844          * @static
20845          */
20846         dragOvers: {},
20847
20848         /**
20849          * the X distance between the cursor and the object being dragged
20850          * @property deltaX
20851          * @type int
20852          * @private
20853          * @static
20854          */
20855         deltaX: 0,
20856
20857         /**
20858          * the Y distance between the cursor and the object being dragged
20859          * @property deltaY
20860          * @type int
20861          * @private
20862          * @static
20863          */
20864         deltaY: 0,
20865
20866         /**
20867          * Flag to determine if we should prevent the default behavior of the
20868          * events we define. By default this is true, but this can be set to
20869          * false if you need the default behavior (not recommended)
20870          * @property preventDefault
20871          * @type boolean
20872          * @static
20873          */
20874         preventDefault: true,
20875
20876         /**
20877          * Flag to determine if we should stop the propagation of the events
20878          * we generate. This is true by default but you may want to set it to
20879          * false if the html element contains other features that require the
20880          * mouse click.
20881          * @property stopPropagation
20882          * @type boolean
20883          * @static
20884          */
20885         stopPropagation: true,
20886
20887         /**
20888          * Internal flag that is set to true when drag and drop has been
20889          * intialized
20890          * @property initialized
20891          * @private
20892          * @static
20893          */
20894         initalized: false,
20895
20896         /**
20897          * All drag and drop can be disabled.
20898          * @property locked
20899          * @private
20900          * @static
20901          */
20902         locked: false,
20903
20904         /**
20905          * Called the first time an element is registered.
20906          * @method init
20907          * @private
20908          * @static
20909          */
20910         init: function() {
20911             this.initialized = true;
20912         },
20913
20914         /**
20915          * In point mode, drag and drop interaction is defined by the
20916          * location of the cursor during the drag/drop
20917          * @property POINT
20918          * @type int
20919          * @static
20920          */
20921         POINT: 0,
20922
20923         /**
20924          * In intersect mode, drag and drop interactio nis defined by the
20925          * overlap of two or more drag and drop objects.
20926          * @property INTERSECT
20927          * @type int
20928          * @static
20929          */
20930         INTERSECT: 1,
20931
20932         /**
20933          * The current drag and drop mode.  Default: POINT
20934          * @property mode
20935          * @type int
20936          * @static
20937          */
20938         mode: 0,
20939
20940         /**
20941          * Runs method on all drag and drop objects
20942          * @method _execOnAll
20943          * @private
20944          * @static
20945          */
20946         _execOnAll: function(sMethod, args) {
20947             for (var i in this.ids) {
20948                 for (var j in this.ids[i]) {
20949                     var oDD = this.ids[i][j];
20950                     if (! this.isTypeOfDD(oDD)) {
20951                         continue;
20952                     }
20953                     oDD[sMethod].apply(oDD, args);
20954                 }
20955             }
20956         },
20957
20958         /**
20959          * Drag and drop initialization.  Sets up the global event handlers
20960          * @method _onLoad
20961          * @private
20962          * @static
20963          */
20964         _onLoad: function() {
20965
20966             this.init();
20967
20968             if (!Roo.isTouch) {
20969                 Event.on(document, "mouseup",   this.handleMouseUp, this, true);
20970                 Event.on(document, "mousemove", this.handleMouseMove, this, true);
20971             }
20972             Event.on(document, "touchend",   this.handleMouseUp, this, true);
20973             Event.on(document, "touchmove", this.handleMouseMove, this, true);
20974             
20975             Event.on(window,   "unload",    this._onUnload, this, true);
20976             Event.on(window,   "resize",    this._onResize, this, true);
20977             // Event.on(window,   "mouseout",    this._test);
20978
20979         },
20980
20981         /**
20982          * Reset constraints on all drag and drop objs
20983          * @method _onResize
20984          * @private
20985          * @static
20986          */
20987         _onResize: function(e) {
20988             this._execOnAll("resetConstraints", []);
20989         },
20990
20991         /**
20992          * Lock all drag and drop functionality
20993          * @method lock
20994          * @static
20995          */
20996         lock: function() { this.locked = true; },
20997
20998         /**
20999          * Unlock all drag and drop functionality
21000          * @method unlock
21001          * @static
21002          */
21003         unlock: function() { this.locked = false; },
21004
21005         /**
21006          * Is drag and drop locked?
21007          * @method isLocked
21008          * @return {boolean} True if drag and drop is locked, false otherwise.
21009          * @static
21010          */
21011         isLocked: function() { return this.locked; },
21012
21013         /**
21014          * Location cache that is set for all drag drop objects when a drag is
21015          * initiated, cleared when the drag is finished.
21016          * @property locationCache
21017          * @private
21018          * @static
21019          */
21020         locationCache: {},
21021
21022         /**
21023          * Set useCache to false if you want to force object the lookup of each
21024          * drag and drop linked element constantly during a drag.
21025          * @property useCache
21026          * @type boolean
21027          * @static
21028          */
21029         useCache: true,
21030
21031         /**
21032          * The number of pixels that the mouse needs to move after the
21033          * mousedown before the drag is initiated.  Default=3;
21034          * @property clickPixelThresh
21035          * @type int
21036          * @static
21037          */
21038         clickPixelThresh: 3,
21039
21040         /**
21041          * The number of milliseconds after the mousedown event to initiate the
21042          * drag if we don't get a mouseup event. Default=1000
21043          * @property clickTimeThresh
21044          * @type int
21045          * @static
21046          */
21047         clickTimeThresh: 350,
21048
21049         /**
21050          * Flag that indicates that either the drag pixel threshold or the
21051          * mousdown time threshold has been met
21052          * @property dragThreshMet
21053          * @type boolean
21054          * @private
21055          * @static
21056          */
21057         dragThreshMet: false,
21058
21059         /**
21060          * Timeout used for the click time threshold
21061          * @property clickTimeout
21062          * @type Object
21063          * @private
21064          * @static
21065          */
21066         clickTimeout: null,
21067
21068         /**
21069          * The X position of the mousedown event stored for later use when a
21070          * drag threshold is met.
21071          * @property startX
21072          * @type int
21073          * @private
21074          * @static
21075          */
21076         startX: 0,
21077
21078         /**
21079          * The Y position of the mousedown event stored for later use when a
21080          * drag threshold is met.
21081          * @property startY
21082          * @type int
21083          * @private
21084          * @static
21085          */
21086         startY: 0,
21087
21088         /**
21089          * Each DragDrop instance must be registered with the DragDropMgr.
21090          * This is executed in DragDrop.init()
21091          * @method regDragDrop
21092          * @param {DragDrop} oDD the DragDrop object to register
21093          * @param {String} sGroup the name of the group this element belongs to
21094          * @static
21095          */
21096         regDragDrop: function(oDD, sGroup) {
21097             if (!this.initialized) { this.init(); }
21098
21099             if (!this.ids[sGroup]) {
21100                 this.ids[sGroup] = {};
21101             }
21102             this.ids[sGroup][oDD.id] = oDD;
21103         },
21104
21105         /**
21106          * Removes the supplied dd instance from the supplied group. Executed
21107          * by DragDrop.removeFromGroup, so don't call this function directly.
21108          * @method removeDDFromGroup
21109          * @private
21110          * @static
21111          */
21112         removeDDFromGroup: function(oDD, sGroup) {
21113             if (!this.ids[sGroup]) {
21114                 this.ids[sGroup] = {};
21115             }
21116
21117             var obj = this.ids[sGroup];
21118             if (obj && obj[oDD.id]) {
21119                 delete obj[oDD.id];
21120             }
21121         },
21122
21123         /**
21124          * Unregisters a drag and drop item.  This is executed in
21125          * DragDrop.unreg, use that method instead of calling this directly.
21126          * @method _remove
21127          * @private
21128          * @static
21129          */
21130         _remove: function(oDD) {
21131             for (var g in oDD.groups) {
21132                 if (g && this.ids[g][oDD.id]) {
21133                     delete this.ids[g][oDD.id];
21134                 }
21135             }
21136             delete this.handleIds[oDD.id];
21137         },
21138
21139         /**
21140          * Each DragDrop handle element must be registered.  This is done
21141          * automatically when executing DragDrop.setHandleElId()
21142          * @method regHandle
21143          * @param {String} sDDId the DragDrop id this element is a handle for
21144          * @param {String} sHandleId the id of the element that is the drag
21145          * handle
21146          * @static
21147          */
21148         regHandle: function(sDDId, sHandleId) {
21149             if (!this.handleIds[sDDId]) {
21150                 this.handleIds[sDDId] = {};
21151             }
21152             this.handleIds[sDDId][sHandleId] = sHandleId;
21153         },
21154
21155         /**
21156          * Utility function to determine if a given element has been
21157          * registered as a drag drop item.
21158          * @method isDragDrop
21159          * @param {String} id the element id to check
21160          * @return {boolean} true if this element is a DragDrop item,
21161          * false otherwise
21162          * @static
21163          */
21164         isDragDrop: function(id) {
21165             return ( this.getDDById(id) ) ? true : false;
21166         },
21167
21168         /**
21169          * Returns the drag and drop instances that are in all groups the
21170          * passed in instance belongs to.
21171          * @method getRelated
21172          * @param {DragDrop} p_oDD the obj to get related data for
21173          * @param {boolean} bTargetsOnly if true, only return targetable objs
21174          * @return {DragDrop[]} the related instances
21175          * @static
21176          */
21177         getRelated: function(p_oDD, bTargetsOnly) {
21178             var oDDs = [];
21179             for (var i in p_oDD.groups) {
21180                 for (j in this.ids[i]) {
21181                     var dd = this.ids[i][j];
21182                     if (! this.isTypeOfDD(dd)) {
21183                         continue;
21184                     }
21185                     if (!bTargetsOnly || dd.isTarget) {
21186                         oDDs[oDDs.length] = dd;
21187                     }
21188                 }
21189             }
21190
21191             return oDDs;
21192         },
21193
21194         /**
21195          * Returns true if the specified dd target is a legal target for
21196          * the specifice drag obj
21197          * @method isLegalTarget
21198          * @param {DragDrop} the drag obj
21199          * @param {DragDrop} the target
21200          * @return {boolean} true if the target is a legal target for the
21201          * dd obj
21202          * @static
21203          */
21204         isLegalTarget: function (oDD, oTargetDD) {
21205             var targets = this.getRelated(oDD, true);
21206             for (var i=0, len=targets.length;i<len;++i) {
21207                 if (targets[i].id == oTargetDD.id) {
21208                     return true;
21209                 }
21210             }
21211
21212             return false;
21213         },
21214
21215         /**
21216          * My goal is to be able to transparently determine if an object is
21217          * typeof DragDrop, and the exact subclass of DragDrop.  typeof
21218          * returns "object", oDD.constructor.toString() always returns
21219          * "DragDrop" and not the name of the subclass.  So for now it just
21220          * evaluates a well-known variable in DragDrop.
21221          * @method isTypeOfDD
21222          * @param {Object} the object to evaluate
21223          * @return {boolean} true if typeof oDD = DragDrop
21224          * @static
21225          */
21226         isTypeOfDD: function (oDD) {
21227             return (oDD && oDD.__ygDragDrop);
21228         },
21229
21230         /**
21231          * Utility function to determine if a given element has been
21232          * registered as a drag drop handle for the given Drag Drop object.
21233          * @method isHandle
21234          * @param {String} id the element id to check
21235          * @return {boolean} true if this element is a DragDrop handle, false
21236          * otherwise
21237          * @static
21238          */
21239         isHandle: function(sDDId, sHandleId) {
21240             return ( this.handleIds[sDDId] &&
21241                             this.handleIds[sDDId][sHandleId] );
21242         },
21243
21244         /**
21245          * Returns the DragDrop instance for a given id
21246          * @method getDDById
21247          * @param {String} id the id of the DragDrop object
21248          * @return {DragDrop} the drag drop object, null if it is not found
21249          * @static
21250          */
21251         getDDById: function(id) {
21252             for (var i in this.ids) {
21253                 if (this.ids[i][id]) {
21254                     return this.ids[i][id];
21255                 }
21256             }
21257             return null;
21258         },
21259
21260         /**
21261          * Fired after a registered DragDrop object gets the mousedown event.
21262          * Sets up the events required to track the object being dragged
21263          * @method handleMouseDown
21264          * @param {Event} e the event
21265          * @param oDD the DragDrop object being dragged
21266          * @private
21267          * @static
21268          */
21269         handleMouseDown: function(e, oDD) {
21270             if(Roo.QuickTips){
21271                 Roo.QuickTips.disable();
21272             }
21273             this.currentTarget = e.getTarget();
21274
21275             this.dragCurrent = oDD;
21276
21277             var el = oDD.getEl();
21278
21279             // track start position
21280             this.startX = e.getPageX();
21281             this.startY = e.getPageY();
21282
21283             this.deltaX = this.startX - el.offsetLeft;
21284             this.deltaY = this.startY - el.offsetTop;
21285
21286             this.dragThreshMet = false;
21287
21288             this.clickTimeout = setTimeout(
21289                     function() {
21290                         var DDM = Roo.dd.DDM;
21291                         DDM.startDrag(DDM.startX, DDM.startY);
21292                     },
21293                     this.clickTimeThresh );
21294         },
21295
21296         /**
21297          * Fired when either the drag pixel threshol or the mousedown hold
21298          * time threshold has been met.
21299          * @method startDrag
21300          * @param x {int} the X position of the original mousedown
21301          * @param y {int} the Y position of the original mousedown
21302          * @static
21303          */
21304         startDrag: function(x, y) {
21305             clearTimeout(this.clickTimeout);
21306             if (this.dragCurrent) {
21307                 this.dragCurrent.b4StartDrag(x, y);
21308                 this.dragCurrent.startDrag(x, y);
21309             }
21310             this.dragThreshMet = true;
21311         },
21312
21313         /**
21314          * Internal function to handle the mouseup event.  Will be invoked
21315          * from the context of the document.
21316          * @method handleMouseUp
21317          * @param {Event} e the event
21318          * @private
21319          * @static
21320          */
21321         handleMouseUp: function(e) {
21322
21323             if(Roo.QuickTips){
21324                 Roo.QuickTips.enable();
21325             }
21326             if (! this.dragCurrent) {
21327                 return;
21328             }
21329
21330             clearTimeout(this.clickTimeout);
21331
21332             if (this.dragThreshMet) {
21333                 this.fireEvents(e, true);
21334             } else {
21335             }
21336
21337             this.stopDrag(e);
21338
21339             this.stopEvent(e);
21340         },
21341
21342         /**
21343          * Utility to stop event propagation and event default, if these
21344          * features are turned on.
21345          * @method stopEvent
21346          * @param {Event} e the event as returned by this.getEvent()
21347          * @static
21348          */
21349         stopEvent: function(e){
21350             if(this.stopPropagation) {
21351                 e.stopPropagation();
21352             }
21353
21354             if (this.preventDefault) {
21355                 e.preventDefault();
21356             }
21357         },
21358
21359         /**
21360          * Internal function to clean up event handlers after the drag
21361          * operation is complete
21362          * @method stopDrag
21363          * @param {Event} e the event
21364          * @private
21365          * @static
21366          */
21367         stopDrag: function(e) {
21368             // Fire the drag end event for the item that was dragged
21369             if (this.dragCurrent) {
21370                 if (this.dragThreshMet) {
21371                     this.dragCurrent.b4EndDrag(e);
21372                     this.dragCurrent.endDrag(e);
21373                 }
21374
21375                 this.dragCurrent.onMouseUp(e);
21376             }
21377
21378             this.dragCurrent = null;
21379             this.dragOvers = {};
21380         },
21381
21382         /**
21383          * Internal function to handle the mousemove event.  Will be invoked
21384          * from the context of the html element.
21385          *
21386          * @TODO figure out what we can do about mouse events lost when the
21387          * user drags objects beyond the window boundary.  Currently we can
21388          * detect this in internet explorer by verifying that the mouse is
21389          * down during the mousemove event.  Firefox doesn't give us the
21390          * button state on the mousemove event.
21391          * @method handleMouseMove
21392          * @param {Event} e the event
21393          * @private
21394          * @static
21395          */
21396         handleMouseMove: function(e) {
21397             if (! this.dragCurrent) {
21398                 return true;
21399             }
21400
21401             // var button = e.which || e.button;
21402
21403             // check for IE mouseup outside of page boundary
21404             if (Roo.isIE && (e.button !== 0 && e.button !== 1 && e.button !== 2)) {
21405                 this.stopEvent(e);
21406                 return this.handleMouseUp(e);
21407             }
21408
21409             if (!this.dragThreshMet) {
21410                 var diffX = Math.abs(this.startX - e.getPageX());
21411                 var diffY = Math.abs(this.startY - e.getPageY());
21412                 if (diffX > this.clickPixelThresh ||
21413                             diffY > this.clickPixelThresh) {
21414                     this.startDrag(this.startX, this.startY);
21415                 }
21416             }
21417
21418             if (this.dragThreshMet) {
21419                 this.dragCurrent.b4Drag(e);
21420                 this.dragCurrent.onDrag(e);
21421                 if(!this.dragCurrent.moveOnly){
21422                     this.fireEvents(e, false);
21423                 }
21424             }
21425
21426             this.stopEvent(e);
21427
21428             return true;
21429         },
21430
21431         /**
21432          * Iterates over all of the DragDrop elements to find ones we are
21433          * hovering over or dropping on
21434          * @method fireEvents
21435          * @param {Event} e the event
21436          * @param {boolean} isDrop is this a drop op or a mouseover op?
21437          * @private
21438          * @static
21439          */
21440         fireEvents: function(e, isDrop) {
21441             var dc = this.dragCurrent;
21442
21443             // If the user did the mouse up outside of the window, we could
21444             // get here even though we have ended the drag.
21445             if (!dc || dc.isLocked()) {
21446                 return;
21447             }
21448
21449             var pt = e.getPoint();
21450
21451             // cache the previous dragOver array
21452             var oldOvers = [];
21453
21454             var outEvts   = [];
21455             var overEvts  = [];
21456             var dropEvts  = [];
21457             var enterEvts = [];
21458
21459             // Check to see if the object(s) we were hovering over is no longer
21460             // being hovered over so we can fire the onDragOut event
21461             for (var i in this.dragOvers) {
21462
21463                 var ddo = this.dragOvers[i];
21464
21465                 if (! this.isTypeOfDD(ddo)) {
21466                     continue;
21467                 }
21468
21469                 if (! this.isOverTarget(pt, ddo, this.mode)) {
21470                     outEvts.push( ddo );
21471                 }
21472
21473                 oldOvers[i] = true;
21474                 delete this.dragOvers[i];
21475             }
21476
21477             for (var sGroup in dc.groups) {
21478
21479                 if ("string" != typeof sGroup) {
21480                     continue;
21481                 }
21482
21483                 for (i in this.ids[sGroup]) {
21484                     var oDD = this.ids[sGroup][i];
21485                     if (! this.isTypeOfDD(oDD)) {
21486                         continue;
21487                     }
21488
21489                     if (oDD.isTarget && !oDD.isLocked() && oDD != dc) {
21490                         if (this.isOverTarget(pt, oDD, this.mode)) {
21491                             // look for drop interactions
21492                             if (isDrop) {
21493                                 dropEvts.push( oDD );
21494                             // look for drag enter and drag over interactions
21495                             } else {
21496
21497                                 // initial drag over: dragEnter fires
21498                                 if (!oldOvers[oDD.id]) {
21499                                     enterEvts.push( oDD );
21500                                 // subsequent drag overs: dragOver fires
21501                                 } else {
21502                                     overEvts.push( oDD );
21503                                 }
21504
21505                                 this.dragOvers[oDD.id] = oDD;
21506                             }
21507                         }
21508                     }
21509                 }
21510             }
21511
21512             if (this.mode) {
21513                 if (outEvts.length) {
21514                     dc.b4DragOut(e, outEvts);
21515                     dc.onDragOut(e, outEvts);
21516                 }
21517
21518                 if (enterEvts.length) {
21519                     dc.onDragEnter(e, enterEvts);
21520                 }
21521
21522                 if (overEvts.length) {
21523                     dc.b4DragOver(e, overEvts);
21524                     dc.onDragOver(e, overEvts);
21525                 }
21526
21527                 if (dropEvts.length) {
21528                     dc.b4DragDrop(e, dropEvts);
21529                     dc.onDragDrop(e, dropEvts);
21530                 }
21531
21532             } else {
21533                 // fire dragout events
21534                 var len = 0;
21535                 for (i=0, len=outEvts.length; i<len; ++i) {
21536                     dc.b4DragOut(e, outEvts[i].id);
21537                     dc.onDragOut(e, outEvts[i].id);
21538                 }
21539
21540                 // fire enter events
21541                 for (i=0,len=enterEvts.length; i<len; ++i) {
21542                     // dc.b4DragEnter(e, oDD.id);
21543                     dc.onDragEnter(e, enterEvts[i].id);
21544                 }
21545
21546                 // fire over events
21547                 for (i=0,len=overEvts.length; i<len; ++i) {
21548                     dc.b4DragOver(e, overEvts[i].id);
21549                     dc.onDragOver(e, overEvts[i].id);
21550                 }
21551
21552                 // fire drop events
21553                 for (i=0, len=dropEvts.length; i<len; ++i) {
21554                     dc.b4DragDrop(e, dropEvts[i].id);
21555                     dc.onDragDrop(e, dropEvts[i].id);
21556                 }
21557
21558             }
21559
21560             // notify about a drop that did not find a target
21561             if (isDrop && !dropEvts.length) {
21562                 dc.onInvalidDrop(e);
21563             }
21564
21565         },
21566
21567         /**
21568          * Helper function for getting the best match from the list of drag
21569          * and drop objects returned by the drag and drop events when we are
21570          * in INTERSECT mode.  It returns either the first object that the
21571          * cursor is over, or the object that has the greatest overlap with
21572          * the dragged element.
21573          * @method getBestMatch
21574          * @param  {DragDrop[]} dds The array of drag and drop objects
21575          * targeted
21576          * @return {DragDrop}       The best single match
21577          * @static
21578          */
21579         getBestMatch: function(dds) {
21580             var winner = null;
21581             // Return null if the input is not what we expect
21582             //if (!dds || !dds.length || dds.length == 0) {
21583                // winner = null;
21584             // If there is only one item, it wins
21585             //} else if (dds.length == 1) {
21586
21587             var len = dds.length;
21588
21589             if (len == 1) {
21590                 winner = dds[0];
21591             } else {
21592                 // Loop through the targeted items
21593                 for (var i=0; i<len; ++i) {
21594                     var dd = dds[i];
21595                     // If the cursor is over the object, it wins.  If the
21596                     // cursor is over multiple matches, the first one we come
21597                     // to wins.
21598                     if (dd.cursorIsOver) {
21599                         winner = dd;
21600                         break;
21601                     // Otherwise the object with the most overlap wins
21602                     } else {
21603                         if (!winner ||
21604                             winner.overlap.getArea() < dd.overlap.getArea()) {
21605                             winner = dd;
21606                         }
21607                     }
21608                 }
21609             }
21610
21611             return winner;
21612         },
21613
21614         /**
21615          * Refreshes the cache of the top-left and bottom-right points of the
21616          * drag and drop objects in the specified group(s).  This is in the
21617          * format that is stored in the drag and drop instance, so typical
21618          * usage is:
21619          * <code>
21620          * Roo.dd.DragDropMgr.refreshCache(ddinstance.groups);
21621          * </code>
21622          * Alternatively:
21623          * <code>
21624          * Roo.dd.DragDropMgr.refreshCache({group1:true, group2:true});
21625          * </code>
21626          * @TODO this really should be an indexed array.  Alternatively this
21627          * method could accept both.
21628          * @method refreshCache
21629          * @param {Object} groups an associative array of groups to refresh
21630          * @static
21631          */
21632         refreshCache: function(groups) {
21633             for (var sGroup in groups) {
21634                 if ("string" != typeof sGroup) {
21635                     continue;
21636                 }
21637                 for (var i in this.ids[sGroup]) {
21638                     var oDD = this.ids[sGroup][i];
21639
21640                     if (this.isTypeOfDD(oDD)) {
21641                     // if (this.isTypeOfDD(oDD) && oDD.isTarget) {
21642                         var loc = this.getLocation(oDD);
21643                         if (loc) {
21644                             this.locationCache[oDD.id] = loc;
21645                         } else {
21646                             delete this.locationCache[oDD.id];
21647                             // this will unregister the drag and drop object if
21648                             // the element is not in a usable state
21649                             // oDD.unreg();
21650                         }
21651                     }
21652                 }
21653             }
21654         },
21655
21656         /**
21657          * This checks to make sure an element exists and is in the DOM.  The
21658          * main purpose is to handle cases where innerHTML is used to remove
21659          * drag and drop objects from the DOM.  IE provides an 'unspecified
21660          * error' when trying to access the offsetParent of such an element
21661          * @method verifyEl
21662          * @param {HTMLElement} el the element to check
21663          * @return {boolean} true if the element looks usable
21664          * @static
21665          */
21666         verifyEl: function(el) {
21667             if (el) {
21668                 var parent;
21669                 if(Roo.isIE){
21670                     try{
21671                         parent = el.offsetParent;
21672                     }catch(e){}
21673                 }else{
21674                     parent = el.offsetParent;
21675                 }
21676                 if (parent) {
21677                     return true;
21678                 }
21679             }
21680
21681             return false;
21682         },
21683
21684         /**
21685          * Returns a Region object containing the drag and drop element's position
21686          * and size, including the padding configured for it
21687          * @method getLocation
21688          * @param {DragDrop} oDD the drag and drop object to get the
21689          *                       location for
21690          * @return {Roo.lib.Region} a Region object representing the total area
21691          *                             the element occupies, including any padding
21692          *                             the instance is configured for.
21693          * @static
21694          */
21695         getLocation: function(oDD) {
21696             if (! this.isTypeOfDD(oDD)) {
21697                 return null;
21698             }
21699
21700             var el = oDD.getEl(), pos, x1, x2, y1, y2, t, r, b, l;
21701
21702             try {
21703                 pos= Roo.lib.Dom.getXY(el);
21704             } catch (e) { }
21705
21706             if (!pos) {
21707                 return null;
21708             }
21709
21710             x1 = pos[0];
21711             x2 = x1 + el.offsetWidth;
21712             y1 = pos[1];
21713             y2 = y1 + el.offsetHeight;
21714
21715             t = y1 - oDD.padding[0];
21716             r = x2 + oDD.padding[1];
21717             b = y2 + oDD.padding[2];
21718             l = x1 - oDD.padding[3];
21719
21720             return new Roo.lib.Region( t, r, b, l );
21721         },
21722
21723         /**
21724          * Checks the cursor location to see if it over the target
21725          * @method isOverTarget
21726          * @param {Roo.lib.Point} pt The point to evaluate
21727          * @param {DragDrop} oTarget the DragDrop object we are inspecting
21728          * @return {boolean} true if the mouse is over the target
21729          * @private
21730          * @static
21731          */
21732         isOverTarget: function(pt, oTarget, intersect) {
21733             // use cache if available
21734             var loc = this.locationCache[oTarget.id];
21735             if (!loc || !this.useCache) {
21736                 loc = this.getLocation(oTarget);
21737                 this.locationCache[oTarget.id] = loc;
21738
21739             }
21740
21741             if (!loc) {
21742                 return false;
21743             }
21744
21745             oTarget.cursorIsOver = loc.contains( pt );
21746
21747             // DragDrop is using this as a sanity check for the initial mousedown
21748             // in this case we are done.  In POINT mode, if the drag obj has no
21749             // contraints, we are also done. Otherwise we need to evaluate the
21750             // location of the target as related to the actual location of the
21751             // dragged element.
21752             var dc = this.dragCurrent;
21753             if (!dc || !dc.getTargetCoord ||
21754                     (!intersect && !dc.constrainX && !dc.constrainY)) {
21755                 return oTarget.cursorIsOver;
21756             }
21757
21758             oTarget.overlap = null;
21759
21760             // Get the current location of the drag element, this is the
21761             // location of the mouse event less the delta that represents
21762             // where the original mousedown happened on the element.  We
21763             // need to consider constraints and ticks as well.
21764             var pos = dc.getTargetCoord(pt.x, pt.y);
21765
21766             var el = dc.getDragEl();
21767             var curRegion = new Roo.lib.Region( pos.y,
21768                                                    pos.x + el.offsetWidth,
21769                                                    pos.y + el.offsetHeight,
21770                                                    pos.x );
21771
21772             var overlap = curRegion.intersect(loc);
21773
21774             if (overlap) {
21775                 oTarget.overlap = overlap;
21776                 return (intersect) ? true : oTarget.cursorIsOver;
21777             } else {
21778                 return false;
21779             }
21780         },
21781
21782         /**
21783          * unload event handler
21784          * @method _onUnload
21785          * @private
21786          * @static
21787          */
21788         _onUnload: function(e, me) {
21789             Roo.dd.DragDropMgr.unregAll();
21790         },
21791
21792         /**
21793          * Cleans up the drag and drop events and objects.
21794          * @method unregAll
21795          * @private
21796          * @static
21797          */
21798         unregAll: function() {
21799
21800             if (this.dragCurrent) {
21801                 this.stopDrag();
21802                 this.dragCurrent = null;
21803             }
21804
21805             this._execOnAll("unreg", []);
21806
21807             for (i in this.elementCache) {
21808                 delete this.elementCache[i];
21809             }
21810
21811             this.elementCache = {};
21812             this.ids = {};
21813         },
21814
21815         /**
21816          * A cache of DOM elements
21817          * @property elementCache
21818          * @private
21819          * @static
21820          */
21821         elementCache: {},
21822
21823         /**
21824          * Get the wrapper for the DOM element specified
21825          * @method getElWrapper
21826          * @param {String} id the id of the element to get
21827          * @return {Roo.dd.DDM.ElementWrapper} the wrapped element
21828          * @private
21829          * @deprecated This wrapper isn't that useful
21830          * @static
21831          */
21832         getElWrapper: function(id) {
21833             var oWrapper = this.elementCache[id];
21834             if (!oWrapper || !oWrapper.el) {
21835                 oWrapper = this.elementCache[id] =
21836                     new this.ElementWrapper(Roo.getDom(id));
21837             }
21838             return oWrapper;
21839         },
21840
21841         /**
21842          * Returns the actual DOM element
21843          * @method getElement
21844          * @param {String} id the id of the elment to get
21845          * @return {Object} The element
21846          * @deprecated use Roo.getDom instead
21847          * @static
21848          */
21849         getElement: function(id) {
21850             return Roo.getDom(id);
21851         },
21852
21853         /**
21854          * Returns the style property for the DOM element (i.e.,
21855          * document.getElById(id).style)
21856          * @method getCss
21857          * @param {String} id the id of the elment to get
21858          * @return {Object} The style property of the element
21859          * @deprecated use Roo.getDom instead
21860          * @static
21861          */
21862         getCss: function(id) {
21863             var el = Roo.getDom(id);
21864             return (el) ? el.style : null;
21865         },
21866
21867         /**
21868          * Inner class for cached elements
21869          * @class DragDropMgr.ElementWrapper
21870          * @for DragDropMgr
21871          * @private
21872          * @deprecated
21873          */
21874         ElementWrapper: function(el) {
21875                 /**
21876                  * The element
21877                  * @property el
21878                  */
21879                 this.el = el || null;
21880                 /**
21881                  * The element id
21882                  * @property id
21883                  */
21884                 this.id = this.el && el.id;
21885                 /**
21886                  * A reference to the style property
21887                  * @property css
21888                  */
21889                 this.css = this.el && el.style;
21890             },
21891
21892         /**
21893          * Returns the X position of an html element
21894          * @method getPosX
21895          * @param el the element for which to get the position
21896          * @return {int} the X coordinate
21897          * @for DragDropMgr
21898          * @deprecated use Roo.lib.Dom.getX instead
21899          * @static
21900          */
21901         getPosX: function(el) {
21902             return Roo.lib.Dom.getX(el);
21903         },
21904
21905         /**
21906          * Returns the Y position of an html element
21907          * @method getPosY
21908          * @param el the element for which to get the position
21909          * @return {int} the Y coordinate
21910          * @deprecated use Roo.lib.Dom.getY instead
21911          * @static
21912          */
21913         getPosY: function(el) {
21914             return Roo.lib.Dom.getY(el);
21915         },
21916
21917         /**
21918          * Swap two nodes.  In IE, we use the native method, for others we
21919          * emulate the IE behavior
21920          * @method swapNode
21921          * @param n1 the first node to swap
21922          * @param n2 the other node to swap
21923          * @static
21924          */
21925         swapNode: function(n1, n2) {
21926             if (n1.swapNode) {
21927                 n1.swapNode(n2);
21928             } else {
21929                 var p = n2.parentNode;
21930                 var s = n2.nextSibling;
21931
21932                 if (s == n1) {
21933                     p.insertBefore(n1, n2);
21934                 } else if (n2 == n1.nextSibling) {
21935                     p.insertBefore(n2, n1);
21936                 } else {
21937                     n1.parentNode.replaceChild(n2, n1);
21938                     p.insertBefore(n1, s);
21939                 }
21940             }
21941         },
21942
21943         /**
21944          * Returns the current scroll position
21945          * @method getScroll
21946          * @private
21947          * @static
21948          */
21949         getScroll: function () {
21950             var t, l, dde=document.documentElement, db=document.body;
21951             if (dde && (dde.scrollTop || dde.scrollLeft)) {
21952                 t = dde.scrollTop;
21953                 l = dde.scrollLeft;
21954             } else if (db) {
21955                 t = db.scrollTop;
21956                 l = db.scrollLeft;
21957             } else {
21958
21959             }
21960             return { top: t, left: l };
21961         },
21962
21963         /**
21964          * Returns the specified element style property
21965          * @method getStyle
21966          * @param {HTMLElement} el          the element
21967          * @param {string}      styleProp   the style property
21968          * @return {string} The value of the style property
21969          * @deprecated use Roo.lib.Dom.getStyle
21970          * @static
21971          */
21972         getStyle: function(el, styleProp) {
21973             return Roo.fly(el).getStyle(styleProp);
21974         },
21975
21976         /**
21977          * Gets the scrollTop
21978          * @method getScrollTop
21979          * @return {int} the document's scrollTop
21980          * @static
21981          */
21982         getScrollTop: function () { return this.getScroll().top; },
21983
21984         /**
21985          * Gets the scrollLeft
21986          * @method getScrollLeft
21987          * @return {int} the document's scrollTop
21988          * @static
21989          */
21990         getScrollLeft: function () { return this.getScroll().left; },
21991
21992         /**
21993          * Sets the x/y position of an element to the location of the
21994          * target element.
21995          * @method moveToEl
21996          * @param {HTMLElement} moveEl      The element to move
21997          * @param {HTMLElement} targetEl    The position reference element
21998          * @static
21999          */
22000         moveToEl: function (moveEl, targetEl) {
22001             var aCoord = Roo.lib.Dom.getXY(targetEl);
22002             Roo.lib.Dom.setXY(moveEl, aCoord);
22003         },
22004
22005         /**
22006          * Numeric array sort function
22007          * @method numericSort
22008          * @static
22009          */
22010         numericSort: function(a, b) { return (a - b); },
22011
22012         /**
22013          * Internal counter
22014          * @property _timeoutCount
22015          * @private
22016          * @static
22017          */
22018         _timeoutCount: 0,
22019
22020         /**
22021          * Trying to make the load order less important.  Without this we get
22022          * an error if this file is loaded before the Event Utility.
22023          * @method _addListeners
22024          * @private
22025          * @static
22026          */
22027         _addListeners: function() {
22028             var DDM = Roo.dd.DDM;
22029             if ( Roo.lib.Event && document ) {
22030                 DDM._onLoad();
22031             } else {
22032                 if (DDM._timeoutCount > 2000) {
22033                 } else {
22034                     setTimeout(DDM._addListeners, 10);
22035                     if (document && document.body) {
22036                         DDM._timeoutCount += 1;
22037                     }
22038                 }
22039             }
22040         },
22041
22042         /**
22043          * Recursively searches the immediate parent and all child nodes for
22044          * the handle element in order to determine wheter or not it was
22045          * clicked.
22046          * @method handleWasClicked
22047          * @param node the html element to inspect
22048          * @static
22049          */
22050         handleWasClicked: function(node, id) {
22051             if (this.isHandle(id, node.id)) {
22052                 return true;
22053             } else {
22054                 // check to see if this is a text node child of the one we want
22055                 var p = node.parentNode;
22056
22057                 while (p) {
22058                     if (this.isHandle(id, p.id)) {
22059                         return true;
22060                     } else {
22061                         p = p.parentNode;
22062                     }
22063                 }
22064             }
22065
22066             return false;
22067         }
22068
22069     };
22070
22071 }();
22072
22073 // shorter alias, save a few bytes
22074 Roo.dd.DDM = Roo.dd.DragDropMgr;
22075 Roo.dd.DDM._addListeners();
22076
22077 }/*
22078  * Based on:
22079  * Ext JS Library 1.1.1
22080  * Copyright(c) 2006-2007, Ext JS, LLC.
22081  *
22082  * Originally Released Under LGPL - original licence link has changed is not relivant.
22083  *
22084  * Fork - LGPL
22085  * <script type="text/javascript">
22086  */
22087
22088 /**
22089  * @class Roo.dd.DD
22090  * A DragDrop implementation where the linked element follows the
22091  * mouse cursor during a drag.
22092  * @extends Roo.dd.DragDrop
22093  * @constructor
22094  * @param {String} id the id of the linked element
22095  * @param {String} sGroup the group of related DragDrop items
22096  * @param {object} config an object containing configurable attributes
22097  *                Valid properties for DD:
22098  *                    scroll
22099  */
22100 Roo.dd.DD = function(id, sGroup, config) {
22101     if (id) {
22102         this.init(id, sGroup, config);
22103     }
22104 };
22105
22106 Roo.extend(Roo.dd.DD, Roo.dd.DragDrop, {
22107
22108     /**
22109      * When set to true, the utility automatically tries to scroll the browser
22110      * window wehn a drag and drop element is dragged near the viewport boundary.
22111      * Defaults to true.
22112      * @property scroll
22113      * @type boolean
22114      */
22115     scroll: true,
22116
22117     /**
22118      * Sets the pointer offset to the distance between the linked element's top
22119      * left corner and the location the element was clicked
22120      * @method autoOffset
22121      * @param {int} iPageX the X coordinate of the click
22122      * @param {int} iPageY the Y coordinate of the click
22123      */
22124     autoOffset: function(iPageX, iPageY) {
22125         var x = iPageX - this.startPageX;
22126         var y = iPageY - this.startPageY;
22127         this.setDelta(x, y);
22128     },
22129
22130     /**
22131      * Sets the pointer offset.  You can call this directly to force the
22132      * offset to be in a particular location (e.g., pass in 0,0 to set it
22133      * to the center of the object)
22134      * @method setDelta
22135      * @param {int} iDeltaX the distance from the left
22136      * @param {int} iDeltaY the distance from the top
22137      */
22138     setDelta: function(iDeltaX, iDeltaY) {
22139         this.deltaX = iDeltaX;
22140         this.deltaY = iDeltaY;
22141     },
22142
22143     /**
22144      * Sets the drag element to the location of the mousedown or click event,
22145      * maintaining the cursor location relative to the location on the element
22146      * that was clicked.  Override this if you want to place the element in a
22147      * location other than where the cursor is.
22148      * @method setDragElPos
22149      * @param {int} iPageX the X coordinate of the mousedown or drag event
22150      * @param {int} iPageY the Y coordinate of the mousedown or drag event
22151      */
22152     setDragElPos: function(iPageX, iPageY) {
22153         // the first time we do this, we are going to check to make sure
22154         // the element has css positioning
22155
22156         var el = this.getDragEl();
22157         this.alignElWithMouse(el, iPageX, iPageY);
22158     },
22159
22160     /**
22161      * Sets the element to the location of the mousedown or click event,
22162      * maintaining the cursor location relative to the location on the element
22163      * that was clicked.  Override this if you want to place the element in a
22164      * location other than where the cursor is.
22165      * @method alignElWithMouse
22166      * @param {HTMLElement} el the element to move
22167      * @param {int} iPageX the X coordinate of the mousedown or drag event
22168      * @param {int} iPageY the Y coordinate of the mousedown or drag event
22169      */
22170     alignElWithMouse: function(el, iPageX, iPageY) {
22171         var oCoord = this.getTargetCoord(iPageX, iPageY);
22172         var fly = el.dom ? el : Roo.fly(el);
22173         if (!this.deltaSetXY) {
22174             var aCoord = [oCoord.x, oCoord.y];
22175             fly.setXY(aCoord);
22176             var newLeft = fly.getLeft(true);
22177             var newTop  = fly.getTop(true);
22178             this.deltaSetXY = [ newLeft - oCoord.x, newTop - oCoord.y ];
22179         } else {
22180             fly.setLeftTop(oCoord.x + this.deltaSetXY[0], oCoord.y + this.deltaSetXY[1]);
22181         }
22182
22183         this.cachePosition(oCoord.x, oCoord.y);
22184         this.autoScroll(oCoord.x, oCoord.y, el.offsetHeight, el.offsetWidth);
22185         return oCoord;
22186     },
22187
22188     /**
22189      * Saves the most recent position so that we can reset the constraints and
22190      * tick marks on-demand.  We need to know this so that we can calculate the
22191      * number of pixels the element is offset from its original position.
22192      * @method cachePosition
22193      * @param iPageX the current x position (optional, this just makes it so we
22194      * don't have to look it up again)
22195      * @param iPageY the current y position (optional, this just makes it so we
22196      * don't have to look it up again)
22197      */
22198     cachePosition: function(iPageX, iPageY) {
22199         if (iPageX) {
22200             this.lastPageX = iPageX;
22201             this.lastPageY = iPageY;
22202         } else {
22203             var aCoord = Roo.lib.Dom.getXY(this.getEl());
22204             this.lastPageX = aCoord[0];
22205             this.lastPageY = aCoord[1];
22206         }
22207     },
22208
22209     /**
22210      * Auto-scroll the window if the dragged object has been moved beyond the
22211      * visible window boundary.
22212      * @method autoScroll
22213      * @param {int} x the drag element's x position
22214      * @param {int} y the drag element's y position
22215      * @param {int} h the height of the drag element
22216      * @param {int} w the width of the drag element
22217      * @private
22218      */
22219     autoScroll: function(x, y, h, w) {
22220
22221         if (this.scroll) {
22222             // The client height
22223             var clientH = Roo.lib.Dom.getViewWidth();
22224
22225             // The client width
22226             var clientW = Roo.lib.Dom.getViewHeight();
22227
22228             // The amt scrolled down
22229             var st = this.DDM.getScrollTop();
22230
22231             // The amt scrolled right
22232             var sl = this.DDM.getScrollLeft();
22233
22234             // Location of the bottom of the element
22235             var bot = h + y;
22236
22237             // Location of the right of the element
22238             var right = w + x;
22239
22240             // The distance from the cursor to the bottom of the visible area,
22241             // adjusted so that we don't scroll if the cursor is beyond the
22242             // element drag constraints
22243             var toBot = (clientH + st - y - this.deltaY);
22244
22245             // The distance from the cursor to the right of the visible area
22246             var toRight = (clientW + sl - x - this.deltaX);
22247
22248
22249             // How close to the edge the cursor must be before we scroll
22250             // var thresh = (document.all) ? 100 : 40;
22251             var thresh = 40;
22252
22253             // How many pixels to scroll per autoscroll op.  This helps to reduce
22254             // clunky scrolling. IE is more sensitive about this ... it needs this
22255             // value to be higher.
22256             var scrAmt = (document.all) ? 80 : 30;
22257
22258             // Scroll down if we are near the bottom of the visible page and the
22259             // obj extends below the crease
22260             if ( bot > clientH && toBot < thresh ) {
22261                 window.scrollTo(sl, st + scrAmt);
22262             }
22263
22264             // Scroll up if the window is scrolled down and the top of the object
22265             // goes above the top border
22266             if ( y < st && st > 0 && y - st < thresh ) {
22267                 window.scrollTo(sl, st - scrAmt);
22268             }
22269
22270             // Scroll right if the obj is beyond the right border and the cursor is
22271             // near the border.
22272             if ( right > clientW && toRight < thresh ) {
22273                 window.scrollTo(sl + scrAmt, st);
22274             }
22275
22276             // Scroll left if the window has been scrolled to the right and the obj
22277             // extends past the left border
22278             if ( x < sl && sl > 0 && x - sl < thresh ) {
22279                 window.scrollTo(sl - scrAmt, st);
22280             }
22281         }
22282     },
22283
22284     /**
22285      * Finds the location the element should be placed if we want to move
22286      * it to where the mouse location less the click offset would place us.
22287      * @method getTargetCoord
22288      * @param {int} iPageX the X coordinate of the click
22289      * @param {int} iPageY the Y coordinate of the click
22290      * @return an object that contains the coordinates (Object.x and Object.y)
22291      * @private
22292      */
22293     getTargetCoord: function(iPageX, iPageY) {
22294
22295
22296         var x = iPageX - this.deltaX;
22297         var y = iPageY - this.deltaY;
22298
22299         if (this.constrainX) {
22300             if (x < this.minX) { x = this.minX; }
22301             if (x > this.maxX) { x = this.maxX; }
22302         }
22303
22304         if (this.constrainY) {
22305             if (y < this.minY) { y = this.minY; }
22306             if (y > this.maxY) { y = this.maxY; }
22307         }
22308
22309         x = this.getTick(x, this.xTicks);
22310         y = this.getTick(y, this.yTicks);
22311
22312
22313         return {x:x, y:y};
22314     },
22315
22316     /*
22317      * Sets up config options specific to this class. Overrides
22318      * Roo.dd.DragDrop, but all versions of this method through the
22319      * inheritance chain are called
22320      */
22321     applyConfig: function() {
22322         Roo.dd.DD.superclass.applyConfig.call(this);
22323         this.scroll = (this.config.scroll !== false);
22324     },
22325
22326     /*
22327      * Event that fires prior to the onMouseDown event.  Overrides
22328      * Roo.dd.DragDrop.
22329      */
22330     b4MouseDown: function(e) {
22331         // this.resetConstraints();
22332         this.autoOffset(e.getPageX(),
22333                             e.getPageY());
22334     },
22335
22336     /*
22337      * Event that fires prior to the onDrag event.  Overrides
22338      * Roo.dd.DragDrop.
22339      */
22340     b4Drag: function(e) {
22341         this.setDragElPos(e.getPageX(),
22342                             e.getPageY());
22343     },
22344
22345     toString: function() {
22346         return ("DD " + this.id);
22347     }
22348
22349     //////////////////////////////////////////////////////////////////////////
22350     // Debugging ygDragDrop events that can be overridden
22351     //////////////////////////////////////////////////////////////////////////
22352     /*
22353     startDrag: function(x, y) {
22354     },
22355
22356     onDrag: function(e) {
22357     },
22358
22359     onDragEnter: function(e, id) {
22360     },
22361
22362     onDragOver: function(e, id) {
22363     },
22364
22365     onDragOut: function(e, id) {
22366     },
22367
22368     onDragDrop: function(e, id) {
22369     },
22370
22371     endDrag: function(e) {
22372     }
22373
22374     */
22375
22376 });/*
22377  * Based on:
22378  * Ext JS Library 1.1.1
22379  * Copyright(c) 2006-2007, Ext JS, LLC.
22380  *
22381  * Originally Released Under LGPL - original licence link has changed is not relivant.
22382  *
22383  * Fork - LGPL
22384  * <script type="text/javascript">
22385  */
22386
22387 /**
22388  * @class Roo.dd.DDProxy
22389  * A DragDrop implementation that inserts an empty, bordered div into
22390  * the document that follows the cursor during drag operations.  At the time of
22391  * the click, the frame div is resized to the dimensions of the linked html
22392  * element, and moved to the exact location of the linked element.
22393  *
22394  * References to the "frame" element refer to the single proxy element that
22395  * was created to be dragged in place of all DDProxy elements on the
22396  * page.
22397  *
22398  * @extends Roo.dd.DD
22399  * @constructor
22400  * @param {String} id the id of the linked html element
22401  * @param {String} sGroup the group of related DragDrop objects
22402  * @param {object} config an object containing configurable attributes
22403  *                Valid properties for DDProxy in addition to those in DragDrop:
22404  *                   resizeFrame, centerFrame, dragElId
22405  */
22406 Roo.dd.DDProxy = function(id, sGroup, config) {
22407     if (id) {
22408         this.init(id, sGroup, config);
22409         this.initFrame();
22410     }
22411 };
22412
22413 /**
22414  * The default drag frame div id
22415  * @property Roo.dd.DDProxy.dragElId
22416  * @type String
22417  * @static
22418  */
22419 Roo.dd.DDProxy.dragElId = "ygddfdiv";
22420
22421 Roo.extend(Roo.dd.DDProxy, Roo.dd.DD, {
22422
22423     /**
22424      * By default we resize the drag frame to be the same size as the element
22425      * we want to drag (this is to get the frame effect).  We can turn it off
22426      * if we want a different behavior.
22427      * @property resizeFrame
22428      * @type boolean
22429      */
22430     resizeFrame: true,
22431
22432     /**
22433      * By default the frame is positioned exactly where the drag element is, so
22434      * we use the cursor offset provided by Roo.dd.DD.  Another option that works only if
22435      * you do not have constraints on the obj is to have the drag frame centered
22436      * around the cursor.  Set centerFrame to true for this effect.
22437      * @property centerFrame
22438      * @type boolean
22439      */
22440     centerFrame: false,
22441
22442     /**
22443      * Creates the proxy element if it does not yet exist
22444      * @method createFrame
22445      */
22446     createFrame: function() {
22447         var self = this;
22448         var body = document.body;
22449
22450         if (!body || !body.firstChild) {
22451             setTimeout( function() { self.createFrame(); }, 50 );
22452             return;
22453         }
22454
22455         var div = this.getDragEl();
22456
22457         if (!div) {
22458             div    = document.createElement("div");
22459             div.id = this.dragElId;
22460             var s  = div.style;
22461
22462             s.position   = "absolute";
22463             s.visibility = "hidden";
22464             s.cursor     = "move";
22465             s.border     = "2px solid #aaa";
22466             s.zIndex     = 999;
22467
22468             // appendChild can blow up IE if invoked prior to the window load event
22469             // while rendering a table.  It is possible there are other scenarios
22470             // that would cause this to happen as well.
22471             body.insertBefore(div, body.firstChild);
22472         }
22473     },
22474
22475     /**
22476      * Initialization for the drag frame element.  Must be called in the
22477      * constructor of all subclasses
22478      * @method initFrame
22479      */
22480     initFrame: function() {
22481         this.createFrame();
22482     },
22483
22484     applyConfig: function() {
22485         Roo.dd.DDProxy.superclass.applyConfig.call(this);
22486
22487         this.resizeFrame = (this.config.resizeFrame !== false);
22488         this.centerFrame = (this.config.centerFrame);
22489         this.setDragElId(this.config.dragElId || Roo.dd.DDProxy.dragElId);
22490     },
22491
22492     /**
22493      * Resizes the drag frame to the dimensions of the clicked object, positions
22494      * it over the object, and finally displays it
22495      * @method showFrame
22496      * @param {int} iPageX X click position
22497      * @param {int} iPageY Y click position
22498      * @private
22499      */
22500     showFrame: function(iPageX, iPageY) {
22501         var el = this.getEl();
22502         var dragEl = this.getDragEl();
22503         var s = dragEl.style;
22504
22505         this._resizeProxy();
22506
22507         if (this.centerFrame) {
22508             this.setDelta( Math.round(parseInt(s.width,  10)/2),
22509                            Math.round(parseInt(s.height, 10)/2) );
22510         }
22511
22512         this.setDragElPos(iPageX, iPageY);
22513
22514         Roo.fly(dragEl).show();
22515     },
22516
22517     /**
22518      * The proxy is automatically resized to the dimensions of the linked
22519      * element when a drag is initiated, unless resizeFrame is set to false
22520      * @method _resizeProxy
22521      * @private
22522      */
22523     _resizeProxy: function() {
22524         if (this.resizeFrame) {
22525             var el = this.getEl();
22526             Roo.fly(this.getDragEl()).setSize(el.offsetWidth, el.offsetHeight);
22527         }
22528     },
22529
22530     // overrides Roo.dd.DragDrop
22531     b4MouseDown: function(e) {
22532         var x = e.getPageX();
22533         var y = e.getPageY();
22534         this.autoOffset(x, y);
22535         this.setDragElPos(x, y);
22536     },
22537
22538     // overrides Roo.dd.DragDrop
22539     b4StartDrag: function(x, y) {
22540         // show the drag frame
22541         this.showFrame(x, y);
22542     },
22543
22544     // overrides Roo.dd.DragDrop
22545     b4EndDrag: function(e) {
22546         Roo.fly(this.getDragEl()).hide();
22547     },
22548
22549     // overrides Roo.dd.DragDrop
22550     // By default we try to move the element to the last location of the frame.
22551     // This is so that the default behavior mirrors that of Roo.dd.DD.
22552     endDrag: function(e) {
22553
22554         var lel = this.getEl();
22555         var del = this.getDragEl();
22556
22557         // Show the drag frame briefly so we can get its position
22558         del.style.visibility = "";
22559
22560         this.beforeMove();
22561         // Hide the linked element before the move to get around a Safari
22562         // rendering bug.
22563         lel.style.visibility = "hidden";
22564         Roo.dd.DDM.moveToEl(lel, del);
22565         del.style.visibility = "hidden";
22566         lel.style.visibility = "";
22567
22568         this.afterDrag();
22569     },
22570
22571     beforeMove : function(){
22572
22573     },
22574
22575     afterDrag : function(){
22576
22577     },
22578
22579     toString: function() {
22580         return ("DDProxy " + this.id);
22581     }
22582
22583 });
22584 /*
22585  * Based on:
22586  * Ext JS Library 1.1.1
22587  * Copyright(c) 2006-2007, Ext JS, LLC.
22588  *
22589  * Originally Released Under LGPL - original licence link has changed is not relivant.
22590  *
22591  * Fork - LGPL
22592  * <script type="text/javascript">
22593  */
22594
22595  /**
22596  * @class Roo.dd.DDTarget
22597  * A DragDrop implementation that does not move, but can be a drop
22598  * target.  You would get the same result by simply omitting implementation
22599  * for the event callbacks, but this way we reduce the processing cost of the
22600  * event listener and the callbacks.
22601  * @extends Roo.dd.DragDrop
22602  * @constructor
22603  * @param {String} id the id of the element that is a drop target
22604  * @param {String} sGroup the group of related DragDrop objects
22605  * @param {object} config an object containing configurable attributes
22606  *                 Valid properties for DDTarget in addition to those in
22607  *                 DragDrop:
22608  *                    none
22609  */
22610 Roo.dd.DDTarget = function(id, sGroup, config) {
22611     if (id) {
22612         this.initTarget(id, sGroup, config);
22613     }
22614     if (config && (config.listeners || config.events)) { 
22615         Roo.dd.DragDrop.superclass.constructor.call(this,  { 
22616             listeners : config.listeners || {}, 
22617             events : config.events || {} 
22618         });    
22619     }
22620 };
22621
22622 // Roo.dd.DDTarget.prototype = new Roo.dd.DragDrop();
22623 Roo.extend(Roo.dd.DDTarget, Roo.dd.DragDrop, {
22624     toString: function() {
22625         return ("DDTarget " + this.id);
22626     }
22627 });
22628 /*
22629  * Based on:
22630  * Ext JS Library 1.1.1
22631  * Copyright(c) 2006-2007, Ext JS, LLC.
22632  *
22633  * Originally Released Under LGPL - original licence link has changed is not relivant.
22634  *
22635  * Fork - LGPL
22636  * <script type="text/javascript">
22637  */
22638  
22639
22640 /**
22641  * @class Roo.dd.ScrollManager
22642  * Provides automatic scrolling of overflow regions in the page during drag operations.<br><br>
22643  * <b>Note: This class uses "Point Mode" and is untested in "Intersect Mode".</b>
22644  * @static
22645  */
22646 Roo.dd.ScrollManager = function(){
22647     var ddm = Roo.dd.DragDropMgr;
22648     var els = {};
22649     var dragEl = null;
22650     var proc = {};
22651     
22652     
22653     
22654     var onStop = function(e){
22655         dragEl = null;
22656         clearProc();
22657     };
22658     
22659     var triggerRefresh = function(){
22660         if(ddm.dragCurrent){
22661              ddm.refreshCache(ddm.dragCurrent.groups);
22662         }
22663     };
22664     
22665     var doScroll = function(){
22666         if(ddm.dragCurrent){
22667             var dds = Roo.dd.ScrollManager;
22668             if(!dds.animate){
22669                 if(proc.el.scroll(proc.dir, dds.increment)){
22670                     triggerRefresh();
22671                 }
22672             }else{
22673                 proc.el.scroll(proc.dir, dds.increment, true, dds.animDuration, triggerRefresh);
22674             }
22675         }
22676     };
22677     
22678     var clearProc = function(){
22679         if(proc.id){
22680             clearInterval(proc.id);
22681         }
22682         proc.id = 0;
22683         proc.el = null;
22684         proc.dir = "";
22685     };
22686     
22687     var startProc = function(el, dir){
22688          Roo.log('scroll startproc');
22689         clearProc();
22690         proc.el = el;
22691         proc.dir = dir;
22692         proc.id = setInterval(doScroll, Roo.dd.ScrollManager.frequency);
22693     };
22694     
22695     var onFire = function(e, isDrop){
22696        
22697         if(isDrop || !ddm.dragCurrent){ return; }
22698         var dds = Roo.dd.ScrollManager;
22699         if(!dragEl || dragEl != ddm.dragCurrent){
22700             dragEl = ddm.dragCurrent;
22701             // refresh regions on drag start
22702             dds.refreshCache();
22703         }
22704         
22705         var xy = Roo.lib.Event.getXY(e);
22706         var pt = new Roo.lib.Point(xy[0], xy[1]);
22707         for(var id in els){
22708             var el = els[id], r = el._region;
22709             if(r && r.contains(pt) && el.isScrollable()){
22710                 if(r.bottom - pt.y <= dds.thresh){
22711                     if(proc.el != el){
22712                         startProc(el, "down");
22713                     }
22714                     return;
22715                 }else if(r.right - pt.x <= dds.thresh){
22716                     if(proc.el != el){
22717                         startProc(el, "left");
22718                     }
22719                     return;
22720                 }else if(pt.y - r.top <= dds.thresh){
22721                     if(proc.el != el){
22722                         startProc(el, "up");
22723                     }
22724                     return;
22725                 }else if(pt.x - r.left <= dds.thresh){
22726                     if(proc.el != el){
22727                         startProc(el, "right");
22728                     }
22729                     return;
22730                 }
22731             }
22732         }
22733         clearProc();
22734     };
22735     
22736     ddm.fireEvents = ddm.fireEvents.createSequence(onFire, ddm);
22737     ddm.stopDrag = ddm.stopDrag.createSequence(onStop, ddm);
22738     
22739     return {
22740         /**
22741          * Registers new overflow element(s) to auto scroll
22742          * @param {String/HTMLElement/Element/Array} el The id of or the element to be scrolled or an array of either
22743          */
22744         register : function(el){
22745             if(el instanceof Array){
22746                 for(var i = 0, len = el.length; i < len; i++) {
22747                         this.register(el[i]);
22748                 }
22749             }else{
22750                 el = Roo.get(el);
22751                 els[el.id] = el;
22752             }
22753             Roo.dd.ScrollManager.els = els;
22754         },
22755         
22756         /**
22757          * Unregisters overflow element(s) so they are no longer scrolled
22758          * @param {String/HTMLElement/Element/Array} el The id of or the element to be removed or an array of either
22759          */
22760         unregister : function(el){
22761             if(el instanceof Array){
22762                 for(var i = 0, len = el.length; i < len; i++) {
22763                         this.unregister(el[i]);
22764                 }
22765             }else{
22766                 el = Roo.get(el);
22767                 delete els[el.id];
22768             }
22769         },
22770         
22771         /**
22772          * The number of pixels from the edge of a container the pointer needs to be to 
22773          * trigger scrolling (defaults to 25)
22774          * @type Number
22775          */
22776         thresh : 25,
22777         
22778         /**
22779          * The number of pixels to scroll in each scroll increment (defaults to 50)
22780          * @type Number
22781          */
22782         increment : 100,
22783         
22784         /**
22785          * The frequency of scrolls in milliseconds (defaults to 500)
22786          * @type Number
22787          */
22788         frequency : 500,
22789         
22790         /**
22791          * True to animate the scroll (defaults to true)
22792          * @type Boolean
22793          */
22794         animate: true,
22795         
22796         /**
22797          * The animation duration in seconds - 
22798          * MUST BE less than Roo.dd.ScrollManager.frequency! (defaults to .4)
22799          * @type Number
22800          */
22801         animDuration: .4,
22802         
22803         /**
22804          * Manually trigger a cache refresh.
22805          */
22806         refreshCache : function(){
22807             for(var id in els){
22808                 if(typeof els[id] == 'object'){ // for people extending the object prototype
22809                     els[id]._region = els[id].getRegion();
22810                 }
22811             }
22812         }
22813     };
22814 }();/*
22815  * Based on:
22816  * Ext JS Library 1.1.1
22817  * Copyright(c) 2006-2007, Ext JS, LLC.
22818  *
22819  * Originally Released Under LGPL - original licence link has changed is not relivant.
22820  *
22821  * Fork - LGPL
22822  * <script type="text/javascript">
22823  */
22824  
22825
22826 /**
22827  * @class Roo.dd.Registry
22828  * Provides easy access to all drag drop components that are registered on a page.  Items can be retrieved either
22829  * directly by DOM node id, or by passing in the drag drop event that occurred and looking up the event target.
22830  * @static
22831  */
22832 Roo.dd.Registry = function(){
22833     var elements = {}; 
22834     var handles = {}; 
22835     var autoIdSeed = 0;
22836
22837     var getId = function(el, autogen){
22838         if(typeof el == "string"){
22839             return el;
22840         }
22841         var id = el.id;
22842         if(!id && autogen !== false){
22843             id = "roodd-" + (++autoIdSeed);
22844             el.id = id;
22845         }
22846         return id;
22847     };
22848     
22849     return {
22850     /**
22851      * Register a drag drop element
22852      * @param {String|HTMLElement} element The id or DOM node to register
22853      * @param {Object} data (optional) A custom data object that will be passed between the elements that are involved
22854      * in drag drop operations.  You can populate this object with any arbitrary properties that your own code
22855      * knows how to interpret, plus there are some specific properties known to the Registry that should be
22856      * populated in the data object (if applicable):
22857      * <pre>
22858 Value      Description<br />
22859 ---------  ------------------------------------------<br />
22860 handles    Array of DOM nodes that trigger dragging<br />
22861            for the element being registered<br />
22862 isHandle   True if the element passed in triggers<br />
22863            dragging itself, else false
22864 </pre>
22865      */
22866         register : function(el, data){
22867             data = data || {};
22868             if(typeof el == "string"){
22869                 el = document.getElementById(el);
22870             }
22871             data.ddel = el;
22872             elements[getId(el)] = data;
22873             if(data.isHandle !== false){
22874                 handles[data.ddel.id] = data;
22875             }
22876             if(data.handles){
22877                 var hs = data.handles;
22878                 for(var i = 0, len = hs.length; i < len; i++){
22879                         handles[getId(hs[i])] = data;
22880                 }
22881             }
22882         },
22883
22884     /**
22885      * Unregister a drag drop element
22886      * @param {String|HTMLElement}  element The id or DOM node to unregister
22887      */
22888         unregister : function(el){
22889             var id = getId(el, false);
22890             var data = elements[id];
22891             if(data){
22892                 delete elements[id];
22893                 if(data.handles){
22894                     var hs = data.handles;
22895                     for(var i = 0, len = hs.length; i < len; i++){
22896                         delete handles[getId(hs[i], false)];
22897                     }
22898                 }
22899             }
22900         },
22901
22902     /**
22903      * Returns the handle registered for a DOM Node by id
22904      * @param {String|HTMLElement} id The DOM node or id to look up
22905      * @return {Object} handle The custom handle data
22906      */
22907         getHandle : function(id){
22908             if(typeof id != "string"){ // must be element?
22909                 id = id.id;
22910             }
22911             return handles[id];
22912         },
22913
22914     /**
22915      * Returns the handle that is registered for the DOM node that is the target of the event
22916      * @param {Event} e The event
22917      * @return {Object} handle The custom handle data
22918      */
22919         getHandleFromEvent : function(e){
22920             var t = Roo.lib.Event.getTarget(e);
22921             return t ? handles[t.id] : null;
22922         },
22923
22924     /**
22925      * Returns a custom data object that is registered for a DOM node by id
22926      * @param {String|HTMLElement} id The DOM node or id to look up
22927      * @return {Object} data The custom data
22928      */
22929         getTarget : function(id){
22930             if(typeof id != "string"){ // must be element?
22931                 id = id.id;
22932             }
22933             return elements[id];
22934         },
22935
22936     /**
22937      * Returns a custom data object that is registered for the DOM node that is the target of the event
22938      * @param {Event} e The event
22939      * @return {Object} data The custom data
22940      */
22941         getTargetFromEvent : function(e){
22942             var t = Roo.lib.Event.getTarget(e);
22943             return t ? elements[t.id] || handles[t.id] : null;
22944         }
22945     };
22946 }();/*
22947  * Based on:
22948  * Ext JS Library 1.1.1
22949  * Copyright(c) 2006-2007, Ext JS, LLC.
22950  *
22951  * Originally Released Under LGPL - original licence link has changed is not relivant.
22952  *
22953  * Fork - LGPL
22954  * <script type="text/javascript">
22955  */
22956  
22957
22958 /**
22959  * @class Roo.dd.StatusProxy
22960  * A specialized drag proxy that supports a drop status icon, {@link Roo.Layer} styles and auto-repair.  This is the
22961  * default drag proxy used by all Roo.dd components.
22962  * @constructor
22963  * @param {Object} config
22964  */
22965 Roo.dd.StatusProxy = function(config){
22966     Roo.apply(this, config);
22967     this.id = this.id || Roo.id();
22968     this.el = new Roo.Layer({
22969         dh: {
22970             id: this.id, tag: "div", cls: "x-dd-drag-proxy "+this.dropNotAllowed, children: [
22971                 {tag: "div", cls: "x-dd-drop-icon"},
22972                 {tag: "div", cls: "x-dd-drag-ghost"}
22973             ]
22974         }, 
22975         shadow: !config || config.shadow !== false
22976     });
22977     this.ghost = Roo.get(this.el.dom.childNodes[1]);
22978     this.dropStatus = this.dropNotAllowed;
22979 };
22980
22981 Roo.dd.StatusProxy.prototype = {
22982     /**
22983      * @cfg {String} dropAllowed
22984      * The CSS class to apply to the status element when drop is allowed (defaults to "x-dd-drop-ok").
22985      */
22986     dropAllowed : "x-dd-drop-ok",
22987     /**
22988      * @cfg {String} dropNotAllowed
22989      * The CSS class to apply to the status element when drop is not allowed (defaults to "x-dd-drop-nodrop").
22990      */
22991     dropNotAllowed : "x-dd-drop-nodrop",
22992
22993     /**
22994      * Updates the proxy's visual element to indicate the status of whether or not drop is allowed
22995      * over the current target element.
22996      * @param {String} cssClass The css class for the new drop status indicator image
22997      */
22998     setStatus : function(cssClass){
22999         cssClass = cssClass || this.dropNotAllowed;
23000         if(this.dropStatus != cssClass){
23001             this.el.replaceClass(this.dropStatus, cssClass);
23002             this.dropStatus = cssClass;
23003         }
23004     },
23005
23006     /**
23007      * Resets the status indicator to the default dropNotAllowed value
23008      * @param {Boolean} clearGhost True to also remove all content from the ghost, false to preserve it
23009      */
23010     reset : function(clearGhost){
23011         this.el.dom.className = "x-dd-drag-proxy " + this.dropNotAllowed;
23012         this.dropStatus = this.dropNotAllowed;
23013         if(clearGhost){
23014             this.ghost.update("");
23015         }
23016     },
23017
23018     /**
23019      * Updates the contents of the ghost element
23020      * @param {String} html The html that will replace the current innerHTML of the ghost element
23021      */
23022     update : function(html){
23023         if(typeof html == "string"){
23024             this.ghost.update(html);
23025         }else{
23026             this.ghost.update("");
23027             html.style.margin = "0";
23028             this.ghost.dom.appendChild(html);
23029         }
23030         // ensure float = none set?? cant remember why though.
23031         var el = this.ghost.dom.firstChild;
23032                 if(el){
23033                         Roo.fly(el).setStyle('float', 'none');
23034                 }
23035     },
23036     
23037     /**
23038      * Returns the underlying proxy {@link Roo.Layer}
23039      * @return {Roo.Layer} el
23040     */
23041     getEl : function(){
23042         return this.el;
23043     },
23044
23045     /**
23046      * Returns the ghost element
23047      * @return {Roo.Element} el
23048      */
23049     getGhost : function(){
23050         return this.ghost;
23051     },
23052
23053     /**
23054      * Hides the proxy
23055      * @param {Boolean} clear True to reset the status and clear the ghost contents, false to preserve them
23056      */
23057     hide : function(clear){
23058         this.el.hide();
23059         if(clear){
23060             this.reset(true);
23061         }
23062     },
23063
23064     /**
23065      * Stops the repair animation if it's currently running
23066      */
23067     stop : function(){
23068         if(this.anim && this.anim.isAnimated && this.anim.isAnimated()){
23069             this.anim.stop();
23070         }
23071     },
23072
23073     /**
23074      * Displays this proxy
23075      */
23076     show : function(){
23077         this.el.show();
23078     },
23079
23080     /**
23081      * Force the Layer to sync its shadow and shim positions to the element
23082      */
23083     sync : function(){
23084         this.el.sync();
23085     },
23086
23087     /**
23088      * Causes the proxy to return to its position of origin via an animation.  Should be called after an
23089      * invalid drop operation by the item being dragged.
23090      * @param {Array} xy The XY position of the element ([x, y])
23091      * @param {Function} callback The function to call after the repair is complete
23092      * @param {Object} scope The scope in which to execute the callback
23093      */
23094     repair : function(xy, callback, scope){
23095         this.callback = callback;
23096         this.scope = scope;
23097         if(xy && this.animRepair !== false){
23098             this.el.addClass("x-dd-drag-repair");
23099             this.el.hideUnders(true);
23100             this.anim = this.el.shift({
23101                 duration: this.repairDuration || .5,
23102                 easing: 'easeOut',
23103                 xy: xy,
23104                 stopFx: true,
23105                 callback: this.afterRepair,
23106                 scope: this
23107             });
23108         }else{
23109             this.afterRepair();
23110         }
23111     },
23112
23113     // private
23114     afterRepair : function(){
23115         this.hide(true);
23116         if(typeof this.callback == "function"){
23117             this.callback.call(this.scope || this);
23118         }
23119         this.callback = null;
23120         this.scope = null;
23121     }
23122 };/*
23123  * Based on:
23124  * Ext JS Library 1.1.1
23125  * Copyright(c) 2006-2007, Ext JS, LLC.
23126  *
23127  * Originally Released Under LGPL - original licence link has changed is not relivant.
23128  *
23129  * Fork - LGPL
23130  * <script type="text/javascript">
23131  */
23132
23133 /**
23134  * @class Roo.dd.DragSource
23135  * @extends Roo.dd.DDProxy
23136  * A simple class that provides the basic implementation needed to make any element draggable.
23137  * @constructor
23138  * @param {String/HTMLElement/Element} el The container element
23139  * @param {Object} config
23140  */
23141 Roo.dd.DragSource = function(el, config){
23142     this.el = Roo.get(el);
23143     this.dragData = {};
23144     
23145     Roo.apply(this, config);
23146     
23147     if(!this.proxy){
23148         this.proxy = new Roo.dd.StatusProxy();
23149     }
23150
23151     Roo.dd.DragSource.superclass.constructor.call(this, this.el.dom, this.ddGroup || this.group,
23152           {dragElId : this.proxy.id, resizeFrame: false, isTarget: false, scroll: this.scroll === true});
23153     
23154     this.dragging = false;
23155 };
23156
23157 Roo.extend(Roo.dd.DragSource, Roo.dd.DDProxy, {
23158     /**
23159      * @cfg {String} dropAllowed
23160      * The CSS class returned to the drag source when drop is allowed (defaults to "x-dd-drop-ok").
23161      */
23162     dropAllowed : "x-dd-drop-ok",
23163     /**
23164      * @cfg {String} dropNotAllowed
23165      * The CSS class returned to the drag source when drop is not allowed (defaults to "x-dd-drop-nodrop").
23166      */
23167     dropNotAllowed : "x-dd-drop-nodrop",
23168
23169     /**
23170      * Returns the data object associated with this drag source
23171      * @return {Object} data An object containing arbitrary data
23172      */
23173     getDragData : function(e){
23174         return this.dragData;
23175     },
23176
23177     // private
23178     onDragEnter : function(e, id){
23179         var target = Roo.dd.DragDropMgr.getDDById(id);
23180         this.cachedTarget = target;
23181         if(this.beforeDragEnter(target, e, id) !== false){
23182             if(target.isNotifyTarget){
23183                 var status = target.notifyEnter(this, e, this.dragData);
23184                 this.proxy.setStatus(status);
23185             }else{
23186                 this.proxy.setStatus(this.dropAllowed);
23187             }
23188             
23189             if(this.afterDragEnter){
23190                 /**
23191                  * An empty function by default, but provided so that you can perform a custom action
23192                  * when the dragged item enters the drop target by providing an implementation.
23193                  * @param {Roo.dd.DragDrop} target The drop target
23194                  * @param {Event} e The event object
23195                  * @param {String} id The id of the dragged element
23196                  * @method afterDragEnter
23197                  */
23198                 this.afterDragEnter(target, e, id);
23199             }
23200         }
23201     },
23202
23203     /**
23204      * An empty function by default, but provided so that you can perform a custom action
23205      * before the dragged item enters the drop target and optionally cancel the onDragEnter.
23206      * @param {Roo.dd.DragDrop} target The drop target
23207      * @param {Event} e The event object
23208      * @param {String} id The id of the dragged element
23209      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
23210      */
23211     beforeDragEnter : function(target, e, id){
23212         return true;
23213     },
23214
23215     // private
23216     alignElWithMouse: function() {
23217         Roo.dd.DragSource.superclass.alignElWithMouse.apply(this, arguments);
23218         this.proxy.sync();
23219     },
23220
23221     // private
23222     onDragOver : function(e, id){
23223         var target = this.cachedTarget || Roo.dd.DragDropMgr.getDDById(id);
23224         if(this.beforeDragOver(target, e, id) !== false){
23225             if(target.isNotifyTarget){
23226                 var status = target.notifyOver(this, e, this.dragData);
23227                 this.proxy.setStatus(status);
23228             }
23229
23230             if(this.afterDragOver){
23231                 /**
23232                  * An empty function by default, but provided so that you can perform a custom action
23233                  * while the dragged item is over the drop target by providing an implementation.
23234                  * @param {Roo.dd.DragDrop} target The drop target
23235                  * @param {Event} e The event object
23236                  * @param {String} id The id of the dragged element
23237                  * @method afterDragOver
23238                  */
23239                 this.afterDragOver(target, e, id);
23240             }
23241         }
23242     },
23243
23244     /**
23245      * An empty function by default, but provided so that you can perform a custom action
23246      * while the dragged item is over the drop target and optionally cancel the onDragOver.
23247      * @param {Roo.dd.DragDrop} target The drop target
23248      * @param {Event} e The event object
23249      * @param {String} id The id of the dragged element
23250      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
23251      */
23252     beforeDragOver : function(target, e, id){
23253         return true;
23254     },
23255
23256     // private
23257     onDragOut : function(e, id){
23258         var target = this.cachedTarget || Roo.dd.DragDropMgr.getDDById(id);
23259         if(this.beforeDragOut(target, e, id) !== false){
23260             if(target.isNotifyTarget){
23261                 target.notifyOut(this, e, this.dragData);
23262             }
23263             this.proxy.reset();
23264             if(this.afterDragOut){
23265                 /**
23266                  * An empty function by default, but provided so that you can perform a custom action
23267                  * after the dragged item is dragged out of the target without dropping.
23268                  * @param {Roo.dd.DragDrop} target The drop target
23269                  * @param {Event} e The event object
23270                  * @param {String} id The id of the dragged element
23271                  * @method afterDragOut
23272                  */
23273                 this.afterDragOut(target, e, id);
23274             }
23275         }
23276         this.cachedTarget = null;
23277     },
23278
23279     /**
23280      * An empty function by default, but provided so that you can perform a custom action before the dragged
23281      * item is dragged out of the target without dropping, and optionally cancel the onDragOut.
23282      * @param {Roo.dd.DragDrop} target The drop target
23283      * @param {Event} e The event object
23284      * @param {String} id The id of the dragged element
23285      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
23286      */
23287     beforeDragOut : function(target, e, id){
23288         return true;
23289     },
23290     
23291     // private
23292     onDragDrop : function(e, id){
23293         var target = this.cachedTarget || Roo.dd.DragDropMgr.getDDById(id);
23294         if(this.beforeDragDrop(target, e, id) !== false){
23295             if(target.isNotifyTarget){
23296                 if(target.notifyDrop(this, e, this.dragData)){ // valid drop?
23297                     this.onValidDrop(target, e, id);
23298                 }else{
23299                     this.onInvalidDrop(target, e, id);
23300                 }
23301             }else{
23302                 this.onValidDrop(target, e, id);
23303             }
23304             
23305             if(this.afterDragDrop){
23306                 /**
23307                  * An empty function by default, but provided so that you can perform a custom action
23308                  * after a valid drag drop has occurred by providing an implementation.
23309                  * @param {Roo.dd.DragDrop} target The drop target
23310                  * @param {Event} e The event object
23311                  * @param {String} id The id of the dropped element
23312                  * @method afterDragDrop
23313                  */
23314                 this.afterDragDrop(target, e, id);
23315             }
23316         }
23317         delete this.cachedTarget;
23318     },
23319
23320     /**
23321      * An empty function by default, but provided so that you can perform a custom action before the dragged
23322      * item is dropped onto the target and optionally cancel the onDragDrop.
23323      * @param {Roo.dd.DragDrop} target The drop target
23324      * @param {Event} e The event object
23325      * @param {String} id The id of the dragged element
23326      * @return {Boolean} isValid True if the drag drop event is valid, else false to cancel
23327      */
23328     beforeDragDrop : function(target, e, id){
23329         return true;
23330     },
23331
23332     // private
23333     onValidDrop : function(target, e, id){
23334         this.hideProxy();
23335         if(this.afterValidDrop){
23336             /**
23337              * An empty function by default, but provided so that you can perform a custom action
23338              * after a valid drop has occurred by providing an implementation.
23339              * @param {Object} target The target DD 
23340              * @param {Event} e The event object
23341              * @param {String} id The id of the dropped element
23342              * @method afterInvalidDrop
23343              */
23344             this.afterValidDrop(target, e, id);
23345         }
23346     },
23347
23348     // private
23349     getRepairXY : function(e, data){
23350         return this.el.getXY();  
23351     },
23352
23353     // private
23354     onInvalidDrop : function(target, e, id){
23355         this.beforeInvalidDrop(target, e, id);
23356         if(this.cachedTarget){
23357             if(this.cachedTarget.isNotifyTarget){
23358                 this.cachedTarget.notifyOut(this, e, this.dragData);
23359             }
23360             this.cacheTarget = null;
23361         }
23362         this.proxy.repair(this.getRepairXY(e, this.dragData), this.afterRepair, this);
23363
23364         if(this.afterInvalidDrop){
23365             /**
23366              * An empty function by default, but provided so that you can perform a custom action
23367              * after an invalid drop has occurred by providing an implementation.
23368              * @param {Event} e The event object
23369              * @param {String} id The id of the dropped element
23370              * @method afterInvalidDrop
23371              */
23372             this.afterInvalidDrop(e, id);
23373         }
23374     },
23375
23376     // private
23377     afterRepair : function(){
23378         if(Roo.enableFx){
23379             this.el.highlight(this.hlColor || "c3daf9");
23380         }
23381         this.dragging = false;
23382     },
23383
23384     /**
23385      * An empty function by default, but provided so that you can perform a custom action after an invalid
23386      * drop has occurred.
23387      * @param {Roo.dd.DragDrop} target The drop target
23388      * @param {Event} e The event object
23389      * @param {String} id The id of the dragged element
23390      * @return {Boolean} isValid True if the invalid drop should proceed, else false to cancel
23391      */
23392     beforeInvalidDrop : function(target, e, id){
23393         return true;
23394     },
23395
23396     // private
23397     handleMouseDown : function(e){
23398         if(this.dragging) {
23399             return;
23400         }
23401         var data = this.getDragData(e);
23402         if(data && this.onBeforeDrag(data, e) !== false){
23403             this.dragData = data;
23404             this.proxy.stop();
23405             Roo.dd.DragSource.superclass.handleMouseDown.apply(this, arguments);
23406         } 
23407     },
23408
23409     /**
23410      * An empty function by default, but provided so that you can perform a custom action before the initial
23411      * drag event begins and optionally cancel it.
23412      * @param {Object} data An object containing arbitrary data to be shared with drop targets
23413      * @param {Event} e The event object
23414      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
23415      */
23416     onBeforeDrag : function(data, e){
23417         return true;
23418     },
23419
23420     /**
23421      * An empty function by default, but provided so that you can perform a custom action once the initial
23422      * drag event has begun.  The drag cannot be canceled from this function.
23423      * @param {Number} x The x position of the click on the dragged object
23424      * @param {Number} y The y position of the click on the dragged object
23425      */
23426     onStartDrag : Roo.emptyFn,
23427
23428     // private - YUI override
23429     startDrag : function(x, y){
23430         this.proxy.reset();
23431         this.dragging = true;
23432         this.proxy.update("");
23433         this.onInitDrag(x, y);
23434         this.proxy.show();
23435     },
23436
23437     // private
23438     onInitDrag : function(x, y){
23439         var clone = this.el.dom.cloneNode(true);
23440         clone.id = Roo.id(); // prevent duplicate ids
23441         this.proxy.update(clone);
23442         this.onStartDrag(x, y);
23443         return true;
23444     },
23445
23446     /**
23447      * Returns the drag source's underlying {@link Roo.dd.StatusProxy}
23448      * @return {Roo.dd.StatusProxy} proxy The StatusProxy
23449      */
23450     getProxy : function(){
23451         return this.proxy;  
23452     },
23453
23454     /**
23455      * Hides the drag source's {@link Roo.dd.StatusProxy}
23456      */
23457     hideProxy : function(){
23458         this.proxy.hide();  
23459         this.proxy.reset(true);
23460         this.dragging = false;
23461     },
23462
23463     // private
23464     triggerCacheRefresh : function(){
23465         Roo.dd.DDM.refreshCache(this.groups);
23466     },
23467
23468     // private - override to prevent hiding
23469     b4EndDrag: function(e) {
23470     },
23471
23472     // private - override to prevent moving
23473     endDrag : function(e){
23474         this.onEndDrag(this.dragData, e);
23475     },
23476
23477     // private
23478     onEndDrag : function(data, e){
23479     },
23480     
23481     // private - pin to cursor
23482     autoOffset : function(x, y) {
23483         this.setDelta(-12, -20);
23484     }    
23485 });/*
23486  * Based on:
23487  * Ext JS Library 1.1.1
23488  * Copyright(c) 2006-2007, Ext JS, LLC.
23489  *
23490  * Originally Released Under LGPL - original licence link has changed is not relivant.
23491  *
23492  * Fork - LGPL
23493  * <script type="text/javascript">
23494  */
23495
23496
23497 /**
23498  * @class Roo.dd.DropTarget
23499  * @extends Roo.dd.DDTarget
23500  * A simple class that provides the basic implementation needed to make any element a drop target that can have
23501  * draggable items dropped onto it.  The drop has no effect until an implementation of notifyDrop is provided.
23502  * @constructor
23503  * @param {String/HTMLElement/Element} el The container element
23504  * @param {Object} config
23505  */
23506 Roo.dd.DropTarget = function(el, config){
23507     this.el = Roo.get(el);
23508     
23509     var listeners = false; ;
23510     if (config && config.listeners) {
23511         listeners= config.listeners;
23512         delete config.listeners;
23513     }
23514     Roo.apply(this, config);
23515     
23516     if(this.containerScroll){
23517         Roo.dd.ScrollManager.register(this.el);
23518     }
23519     this.addEvents( {
23520          /**
23521          * @scope Roo.dd.DropTarget
23522          */
23523          
23524          /**
23525          * @event enter
23526          * The function a {@link Roo.dd.DragSource} calls once to notify this drop target that the source is now over the
23527          * target.  This default implementation adds the CSS class specified by overClass (if any) to the drop element
23528          * and returns the dropAllowed config value.  This method should be overridden if drop validation is required.
23529          * 
23530          * IMPORTANT : it should set  this.valid to true|false
23531          * 
23532          * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
23533          * @param {Event} e The event
23534          * @param {Object} data An object containing arbitrary data supplied by the drag source
23535          */
23536         "enter" : true,
23537         
23538          /**
23539          * @event over
23540          * The function a {@link Roo.dd.DragSource} calls continuously while it is being dragged over the target.
23541          * This method will be called on every mouse movement while the drag source is over the drop target.
23542          * This default implementation simply returns the dropAllowed config value.
23543          * 
23544          * IMPORTANT : it should set  this.valid to true|false
23545          * 
23546          * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
23547          * @param {Event} e The event
23548          * @param {Object} data An object containing arbitrary data supplied by the drag source
23549          
23550          */
23551         "over" : true,
23552         /**
23553          * @event out
23554          * The function a {@link Roo.dd.DragSource} calls once to notify this drop target that the source has been dragged
23555          * out of the target without dropping.  This default implementation simply removes the CSS class specified by
23556          * overClass (if any) from the drop element.
23557          * 
23558          * 
23559          * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
23560          * @param {Event} e The event
23561          * @param {Object} data An object containing arbitrary data supplied by the drag source
23562          */
23563          "out" : true,
23564          
23565         /**
23566          * @event drop
23567          * The function a {@link Roo.dd.DragSource} calls once to notify this drop target that the dragged item has
23568          * been dropped on it.  This method has no default implementation and returns false, so you must provide an
23569          * implementation that does something to process the drop event and returns true so that the drag source's
23570          * repair action does not run.
23571          * 
23572          * IMPORTANT : it should set this.success
23573          * 
23574          * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
23575          * @param {Event} e The event
23576          * @param {Object} data An object containing arbitrary data supplied by the drag source
23577         */
23578          "drop" : true
23579     });
23580             
23581      
23582     Roo.dd.DropTarget.superclass.constructor.call(  this, 
23583         this.el.dom, 
23584         this.ddGroup || this.group,
23585         {
23586             isTarget: true,
23587             listeners : listeners || {} 
23588            
23589         
23590         }
23591     );
23592
23593 };
23594
23595 Roo.extend(Roo.dd.DropTarget, Roo.dd.DDTarget, {
23596     /**
23597      * @cfg {String} overClass
23598      * The CSS class applied to the drop target element while the drag source is over it (defaults to "").
23599      */
23600      /**
23601      * @cfg {String} ddGroup
23602      * The drag drop group to handle drop events for
23603      */
23604      
23605     /**
23606      * @cfg {String} dropAllowed
23607      * The CSS class returned to the drag source when drop is allowed (defaults to "x-dd-drop-ok").
23608      */
23609     dropAllowed : "x-dd-drop-ok",
23610     /**
23611      * @cfg {String} dropNotAllowed
23612      * The CSS class returned to the drag source when drop is not allowed (defaults to "x-dd-drop-nodrop").
23613      */
23614     dropNotAllowed : "x-dd-drop-nodrop",
23615     /**
23616      * @cfg {boolean} success
23617      * set this after drop listener.. 
23618      */
23619     success : false,
23620     /**
23621      * @cfg {boolean|String} valid true/false or string (ok-add/ok-sub/ok/nodrop)
23622      * if the drop point is valid for over/enter..
23623      */
23624     valid : false,
23625     // private
23626     isTarget : true,
23627
23628     // private
23629     isNotifyTarget : true,
23630     
23631     /**
23632      * @hide
23633      */
23634     notifyEnter : function(dd, e, data)
23635     {
23636         this.valid = true;
23637         this.fireEvent('enter', dd, e, data);
23638         if(this.overClass){
23639             this.el.addClass(this.overClass);
23640         }
23641         return typeof(this.valid) == 'string' ? 'x-dd-drop-' + this.valid : (
23642             this.valid ? this.dropAllowed : this.dropNotAllowed
23643         );
23644     },
23645
23646     /**
23647      * @hide
23648      */
23649     notifyOver : function(dd, e, data)
23650     {
23651         this.valid = true;
23652         this.fireEvent('over', dd, e, data);
23653         return typeof(this.valid) == 'string' ? 'x-dd-drop-' + this.valid : (
23654             this.valid ? this.dropAllowed : this.dropNotAllowed
23655         );
23656     },
23657
23658     /**
23659      * @hide
23660      */
23661     notifyOut : function(dd, e, data)
23662     {
23663         this.fireEvent('out', dd, e, data);
23664         if(this.overClass){
23665             this.el.removeClass(this.overClass);
23666         }
23667     },
23668
23669     /**
23670      * @hide
23671      */
23672     notifyDrop : function(dd, e, data)
23673     {
23674         this.success = false;
23675         this.fireEvent('drop', dd, e, data);
23676         return this.success;
23677     }
23678 });/*
23679  * Based on:
23680  * Ext JS Library 1.1.1
23681  * Copyright(c) 2006-2007, Ext JS, LLC.
23682  *
23683  * Originally Released Under LGPL - original licence link has changed is not relivant.
23684  *
23685  * Fork - LGPL
23686  * <script type="text/javascript">
23687  */
23688
23689
23690 /**
23691  * @class Roo.dd.DragZone
23692  * @extends Roo.dd.DragSource
23693  * This class provides a container DD instance that proxies for multiple child node sources.<br />
23694  * By default, this class requires that draggable child nodes are registered with {@link Roo.dd.Registry}.
23695  * @constructor
23696  * @param {String/HTMLElement/Element} el The container element
23697  * @param {Object} config
23698  */
23699 Roo.dd.DragZone = function(el, config){
23700     Roo.dd.DragZone.superclass.constructor.call(this, el, config);
23701     if(this.containerScroll){
23702         Roo.dd.ScrollManager.register(this.el);
23703     }
23704 };
23705
23706 Roo.extend(Roo.dd.DragZone, Roo.dd.DragSource, {
23707     /**
23708      * @cfg {Boolean} containerScroll True to register this container with the Scrollmanager
23709      * for auto scrolling during drag operations.
23710      */
23711     /**
23712      * @cfg {String} hlColor The color to use when visually highlighting the drag source in the afterRepair
23713      * method after a failed drop (defaults to "c3daf9" - light blue)
23714      */
23715
23716     /**
23717      * Called when a mousedown occurs in this container. Looks in {@link Roo.dd.Registry}
23718      * for a valid target to drag based on the mouse down. Override this method
23719      * to provide your own lookup logic (e.g. finding a child by class name). Make sure your returned
23720      * object has a "ddel" attribute (with an HTML Element) for other functions to work.
23721      * @param {EventObject} e The mouse down event
23722      * @return {Object} The dragData
23723      */
23724     getDragData : function(e){
23725         return Roo.dd.Registry.getHandleFromEvent(e);
23726     },
23727     
23728     /**
23729      * Called once drag threshold has been reached to initialize the proxy element. By default, it clones the
23730      * this.dragData.ddel
23731      * @param {Number} x The x position of the click on the dragged object
23732      * @param {Number} y The y position of the click on the dragged object
23733      * @return {Boolean} true to continue the drag, false to cancel
23734      */
23735     onInitDrag : function(x, y){
23736         this.proxy.update(this.dragData.ddel.cloneNode(true));
23737         this.onStartDrag(x, y);
23738         return true;
23739     },
23740     
23741     /**
23742      * Called after a repair of an invalid drop. By default, highlights this.dragData.ddel 
23743      */
23744     afterRepair : function(){
23745         if(Roo.enableFx){
23746             Roo.Element.fly(this.dragData.ddel).highlight(this.hlColor || "c3daf9");
23747         }
23748         this.dragging = false;
23749     },
23750
23751     /**
23752      * Called before a repair of an invalid drop to get the XY to animate to. By default returns
23753      * the XY of this.dragData.ddel
23754      * @param {EventObject} e The mouse up event
23755      * @return {Array} The xy location (e.g. [100, 200])
23756      */
23757     getRepairXY : function(e){
23758         return Roo.Element.fly(this.dragData.ddel).getXY();  
23759     }
23760 });/*
23761  * Based on:
23762  * Ext JS Library 1.1.1
23763  * Copyright(c) 2006-2007, Ext JS, LLC.
23764  *
23765  * Originally Released Under LGPL - original licence link has changed is not relivant.
23766  *
23767  * Fork - LGPL
23768  * <script type="text/javascript">
23769  */
23770 /**
23771  * @class Roo.dd.DropZone
23772  * @extends Roo.dd.DropTarget
23773  * This class provides a container DD instance that proxies for multiple child node targets.<br />
23774  * By default, this class requires that child nodes accepting drop are registered with {@link Roo.dd.Registry}.
23775  * @constructor
23776  * @param {String/HTMLElement/Element} el The container element
23777  * @param {Object} config
23778  */
23779 Roo.dd.DropZone = function(el, config){
23780     Roo.dd.DropZone.superclass.constructor.call(this, el, config);
23781 };
23782
23783 Roo.extend(Roo.dd.DropZone, Roo.dd.DropTarget, {
23784     /**
23785      * Returns a custom data object associated with the DOM node that is the target of the event.  By default
23786      * this looks up the event target in the {@link Roo.dd.Registry}, although you can override this method to
23787      * provide your own custom lookup.
23788      * @param {Event} e The event
23789      * @return {Object} data The custom data
23790      */
23791     getTargetFromEvent : function(e){
23792         return Roo.dd.Registry.getTargetFromEvent(e);
23793     },
23794
23795     /**
23796      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has entered a drop node
23797      * that it has registered.  This method has no default implementation and should be overridden to provide
23798      * node-specific processing if necessary.
23799      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from 
23800      * {@link #getTargetFromEvent} for this node)
23801      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
23802      * @param {Event} e The event
23803      * @param {Object} data An object containing arbitrary data supplied by the drag source
23804      */
23805     onNodeEnter : function(n, dd, e, data){
23806         
23807     },
23808
23809     /**
23810      * Called internally while the DropZone determines that a {@link Roo.dd.DragSource} is over a drop node
23811      * that it has registered.  The default implementation returns this.dropNotAllowed, so it should be
23812      * overridden to provide the proper feedback.
23813      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from
23814      * {@link #getTargetFromEvent} for this node)
23815      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
23816      * @param {Event} e The event
23817      * @param {Object} data An object containing arbitrary data supplied by the drag source
23818      * @return {String} status The CSS class that communicates the drop status back to the source so that the
23819      * underlying {@link Roo.dd.StatusProxy} can be updated
23820      */
23821     onNodeOver : function(n, dd, e, data){
23822         return this.dropAllowed;
23823     },
23824
23825     /**
23826      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has been dragged out of
23827      * the drop node without dropping.  This method has no default implementation and should be overridden to provide
23828      * node-specific processing if necessary.
23829      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from
23830      * {@link #getTargetFromEvent} for this node)
23831      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
23832      * @param {Event} e The event
23833      * @param {Object} data An object containing arbitrary data supplied by the drag source
23834      */
23835     onNodeOut : function(n, dd, e, data){
23836         
23837     },
23838
23839     /**
23840      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has been dropped onto
23841      * the drop node.  The default implementation returns false, so it should be overridden to provide the
23842      * appropriate processing of the drop event and return true so that the drag source's repair action does not run.
23843      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from
23844      * {@link #getTargetFromEvent} for this node)
23845      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
23846      * @param {Event} e The event
23847      * @param {Object} data An object containing arbitrary data supplied by the drag source
23848      * @return {Boolean} True if the drop was valid, else false
23849      */
23850     onNodeDrop : function(n, dd, e, data){
23851         return false;
23852     },
23853
23854     /**
23855      * Called internally while the DropZone determines that a {@link Roo.dd.DragSource} is being dragged over it,
23856      * but not over any of its registered drop nodes.  The default implementation returns this.dropNotAllowed, so
23857      * it should be overridden to provide the proper feedback if necessary.
23858      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
23859      * @param {Event} e The event
23860      * @param {Object} data An object containing arbitrary data supplied by the drag source
23861      * @return {String} status The CSS class that communicates the drop status back to the source so that the
23862      * underlying {@link Roo.dd.StatusProxy} can be updated
23863      */
23864     onContainerOver : function(dd, e, data){
23865         return this.dropNotAllowed;
23866     },
23867
23868     /**
23869      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has been dropped on it,
23870      * but not on any of its registered drop nodes.  The default implementation returns false, so it should be
23871      * overridden to provide the appropriate processing of the drop event if you need the drop zone itself to
23872      * be able to accept drops.  It should return true when valid so that the drag source's repair action does not run.
23873      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
23874      * @param {Event} e The event
23875      * @param {Object} data An object containing arbitrary data supplied by the drag source
23876      * @return {Boolean} True if the drop was valid, else false
23877      */
23878     onContainerDrop : function(dd, e, data){
23879         return false;
23880     },
23881
23882     /**
23883      * The function a {@link Roo.dd.DragSource} calls once to notify this drop zone that the source is now over
23884      * the zone.  The default implementation returns this.dropNotAllowed and expects that only registered drop
23885      * nodes can process drag drop operations, so if you need the drop zone itself to be able to process drops
23886      * you should override this method and provide a custom implementation.
23887      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
23888      * @param {Event} e The event
23889      * @param {Object} data An object containing arbitrary data supplied by the drag source
23890      * @return {String} status The CSS class that communicates the drop status back to the source so that the
23891      * underlying {@link Roo.dd.StatusProxy} can be updated
23892      */
23893     notifyEnter : function(dd, e, data){
23894         return this.dropNotAllowed;
23895     },
23896
23897     /**
23898      * The function a {@link Roo.dd.DragSource} calls continuously while it is being dragged over the drop zone.
23899      * This method will be called on every mouse movement while the drag source is over the drop zone.
23900      * It will call {@link #onNodeOver} while the drag source is over a registered node, and will also automatically
23901      * delegate to the appropriate node-specific methods as necessary when the drag source enters and exits
23902      * registered nodes ({@link #onNodeEnter}, {@link #onNodeOut}). If the drag source is not currently over a
23903      * registered node, it will call {@link #onContainerOver}.
23904      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
23905      * @param {Event} e The event
23906      * @param {Object} data An object containing arbitrary data supplied by the drag source
23907      * @return {String} status The CSS class that communicates the drop status back to the source so that the
23908      * underlying {@link Roo.dd.StatusProxy} can be updated
23909      */
23910     notifyOver : function(dd, e, data){
23911         var n = this.getTargetFromEvent(e);
23912         if(!n){ // not over valid drop target
23913             if(this.lastOverNode){
23914                 this.onNodeOut(this.lastOverNode, dd, e, data);
23915                 this.lastOverNode = null;
23916             }
23917             return this.onContainerOver(dd, e, data);
23918         }
23919         if(this.lastOverNode != n){
23920             if(this.lastOverNode){
23921                 this.onNodeOut(this.lastOverNode, dd, e, data);
23922             }
23923             this.onNodeEnter(n, dd, e, data);
23924             this.lastOverNode = n;
23925         }
23926         return this.onNodeOver(n, dd, e, data);
23927     },
23928
23929     /**
23930      * The function a {@link Roo.dd.DragSource} calls once to notify this drop zone that the source has been dragged
23931      * out of the zone without dropping.  If the drag source is currently over a registered node, the notification
23932      * will be delegated to {@link #onNodeOut} for node-specific handling, otherwise it will be ignored.
23933      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
23934      * @param {Event} e The event
23935      * @param {Object} data An object containing arbitrary data supplied by the drag zone
23936      */
23937     notifyOut : function(dd, e, data){
23938         if(this.lastOverNode){
23939             this.onNodeOut(this.lastOverNode, dd, e, data);
23940             this.lastOverNode = null;
23941         }
23942     },
23943
23944     /**
23945      * The function a {@link Roo.dd.DragSource} calls once to notify this drop zone that the dragged item has
23946      * been dropped on it.  The drag zone will look up the target node based on the event passed in, and if there
23947      * is a node registered for that event, it will delegate to {@link #onNodeDrop} for node-specific handling,
23948      * otherwise it will call {@link #onContainerDrop}.
23949      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
23950      * @param {Event} e The event
23951      * @param {Object} data An object containing arbitrary data supplied by the drag source
23952      * @return {Boolean} True if the drop was valid, else false
23953      */
23954     notifyDrop : function(dd, e, data){
23955         if(this.lastOverNode){
23956             this.onNodeOut(this.lastOverNode, dd, e, data);
23957             this.lastOverNode = null;
23958         }
23959         var n = this.getTargetFromEvent(e);
23960         return n ?
23961             this.onNodeDrop(n, dd, e, data) :
23962             this.onContainerDrop(dd, e, data);
23963     },
23964
23965     // private
23966     triggerCacheRefresh : function(){
23967         Roo.dd.DDM.refreshCache(this.groups);
23968     }  
23969 });/*
23970  * Based on:
23971  * Ext JS Library 1.1.1
23972  * Copyright(c) 2006-2007, Ext JS, LLC.
23973  *
23974  * Originally Released Under LGPL - original licence link has changed is not relivant.
23975  *
23976  * Fork - LGPL
23977  * <script type="text/javascript">
23978  */
23979
23980
23981 /**
23982  * @class Roo.data.SortTypes
23983  * @static
23984  * Defines the default sorting (casting?) comparison functions used when sorting data.
23985  */
23986 Roo.data.SortTypes = {
23987     /**
23988      * Default sort that does nothing
23989      * @param {Mixed} s The value being converted
23990      * @return {Mixed} The comparison value
23991      */
23992     none : function(s){
23993         return s;
23994     },
23995     
23996     /**
23997      * The regular expression used to strip tags
23998      * @type {RegExp}
23999      * @property
24000      */
24001     stripTagsRE : /<\/?[^>]+>/gi,
24002     
24003     /**
24004      * Strips all HTML tags to sort on text only
24005      * @param {Mixed} s The value being converted
24006      * @return {String} The comparison value
24007      */
24008     asText : function(s){
24009         return String(s).replace(this.stripTagsRE, "");
24010     },
24011     
24012     /**
24013      * Strips all HTML tags to sort on text only - Case insensitive
24014      * @param {Mixed} s The value being converted
24015      * @return {String} The comparison value
24016      */
24017     asUCText : function(s){
24018         return String(s).toUpperCase().replace(this.stripTagsRE, "");
24019     },
24020     
24021     /**
24022      * Case insensitive string
24023      * @param {Mixed} s The value being converted
24024      * @return {String} The comparison value
24025      */
24026     asUCString : function(s) {
24027         return String(s).toUpperCase();
24028     },
24029     
24030     /**
24031      * Date sorting
24032      * @param {Mixed} s The value being converted
24033      * @return {Number} The comparison value
24034      */
24035     asDate : function(s) {
24036         if(!s){
24037             return 0;
24038         }
24039         if(s instanceof Date){
24040             return s.getTime();
24041         }
24042         return Date.parse(String(s));
24043     },
24044     
24045     /**
24046      * Float sorting
24047      * @param {Mixed} s The value being converted
24048      * @return {Float} The comparison value
24049      */
24050     asFloat : function(s) {
24051         var val = parseFloat(String(s).replace(/,/g, ""));
24052         if(isNaN(val)) {
24053             val = 0;
24054         }
24055         return val;
24056     },
24057     
24058     /**
24059      * Integer sorting
24060      * @param {Mixed} s The value being converted
24061      * @return {Number} The comparison value
24062      */
24063     asInt : function(s) {
24064         var val = parseInt(String(s).replace(/,/g, ""));
24065         if(isNaN(val)) {
24066             val = 0;
24067         }
24068         return val;
24069     }
24070 };/*
24071  * Based on:
24072  * Ext JS Library 1.1.1
24073  * Copyright(c) 2006-2007, Ext JS, LLC.
24074  *
24075  * Originally Released Under LGPL - original licence link has changed is not relivant.
24076  *
24077  * Fork - LGPL
24078  * <script type="text/javascript">
24079  */
24080
24081 /**
24082 * @class Roo.data.Record
24083  * Instances of this class encapsulate both record <em>definition</em> information, and record
24084  * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
24085  * to access Records cached in an {@link Roo.data.Store} object.<br>
24086  * <p>
24087  * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
24088  * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
24089  * objects.<br>
24090  * <p>
24091  * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
24092  * @constructor
24093  * This constructor should not be used to create Record objects. Instead, use the constructor generated by
24094  * {@link #create}. The parameters are the same.
24095  * @param {Array} data An associative Array of data values keyed by the field name.
24096  * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
24097  * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
24098  * not specified an integer id is generated.
24099  */
24100 Roo.data.Record = function(data, id){
24101     this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
24102     this.data = data;
24103 };
24104
24105 /**
24106  * Generate a constructor for a specific record layout.
24107  * @param {Array} o An Array of field definition objects which specify field names, and optionally,
24108  * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
24109  * Each field definition object may contain the following properties: <ul>
24110  * <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,
24111  * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
24112  * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
24113  * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
24114  * is being used, then this is a string containing the javascript expression to reference the data relative to 
24115  * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
24116  * to the data item relative to the record element. If the mapping expression is the same as the field name,
24117  * this may be omitted.</p></li>
24118  * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
24119  * <ul><li>auto (Default, implies no conversion)</li>
24120  * <li>string</li>
24121  * <li>int</li>
24122  * <li>float</li>
24123  * <li>boolean</li>
24124  * <li>date</li></ul></p></li>
24125  * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
24126  * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
24127  * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
24128  * by the Reader into an object that will be stored in the Record. It is passed the
24129  * following parameters:<ul>
24130  * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
24131  * </ul></p></li>
24132  * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
24133  * </ul>
24134  * <br>usage:<br><pre><code>
24135 var TopicRecord = Roo.data.Record.create(
24136     {name: 'title', mapping: 'topic_title'},
24137     {name: 'author', mapping: 'username'},
24138     {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
24139     {name: 'lastPost', mapping: 'post_time', type: 'date'},
24140     {name: 'lastPoster', mapping: 'user2'},
24141     {name: 'excerpt', mapping: 'post_text'}
24142 );
24143
24144 var myNewRecord = new TopicRecord({
24145     title: 'Do my job please',
24146     author: 'noobie',
24147     totalPosts: 1,
24148     lastPost: new Date(),
24149     lastPoster: 'Animal',
24150     excerpt: 'No way dude!'
24151 });
24152 myStore.add(myNewRecord);
24153 </code></pre>
24154  * @method create
24155  * @static
24156  */
24157 Roo.data.Record.create = function(o){
24158     var f = function(){
24159         f.superclass.constructor.apply(this, arguments);
24160     };
24161     Roo.extend(f, Roo.data.Record);
24162     var p = f.prototype;
24163     p.fields = new Roo.util.MixedCollection(false, function(field){
24164         return field.name;
24165     });
24166     for(var i = 0, len = o.length; i < len; i++){
24167         p.fields.add(new Roo.data.Field(o[i]));
24168     }
24169     f.getField = function(name){
24170         return p.fields.get(name);  
24171     };
24172     return f;
24173 };
24174
24175 Roo.data.Record.AUTO_ID = 1000;
24176 Roo.data.Record.EDIT = 'edit';
24177 Roo.data.Record.REJECT = 'reject';
24178 Roo.data.Record.COMMIT = 'commit';
24179
24180 Roo.data.Record.prototype = {
24181     /**
24182      * Readonly flag - true if this record has been modified.
24183      * @type Boolean
24184      */
24185     dirty : false,
24186     editing : false,
24187     error: null,
24188     modified: null,
24189
24190     // private
24191     join : function(store){
24192         this.store = store;
24193     },
24194
24195     /**
24196      * Set the named field to the specified value.
24197      * @param {String} name The name of the field to set.
24198      * @param {Object} value The value to set the field to.
24199      */
24200     set : function(name, value){
24201         if(this.data[name] == value){
24202             return;
24203         }
24204         this.dirty = true;
24205         if(!this.modified){
24206             this.modified = {};
24207         }
24208         if(typeof this.modified[name] == 'undefined'){
24209             this.modified[name] = this.data[name];
24210         }
24211         this.data[name] = value;
24212         if(!this.editing && this.store){
24213             this.store.afterEdit(this);
24214         }       
24215     },
24216
24217     /**
24218      * Get the value of the named field.
24219      * @param {String} name The name of the field to get the value of.
24220      * @return {Object} The value of the field.
24221      */
24222     get : function(name){
24223         return this.data[name]; 
24224     },
24225
24226     // private
24227     beginEdit : function(){
24228         this.editing = true;
24229         this.modified = {}; 
24230     },
24231
24232     // private
24233     cancelEdit : function(){
24234         this.editing = false;
24235         delete this.modified;
24236     },
24237
24238     // private
24239     endEdit : function(){
24240         this.editing = false;
24241         if(this.dirty && this.store){
24242             this.store.afterEdit(this);
24243         }
24244     },
24245
24246     /**
24247      * Usually called by the {@link Roo.data.Store} which owns the Record.
24248      * Rejects all changes made to the Record since either creation, or the last commit operation.
24249      * Modified fields are reverted to their original values.
24250      * <p>
24251      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
24252      * of reject operations.
24253      */
24254     reject : function(){
24255         var m = this.modified;
24256         for(var n in m){
24257             if(typeof m[n] != "function"){
24258                 this.data[n] = m[n];
24259             }
24260         }
24261         this.dirty = false;
24262         delete this.modified;
24263         this.editing = false;
24264         if(this.store){
24265             this.store.afterReject(this);
24266         }
24267     },
24268
24269     /**
24270      * Usually called by the {@link Roo.data.Store} which owns the Record.
24271      * Commits all changes made to the Record since either creation, or the last commit operation.
24272      * <p>
24273      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
24274      * of commit operations.
24275      */
24276     commit : function(){
24277         this.dirty = false;
24278         delete this.modified;
24279         this.editing = false;
24280         if(this.store){
24281             this.store.afterCommit(this);
24282         }
24283     },
24284
24285     // private
24286     hasError : function(){
24287         return this.error != null;
24288     },
24289
24290     // private
24291     clearError : function(){
24292         this.error = null;
24293     },
24294
24295     /**
24296      * Creates a copy of this record.
24297      * @param {String} id (optional) A new record id if you don't want to use this record's id
24298      * @return {Record}
24299      */
24300     copy : function(newId) {
24301         return new this.constructor(Roo.apply({}, this.data), newId || this.id);
24302     }
24303 };/*
24304  * Based on:
24305  * Ext JS Library 1.1.1
24306  * Copyright(c) 2006-2007, Ext JS, LLC.
24307  *
24308  * Originally Released Under LGPL - original licence link has changed is not relivant.
24309  *
24310  * Fork - LGPL
24311  * <script type="text/javascript">
24312  */
24313
24314
24315
24316 /**
24317  * @class Roo.data.Store
24318  * @extends Roo.util.Observable
24319  * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
24320  * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
24321  * <p>
24322  * 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
24323  * has no knowledge of the format of the data returned by the Proxy.<br>
24324  * <p>
24325  * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
24326  * instances from the data object. These records are cached and made available through accessor functions.
24327  * @constructor
24328  * Creates a new Store.
24329  * @param {Object} config A config object containing the objects needed for the Store to access data,
24330  * and read the data into Records.
24331  */
24332 Roo.data.Store = function(config){
24333     this.data = new Roo.util.MixedCollection(false);
24334     this.data.getKey = function(o){
24335         return o.id;
24336     };
24337     this.baseParams = {};
24338     // private
24339     this.paramNames = {
24340         "start" : "start",
24341         "limit" : "limit",
24342         "sort" : "sort",
24343         "dir" : "dir",
24344         "multisort" : "_multisort"
24345     };
24346
24347     if(config && config.data){
24348         this.inlineData = config.data;
24349         delete config.data;
24350     }
24351
24352     Roo.apply(this, config);
24353     
24354     if(this.reader){ // reader passed
24355         this.reader = Roo.factory(this.reader, Roo.data);
24356         this.reader.xmodule = this.xmodule || false;
24357         if(!this.recordType){
24358             this.recordType = this.reader.recordType;
24359         }
24360         if(this.reader.onMetaChange){
24361             this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
24362         }
24363     }
24364
24365     if(this.recordType){
24366         this.fields = this.recordType.prototype.fields;
24367     }
24368     this.modified = [];
24369
24370     this.addEvents({
24371         /**
24372          * @event datachanged
24373          * Fires when the data cache has changed, and a widget which is using this Store
24374          * as a Record cache should refresh its view.
24375          * @param {Store} this
24376          */
24377         datachanged : true,
24378         /**
24379          * @event metachange
24380          * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
24381          * @param {Store} this
24382          * @param {Object} meta The JSON metadata
24383          */
24384         metachange : true,
24385         /**
24386          * @event add
24387          * Fires when Records have been added to the Store
24388          * @param {Store} this
24389          * @param {Roo.data.Record[]} records The array of Records added
24390          * @param {Number} index The index at which the record(s) were added
24391          */
24392         add : true,
24393         /**
24394          * @event remove
24395          * Fires when a Record has been removed from the Store
24396          * @param {Store} this
24397          * @param {Roo.data.Record} record The Record that was removed
24398          * @param {Number} index The index at which the record was removed
24399          */
24400         remove : true,
24401         /**
24402          * @event update
24403          * Fires when a Record has been updated
24404          * @param {Store} this
24405          * @param {Roo.data.Record} record The Record that was updated
24406          * @param {String} operation The update operation being performed.  Value may be one of:
24407          * <pre><code>
24408  Roo.data.Record.EDIT
24409  Roo.data.Record.REJECT
24410  Roo.data.Record.COMMIT
24411          * </code></pre>
24412          */
24413         update : true,
24414         /**
24415          * @event clear
24416          * Fires when the data cache has been cleared.
24417          * @param {Store} this
24418          */
24419         clear : true,
24420         /**
24421          * @event beforeload
24422          * Fires before a request is made for a new data object.  If the beforeload handler returns false
24423          * the load action will be canceled.
24424          * @param {Store} this
24425          * @param {Object} options The loading options that were specified (see {@link #load} for details)
24426          */
24427         beforeload : true,
24428         /**
24429          * @event beforeloadadd
24430          * Fires after a new set of Records has been loaded.
24431          * @param {Store} this
24432          * @param {Roo.data.Record[]} records The Records that were loaded
24433          * @param {Object} options The loading options that were specified (see {@link #load} for details)
24434          */
24435         beforeloadadd : true,
24436         /**
24437          * @event load
24438          * Fires after a new set of Records has been loaded, before they are added to the store.
24439          * @param {Store} this
24440          * @param {Roo.data.Record[]} records The Records that were loaded
24441          * @param {Object} options The loading options that were specified (see {@link #load} for details)
24442          * @params {Object} return from reader
24443          */
24444         load : true,
24445         /**
24446          * @event loadexception
24447          * Fires if an exception occurs in the Proxy during loading.
24448          * Called with the signature of the Proxy's "loadexception" event.
24449          * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
24450          * 
24451          * @param {Proxy} 
24452          * @param {Object} return from JsonData.reader() - success, totalRecords, records
24453          * @param {Object} load options 
24454          * @param {Object} jsonData from your request (normally this contains the Exception)
24455          */
24456         loadexception : true
24457     });
24458     
24459     if(this.proxy){
24460         this.proxy = Roo.factory(this.proxy, Roo.data);
24461         this.proxy.xmodule = this.xmodule || false;
24462         this.relayEvents(this.proxy,  ["loadexception"]);
24463     }
24464     this.sortToggle = {};
24465     this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
24466
24467     Roo.data.Store.superclass.constructor.call(this);
24468
24469     if(this.inlineData){
24470         this.loadData(this.inlineData);
24471         delete this.inlineData;
24472     }
24473 };
24474
24475 Roo.extend(Roo.data.Store, Roo.util.Observable, {
24476      /**
24477     * @cfg {boolean} isLocal   flag if data is locally available (and can be always looked up
24478     * without a remote query - used by combo/forms at present.
24479     */
24480     
24481     /**
24482     * @cfg {Roo.data.DataProxy} proxy [required] The Proxy object which provides access to a data object.
24483     */
24484     /**
24485     * @cfg {Array} data Inline data to be loaded when the store is initialized.
24486     */
24487     /**
24488     * @cfg {Roo.data.DataReader} reader [required]  The Reader object which processes the data object and returns
24489     * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
24490     */
24491     /**
24492     * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
24493     * on any HTTP request
24494     */
24495     /**
24496     * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
24497     */
24498     /**
24499     * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
24500     */
24501     multiSort: false,
24502     /**
24503     * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
24504     * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
24505     */
24506     remoteSort : false,
24507
24508     /**
24509     * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
24510      * loaded or when a record is removed. (defaults to false).
24511     */
24512     pruneModifiedRecords : false,
24513
24514     // private
24515     lastOptions : null,
24516
24517     /**
24518      * Add Records to the Store and fires the add event.
24519      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
24520      */
24521     add : function(records){
24522         records = [].concat(records);
24523         for(var i = 0, len = records.length; i < len; i++){
24524             records[i].join(this);
24525         }
24526         var index = this.data.length;
24527         this.data.addAll(records);
24528         this.fireEvent("add", this, records, index);
24529     },
24530
24531     /**
24532      * Remove a Record from the Store and fires the remove event.
24533      * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
24534      */
24535     remove : function(record){
24536         var index = this.data.indexOf(record);
24537         this.data.removeAt(index);
24538  
24539         if(this.pruneModifiedRecords){
24540             this.modified.remove(record);
24541         }
24542         this.fireEvent("remove", this, record, index);
24543     },
24544
24545     /**
24546      * Remove all Records from the Store and fires the clear event.
24547      */
24548     removeAll : function(){
24549         this.data.clear();
24550         if(this.pruneModifiedRecords){
24551             this.modified = [];
24552         }
24553         this.fireEvent("clear", this);
24554     },
24555
24556     /**
24557      * Inserts Records to the Store at the given index and fires the add event.
24558      * @param {Number} index The start index at which to insert the passed Records.
24559      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
24560      */
24561     insert : function(index, records){
24562         records = [].concat(records);
24563         for(var i = 0, len = records.length; i < len; i++){
24564             this.data.insert(index, records[i]);
24565             records[i].join(this);
24566         }
24567         this.fireEvent("add", this, records, index);
24568     },
24569
24570     /**
24571      * Get the index within the cache of the passed Record.
24572      * @param {Roo.data.Record} record The Roo.data.Record object to to find.
24573      * @return {Number} The index of the passed Record. Returns -1 if not found.
24574      */
24575     indexOf : function(record){
24576         return this.data.indexOf(record);
24577     },
24578
24579     /**
24580      * Get the index within the cache of the Record with the passed id.
24581      * @param {String} id The id of the Record to find.
24582      * @return {Number} The index of the Record. Returns -1 if not found.
24583      */
24584     indexOfId : function(id){
24585         return this.data.indexOfKey(id);
24586     },
24587
24588     /**
24589      * Get the Record with the specified id.
24590      * @param {String} id The id of the Record to find.
24591      * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
24592      */
24593     getById : function(id){
24594         return this.data.key(id);
24595     },
24596
24597     /**
24598      * Get the Record at the specified index.
24599      * @param {Number} index The index of the Record to find.
24600      * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
24601      */
24602     getAt : function(index){
24603         return this.data.itemAt(index);
24604     },
24605
24606     /**
24607      * Returns a range of Records between specified indices.
24608      * @param {Number} startIndex (optional) The starting index (defaults to 0)
24609      * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
24610      * @return {Roo.data.Record[]} An array of Records
24611      */
24612     getRange : function(start, end){
24613         return this.data.getRange(start, end);
24614     },
24615
24616     // private
24617     storeOptions : function(o){
24618         o = Roo.apply({}, o);
24619         delete o.callback;
24620         delete o.scope;
24621         this.lastOptions = o;
24622     },
24623
24624     /**
24625      * Loads the Record cache from the configured Proxy using the configured Reader.
24626      * <p>
24627      * If using remote paging, then the first load call must specify the <em>start</em>
24628      * and <em>limit</em> properties in the options.params property to establish the initial
24629      * position within the dataset, and the number of Records to cache on each read from the Proxy.
24630      * <p>
24631      * <strong>It is important to note that for remote data sources, loading is asynchronous,
24632      * and this call will return before the new data has been loaded. Perform any post-processing
24633      * in a callback function, or in a "load" event handler.</strong>
24634      * <p>
24635      * @param {Object} options An object containing properties which control loading options:<ul>
24636      * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
24637      * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
24638      * passed the following arguments:<ul>
24639      * <li>r : Roo.data.Record[]</li>
24640      * <li>options: Options object from the load call</li>
24641      * <li>success: Boolean success indicator</li></ul></li>
24642      * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
24643      * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
24644      * </ul>
24645      */
24646     load : function(options){
24647         options = options || {};
24648         if(this.fireEvent("beforeload", this, options) !== false){
24649             this.storeOptions(options);
24650             var p = Roo.apply(options.params || {}, this.baseParams);
24651             // if meta was not loaded from remote source.. try requesting it.
24652             if (!this.reader.metaFromRemote) {
24653                 p._requestMeta = 1;
24654             }
24655             if(this.sortInfo && this.remoteSort){
24656                 var pn = this.paramNames;
24657                 p[pn["sort"]] = this.sortInfo.field;
24658                 p[pn["dir"]] = this.sortInfo.direction;
24659             }
24660             if (this.multiSort) {
24661                 var pn = this.paramNames;
24662                 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
24663             }
24664             
24665             this.proxy.load(p, this.reader, this.loadRecords, this, options);
24666         }
24667     },
24668
24669     /**
24670      * Reloads the Record cache from the configured Proxy using the configured Reader and
24671      * the options from the last load operation performed.
24672      * @param {Object} options (optional) An object containing properties which may override the options
24673      * used in the last load operation. See {@link #load} for details (defaults to null, in which case
24674      * the most recently used options are reused).
24675      */
24676     reload : function(options){
24677         this.load(Roo.applyIf(options||{}, this.lastOptions));
24678     },
24679
24680     // private
24681     // Called as a callback by the Reader during a load operation.
24682     loadRecords : function(o, options, success){
24683          
24684         if(!o){
24685             if(success !== false){
24686                 this.fireEvent("load", this, [], options, o);
24687             }
24688             if(options.callback){
24689                 options.callback.call(options.scope || this, [], options, false);
24690             }
24691             return;
24692         }
24693         // if data returned failure - throw an exception.
24694         if (o.success === false) {
24695             // show a message if no listener is registered.
24696             if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
24697                     Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
24698             }
24699             // loadmask wil be hooked into this..
24700             this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
24701             return;
24702         }
24703         var r = o.records, t = o.totalRecords || r.length;
24704         
24705         this.fireEvent("beforeloadadd", this, r, options, o);
24706         
24707         if(!options || options.add !== true){
24708             if(this.pruneModifiedRecords){
24709                 this.modified = [];
24710             }
24711             for(var i = 0, len = r.length; i < len; i++){
24712                 r[i].join(this);
24713             }
24714             if(this.snapshot){
24715                 this.data = this.snapshot;
24716                 delete this.snapshot;
24717             }
24718             this.data.clear();
24719             this.data.addAll(r);
24720             this.totalLength = t;
24721             this.applySort();
24722             this.fireEvent("datachanged", this);
24723         }else{
24724             this.totalLength = Math.max(t, this.data.length+r.length);
24725             this.add(r);
24726         }
24727         
24728         if(this.parent && !Roo.isIOS && !this.useNativeIOS && this.parent.emptyTitle.length) {
24729                 
24730             var e = new Roo.data.Record({});
24731
24732             e.set(this.parent.displayField, this.parent.emptyTitle);
24733             e.set(this.parent.valueField, '');
24734
24735             this.insert(0, e);
24736         }
24737             
24738         this.fireEvent("load", this, r, options, o);
24739         if(options.callback){
24740             options.callback.call(options.scope || this, r, options, true);
24741         }
24742     },
24743
24744
24745     /**
24746      * Loads data from a passed data block. A Reader which understands the format of the data
24747      * must have been configured in the constructor.
24748      * @param {Object} data The data block from which to read the Records.  The format of the data expected
24749      * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
24750      * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
24751      */
24752     loadData : function(o, append){
24753         var r = this.reader.readRecords(o);
24754         this.loadRecords(r, {add: append}, true);
24755     },
24756     
24757      /**
24758      * using 'cn' the nested child reader read the child array into it's child stores.
24759      * @param {Object} rec The record with a 'children array
24760      */
24761     loadDataFromChildren : function(rec)
24762     {
24763         this.loadData(this.reader.toLoadData(rec));
24764     },
24765     
24766
24767     /**
24768      * Gets the number of cached records.
24769      * <p>
24770      * <em>If using paging, this may not be the total size of the dataset. If the data object
24771      * used by the Reader contains the dataset size, then the getTotalCount() function returns
24772      * the data set size</em>
24773      */
24774     getCount : function(){
24775         return this.data.length || 0;
24776     },
24777
24778     /**
24779      * Gets the total number of records in the dataset as returned by the server.
24780      * <p>
24781      * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
24782      * the dataset size</em>
24783      */
24784     getTotalCount : function(){
24785         return this.totalLength || 0;
24786     },
24787
24788     /**
24789      * Returns the sort state of the Store as an object with two properties:
24790      * <pre><code>
24791  field {String} The name of the field by which the Records are sorted
24792  direction {String} The sort order, "ASC" or "DESC"
24793      * </code></pre>
24794      */
24795     getSortState : function(){
24796         return this.sortInfo;
24797     },
24798
24799     // private
24800     applySort : function(){
24801         if(this.sortInfo && !this.remoteSort){
24802             var s = this.sortInfo, f = s.field;
24803             var st = this.fields.get(f).sortType;
24804             var fn = function(r1, r2){
24805                 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
24806                 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
24807             };
24808             this.data.sort(s.direction, fn);
24809             if(this.snapshot && this.snapshot != this.data){
24810                 this.snapshot.sort(s.direction, fn);
24811             }
24812         }
24813     },
24814
24815     /**
24816      * Sets the default sort column and order to be used by the next load operation.
24817      * @param {String} fieldName The name of the field to sort by.
24818      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
24819      */
24820     setDefaultSort : function(field, dir){
24821         this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
24822     },
24823
24824     /**
24825      * Sort the Records.
24826      * If remote sorting is used, the sort is performed on the server, and the cache is
24827      * reloaded. If local sorting is used, the cache is sorted internally.
24828      * @param {String} fieldName The name of the field to sort by.
24829      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
24830      */
24831     sort : function(fieldName, dir){
24832         var f = this.fields.get(fieldName);
24833         if(!dir){
24834             this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
24835             
24836             if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
24837                 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
24838             }else{
24839                 dir = f.sortDir;
24840             }
24841         }
24842         this.sortToggle[f.name] = dir;
24843         this.sortInfo = {field: f.name, direction: dir};
24844         if(!this.remoteSort){
24845             this.applySort();
24846             this.fireEvent("datachanged", this);
24847         }else{
24848             this.load(this.lastOptions);
24849         }
24850     },
24851
24852     /**
24853      * Calls the specified function for each of the Records in the cache.
24854      * @param {Function} fn The function to call. The Record is passed as the first parameter.
24855      * Returning <em>false</em> aborts and exits the iteration.
24856      * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
24857      */
24858     each : function(fn, scope){
24859         this.data.each(fn, scope);
24860     },
24861
24862     /**
24863      * Gets all records modified since the last commit.  Modified records are persisted across load operations
24864      * (e.g., during paging).
24865      * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
24866      */
24867     getModifiedRecords : function(){
24868         return this.modified;
24869     },
24870
24871     // private
24872     createFilterFn : function(property, value, anyMatch){
24873         if(!value.exec){ // not a regex
24874             value = String(value);
24875             if(value.length == 0){
24876                 return false;
24877             }
24878             value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
24879         }
24880         return function(r){
24881             return value.test(r.data[property]);
24882         };
24883     },
24884
24885     /**
24886      * Sums the value of <i>property</i> for each record between start and end and returns the result.
24887      * @param {String} property A field on your records
24888      * @param {Number} start The record index to start at (defaults to 0)
24889      * @param {Number} end The last record index to include (defaults to length - 1)
24890      * @return {Number} The sum
24891      */
24892     sum : function(property, start, end){
24893         var rs = this.data.items, v = 0;
24894         start = start || 0;
24895         end = (end || end === 0) ? end : rs.length-1;
24896
24897         for(var i = start; i <= end; i++){
24898             v += (rs[i].data[property] || 0);
24899         }
24900         return v;
24901     },
24902
24903     /**
24904      * Filter the records by a specified property.
24905      * @param {String} field A field on your records
24906      * @param {String/RegExp} value Either a string that the field
24907      * should start with or a RegExp to test against the field
24908      * @param {Boolean} anyMatch True to match any part not just the beginning
24909      */
24910     filter : function(property, value, anyMatch){
24911         var fn = this.createFilterFn(property, value, anyMatch);
24912         return fn ? this.filterBy(fn) : this.clearFilter();
24913     },
24914
24915     /**
24916      * Filter by a function. The specified function will be called with each
24917      * record in this data source. If the function returns true the record is included,
24918      * otherwise it is filtered.
24919      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
24920      * @param {Object} scope (optional) The scope of the function (defaults to this)
24921      */
24922     filterBy : function(fn, scope){
24923         this.snapshot = this.snapshot || this.data;
24924         this.data = this.queryBy(fn, scope||this);
24925         this.fireEvent("datachanged", this);
24926     },
24927
24928     /**
24929      * Query the records by a specified property.
24930      * @param {String} field A field on your records
24931      * @param {String/RegExp} value Either a string that the field
24932      * should start with or a RegExp to test against the field
24933      * @param {Boolean} anyMatch True to match any part not just the beginning
24934      * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
24935      */
24936     query : function(property, value, anyMatch){
24937         var fn = this.createFilterFn(property, value, anyMatch);
24938         return fn ? this.queryBy(fn) : this.data.clone();
24939     },
24940
24941     /**
24942      * Query by a function. The specified function will be called with each
24943      * record in this data source. If the function returns true the record is included
24944      * in the results.
24945      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
24946      * @param {Object} scope (optional) The scope of the function (defaults to this)
24947       @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
24948      **/
24949     queryBy : function(fn, scope){
24950         var data = this.snapshot || this.data;
24951         return data.filterBy(fn, scope||this);
24952     },
24953
24954     /**
24955      * Collects unique values for a particular dataIndex from this store.
24956      * @param {String} dataIndex The property to collect
24957      * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
24958      * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
24959      * @return {Array} An array of the unique values
24960      **/
24961     collect : function(dataIndex, allowNull, bypassFilter){
24962         var d = (bypassFilter === true && this.snapshot) ?
24963                 this.snapshot.items : this.data.items;
24964         var v, sv, r = [], l = {};
24965         for(var i = 0, len = d.length; i < len; i++){
24966             v = d[i].data[dataIndex];
24967             sv = String(v);
24968             if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
24969                 l[sv] = true;
24970                 r[r.length] = v;
24971             }
24972         }
24973         return r;
24974     },
24975
24976     /**
24977      * Revert to a view of the Record cache with no filtering applied.
24978      * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
24979      */
24980     clearFilter : function(suppressEvent){
24981         if(this.snapshot && this.snapshot != this.data){
24982             this.data = this.snapshot;
24983             delete this.snapshot;
24984             if(suppressEvent !== true){
24985                 this.fireEvent("datachanged", this);
24986             }
24987         }
24988     },
24989
24990     // private
24991     afterEdit : function(record){
24992         if(this.modified.indexOf(record) == -1){
24993             this.modified.push(record);
24994         }
24995         this.fireEvent("update", this, record, Roo.data.Record.EDIT);
24996     },
24997     
24998     // private
24999     afterReject : function(record){
25000         this.modified.remove(record);
25001         this.fireEvent("update", this, record, Roo.data.Record.REJECT);
25002     },
25003
25004     // private
25005     afterCommit : function(record){
25006         this.modified.remove(record);
25007         this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
25008     },
25009
25010     /**
25011      * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
25012      * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
25013      */
25014     commitChanges : function(){
25015         var m = this.modified.slice(0);
25016         this.modified = [];
25017         for(var i = 0, len = m.length; i < len; i++){
25018             m[i].commit();
25019         }
25020     },
25021
25022     /**
25023      * Cancel outstanding changes on all changed records.
25024      */
25025     rejectChanges : function(){
25026         var m = this.modified.slice(0);
25027         this.modified = [];
25028         for(var i = 0, len = m.length; i < len; i++){
25029             m[i].reject();
25030         }
25031     },
25032
25033     onMetaChange : function(meta, rtype, o){
25034         this.recordType = rtype;
25035         this.fields = rtype.prototype.fields;
25036         delete this.snapshot;
25037         this.sortInfo = meta.sortInfo || this.sortInfo;
25038         this.modified = [];
25039         this.fireEvent('metachange', this, this.reader.meta);
25040     },
25041     
25042     moveIndex : function(data, type)
25043     {
25044         var index = this.indexOf(data);
25045         
25046         var newIndex = index + type;
25047         
25048         this.remove(data);
25049         
25050         this.insert(newIndex, data);
25051         
25052     }
25053 });/*
25054  * Based on:
25055  * Ext JS Library 1.1.1
25056  * Copyright(c) 2006-2007, Ext JS, LLC.
25057  *
25058  * Originally Released Under LGPL - original licence link has changed is not relivant.
25059  *
25060  * Fork - LGPL
25061  * <script type="text/javascript">
25062  */
25063
25064 /**
25065  * @class Roo.data.SimpleStore
25066  * @extends Roo.data.Store
25067  * Small helper class to make creating Stores from Array data easier.
25068  * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
25069  * @cfg {Array} fields An array of field definition objects, or field name strings.
25070  * @cfg {Object} an existing reader (eg. copied from another store)
25071  * @cfg {Array} data The multi-dimensional array of data
25072  * @cfg {Roo.data.DataProxy} proxy [not-required]  
25073  * @cfg {Roo.data.Reader} reader  [not-required] 
25074  * @constructor
25075  * @param {Object} config
25076  */
25077 Roo.data.SimpleStore = function(config)
25078 {
25079     Roo.data.SimpleStore.superclass.constructor.call(this, {
25080         isLocal : true,
25081         reader: typeof(config.reader) != 'undefined' ? config.reader : new Roo.data.ArrayReader({
25082                 id: config.id
25083             },
25084             Roo.data.Record.create(config.fields)
25085         ),
25086         proxy : new Roo.data.MemoryProxy(config.data)
25087     });
25088     this.load();
25089 };
25090 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
25091  * Based on:
25092  * Ext JS Library 1.1.1
25093  * Copyright(c) 2006-2007, Ext JS, LLC.
25094  *
25095  * Originally Released Under LGPL - original licence link has changed is not relivant.
25096  *
25097  * Fork - LGPL
25098  * <script type="text/javascript">
25099  */
25100
25101 /**
25102 /**
25103  * @extends Roo.data.Store
25104  * @class Roo.data.JsonStore
25105  * Small helper class to make creating Stores for JSON data easier. <br/>
25106 <pre><code>
25107 var store = new Roo.data.JsonStore({
25108     url: 'get-images.php',
25109     root: 'images',
25110     fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
25111 });
25112 </code></pre>
25113  * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
25114  * JsonReader and HttpProxy (unless inline data is provided).</b>
25115  * @cfg {Array} fields An array of field definition objects, or field name strings.
25116  * @constructor
25117  * @param {Object} config
25118  */
25119 Roo.data.JsonStore = function(c){
25120     Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
25121         proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
25122         reader: new Roo.data.JsonReader(c, c.fields)
25123     }));
25124 };
25125 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
25126  * Based on:
25127  * Ext JS Library 1.1.1
25128  * Copyright(c) 2006-2007, Ext JS, LLC.
25129  *
25130  * Originally Released Under LGPL - original licence link has changed is not relivant.
25131  *
25132  * Fork - LGPL
25133  * <script type="text/javascript">
25134  */
25135
25136  
25137 Roo.data.Field = function(config){
25138     if(typeof config == "string"){
25139         config = {name: config};
25140     }
25141     Roo.apply(this, config);
25142     
25143     if(!this.type){
25144         this.type = "auto";
25145     }
25146     
25147     var st = Roo.data.SortTypes;
25148     // named sortTypes are supported, here we look them up
25149     if(typeof this.sortType == "string"){
25150         this.sortType = st[this.sortType];
25151     }
25152     
25153     // set default sortType for strings and dates
25154     if(!this.sortType){
25155         switch(this.type){
25156             case "string":
25157                 this.sortType = st.asUCString;
25158                 break;
25159             case "date":
25160                 this.sortType = st.asDate;
25161                 break;
25162             default:
25163                 this.sortType = st.none;
25164         }
25165     }
25166
25167     // define once
25168     var stripRe = /[\$,%]/g;
25169
25170     // prebuilt conversion function for this field, instead of
25171     // switching every time we're reading a value
25172     if(!this.convert){
25173         var cv, dateFormat = this.dateFormat;
25174         switch(this.type){
25175             case "":
25176             case "auto":
25177             case undefined:
25178                 cv = function(v){ return v; };
25179                 break;
25180             case "string":
25181                 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
25182                 break;
25183             case "int":
25184                 cv = function(v){
25185                     return v !== undefined && v !== null && v !== '' ?
25186                            parseInt(String(v).replace(stripRe, ""), 10) : '';
25187                     };
25188                 break;
25189             case "float":
25190                 cv = function(v){
25191                     return v !== undefined && v !== null && v !== '' ?
25192                            parseFloat(String(v).replace(stripRe, ""), 10) : ''; 
25193                     };
25194                 break;
25195             case "bool":
25196             case "boolean":
25197                 cv = function(v){ return v === true || v === "true" || v == 1; };
25198                 break;
25199             case "date":
25200                 cv = function(v){
25201                     if(!v){
25202                         return '';
25203                     }
25204                     if(v instanceof Date){
25205                         return v;
25206                     }
25207                     if(dateFormat){
25208                         if(dateFormat == "timestamp"){
25209                             return new Date(v*1000);
25210                         }
25211                         return Date.parseDate(v, dateFormat);
25212                     }
25213                     var parsed = Date.parse(v);
25214                     return parsed ? new Date(parsed) : null;
25215                 };
25216              break;
25217             
25218         }
25219         this.convert = cv;
25220     }
25221 };
25222
25223 Roo.data.Field.prototype = {
25224     dateFormat: null,
25225     defaultValue: "",
25226     mapping: null,
25227     sortType : null,
25228     sortDir : "ASC"
25229 };/*
25230  * Based on:
25231  * Ext JS Library 1.1.1
25232  * Copyright(c) 2006-2007, Ext JS, LLC.
25233  *
25234  * Originally Released Under LGPL - original licence link has changed is not relivant.
25235  *
25236  * Fork - LGPL
25237  * <script type="text/javascript">
25238  */
25239  
25240 // Base class for reading structured data from a data source.  This class is intended to be
25241 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
25242
25243 /**
25244  * @class Roo.data.DataReader
25245  * @abstract
25246  * Base class for reading structured data from a data source.  This class is intended to be
25247  * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
25248  */
25249
25250 Roo.data.DataReader = function(meta, recordType){
25251     
25252     this.meta = meta;
25253     
25254     this.recordType = recordType instanceof Array ? 
25255         Roo.data.Record.create(recordType) : recordType;
25256 };
25257
25258 Roo.data.DataReader.prototype = {
25259     
25260     
25261     readerType : 'Data',
25262      /**
25263      * Create an empty record
25264      * @param {Object} data (optional) - overlay some values
25265      * @return {Roo.data.Record} record created.
25266      */
25267     newRow :  function(d) {
25268         var da =  {};
25269         this.recordType.prototype.fields.each(function(c) {
25270             switch( c.type) {
25271                 case 'int' : da[c.name] = 0; break;
25272                 case 'date' : da[c.name] = new Date(); break;
25273                 case 'float' : da[c.name] = 0.0; break;
25274                 case 'boolean' : da[c.name] = false; break;
25275                 default : da[c.name] = ""; break;
25276             }
25277             
25278         });
25279         return new this.recordType(Roo.apply(da, d));
25280     }
25281     
25282     
25283 };/*
25284  * Based on:
25285  * Ext JS Library 1.1.1
25286  * Copyright(c) 2006-2007, Ext JS, LLC.
25287  *
25288  * Originally Released Under LGPL - original licence link has changed is not relivant.
25289  *
25290  * Fork - LGPL
25291  * <script type="text/javascript">
25292  */
25293
25294 /**
25295  * @class Roo.data.DataProxy
25296  * @extends Roo.util.Observable
25297  * @abstract
25298  * This class is an abstract base class for implementations which provide retrieval of
25299  * unformatted data objects.<br>
25300  * <p>
25301  * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
25302  * (of the appropriate type which knows how to parse the data object) to provide a block of
25303  * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
25304  * <p>
25305  * Custom implementations must implement the load method as described in
25306  * {@link Roo.data.HttpProxy#load}.
25307  */
25308 Roo.data.DataProxy = function(){
25309     this.addEvents({
25310         /**
25311          * @event beforeload
25312          * Fires before a network request is made to retrieve a data object.
25313          * @param {Object} This DataProxy object.
25314          * @param {Object} params The params parameter to the load function.
25315          */
25316         beforeload : true,
25317         /**
25318          * @event load
25319          * Fires before the load method's callback is called.
25320          * @param {Object} This DataProxy object.
25321          * @param {Object} o The data object.
25322          * @param {Object} arg The callback argument object passed to the load function.
25323          */
25324         load : true,
25325         /**
25326          * @event loadexception
25327          * Fires if an Exception occurs during data retrieval.
25328          * @param {Object} This DataProxy object.
25329          * @param {Object} o The data object.
25330          * @param {Object} arg The callback argument object passed to the load function.
25331          * @param {Object} e The Exception.
25332          */
25333         loadexception : true
25334     });
25335     Roo.data.DataProxy.superclass.constructor.call(this);
25336 };
25337
25338 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
25339
25340     /**
25341      * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
25342      */
25343 /*
25344  * Based on:
25345  * Ext JS Library 1.1.1
25346  * Copyright(c) 2006-2007, Ext JS, LLC.
25347  *
25348  * Originally Released Under LGPL - original licence link has changed is not relivant.
25349  *
25350  * Fork - LGPL
25351  * <script type="text/javascript">
25352  */
25353 /**
25354  * @class Roo.data.MemoryProxy
25355  * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
25356  * to the Reader when its load method is called.
25357  * @constructor
25358  * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
25359  */
25360 Roo.data.MemoryProxy = function(data){
25361     if (data.data) {
25362         data = data.data;
25363     }
25364     Roo.data.MemoryProxy.superclass.constructor.call(this);
25365     this.data = data;
25366 };
25367
25368 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
25369     
25370     /**
25371      * Load data from the requested source (in this case an in-memory
25372      * data object passed to the constructor), read the data object into
25373      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
25374      * process that block using the passed callback.
25375      * @param {Object} params This parameter is not used by the MemoryProxy class.
25376      * @param {Roo.data.DataReader} reader The Reader object which converts the data
25377      * object into a block of Roo.data.Records.
25378      * @param {Function} callback The function into which to pass the block of Roo.data.records.
25379      * The function must be passed <ul>
25380      * <li>The Record block object</li>
25381      * <li>The "arg" argument from the load function</li>
25382      * <li>A boolean success indicator</li>
25383      * </ul>
25384      * @param {Object} scope The scope in which to call the callback
25385      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
25386      */
25387     load : function(params, reader, callback, scope, arg){
25388         params = params || {};
25389         var result;
25390         try {
25391             result = reader.readRecords(params.data ? params.data :this.data);
25392         }catch(e){
25393             this.fireEvent("loadexception", this, arg, null, e);
25394             callback.call(scope, null, arg, false);
25395             return;
25396         }
25397         callback.call(scope, result, arg, true);
25398     },
25399     
25400     // private
25401     update : function(params, records){
25402         
25403     }
25404 });/*
25405  * Based on:
25406  * Ext JS Library 1.1.1
25407  * Copyright(c) 2006-2007, Ext JS, LLC.
25408  *
25409  * Originally Released Under LGPL - original licence link has changed is not relivant.
25410  *
25411  * Fork - LGPL
25412  * <script type="text/javascript">
25413  */
25414 /**
25415  * @class Roo.data.HttpProxy
25416  * @extends Roo.data.DataProxy
25417  * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
25418  * configured to reference a certain URL.<br><br>
25419  * <p>
25420  * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
25421  * from which the running page was served.<br><br>
25422  * <p>
25423  * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
25424  * <p>
25425  * Be aware that to enable the browser to parse an XML document, the server must set
25426  * the Content-Type header in the HTTP response to "text/xml".
25427  * @constructor
25428  * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
25429  * an {@link Roo.data.Connection} object.  If a Connection config is passed, the singleton {@link Roo.Ajax} object
25430  * will be used to make the request.
25431  */
25432 Roo.data.HttpProxy = function(conn){
25433     Roo.data.HttpProxy.superclass.constructor.call(this);
25434     // is conn a conn config or a real conn?
25435     this.conn = conn;
25436     this.useAjax = !conn || !conn.events;
25437   
25438 };
25439
25440 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
25441     // thse are take from connection...
25442     
25443     /**
25444      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
25445      */
25446     /**
25447      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
25448      * extra parameters to each request made by this object. (defaults to undefined)
25449      */
25450     /**
25451      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
25452      *  to each request made by this object. (defaults to undefined)
25453      */
25454     /**
25455      * @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)
25456      */
25457     /**
25458      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
25459      */
25460      /**
25461      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
25462      * @type Boolean
25463      */
25464   
25465
25466     /**
25467      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
25468      * @type Boolean
25469      */
25470     /**
25471      * Return the {@link Roo.data.Connection} object being used by this Proxy.
25472      * @return {Connection} The Connection object. This object may be used to subscribe to events on
25473      * a finer-grained basis than the DataProxy events.
25474      */
25475     getConnection : function(){
25476         return this.useAjax ? Roo.Ajax : this.conn;
25477     },
25478
25479     /**
25480      * Load data from the configured {@link Roo.data.Connection}, read the data object into
25481      * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
25482      * process that block using the passed callback.
25483      * @param {Object} params An object containing properties which are to be used as HTTP parameters
25484      * for the request to the remote server.
25485      * @param {Roo.data.DataReader} reader The Reader object which converts the data
25486      * object into a block of Roo.data.Records.
25487      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
25488      * The function must be passed <ul>
25489      * <li>The Record block object</li>
25490      * <li>The "arg" argument from the load function</li>
25491      * <li>A boolean success indicator</li>
25492      * </ul>
25493      * @param {Object} scope The scope in which to call the callback
25494      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
25495      */
25496     load : function(params, reader, callback, scope, arg){
25497         if(this.fireEvent("beforeload", this, params) !== false){
25498             var  o = {
25499                 params : params || {},
25500                 request: {
25501                     callback : callback,
25502                     scope : scope,
25503                     arg : arg
25504                 },
25505                 reader: reader,
25506                 callback : this.loadResponse,
25507                 scope: this
25508             };
25509             if(this.useAjax){
25510                 Roo.applyIf(o, this.conn);
25511                 if(this.activeRequest){
25512                     Roo.Ajax.abort(this.activeRequest);
25513                 }
25514                 this.activeRequest = Roo.Ajax.request(o);
25515             }else{
25516                 this.conn.request(o);
25517             }
25518         }else{
25519             callback.call(scope||this, null, arg, false);
25520         }
25521     },
25522
25523     // private
25524     loadResponse : function(o, success, response){
25525         delete this.activeRequest;
25526         if(!success){
25527             this.fireEvent("loadexception", this, o, response);
25528             o.request.callback.call(o.request.scope, null, o.request.arg, false);
25529             return;
25530         }
25531         var result;
25532         try {
25533             result = o.reader.read(response);
25534         }catch(e){
25535             o.success = false;
25536             o.raw = { errorMsg : response.responseText };
25537             this.fireEvent("loadexception", this, o, response, e);
25538             o.request.callback.call(o.request.scope, o, o.request.arg, false);
25539             return;
25540         }
25541         
25542         this.fireEvent("load", this, o, o.request.arg);
25543         o.request.callback.call(o.request.scope, result, o.request.arg, true);
25544     },
25545
25546     // private
25547     update : function(dataSet){
25548
25549     },
25550
25551     // private
25552     updateResponse : function(dataSet){
25553
25554     }
25555 });/*
25556  * Based on:
25557  * Ext JS Library 1.1.1
25558  * Copyright(c) 2006-2007, Ext JS, LLC.
25559  *
25560  * Originally Released Under LGPL - original licence link has changed is not relivant.
25561  *
25562  * Fork - LGPL
25563  * <script type="text/javascript">
25564  */
25565
25566 /**
25567  * @class Roo.data.ScriptTagProxy
25568  * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
25569  * other than the originating domain of the running page.<br><br>
25570  * <p>
25571  * <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
25572  * of the running page, you must use this class, rather than DataProxy.</em><br><br>
25573  * <p>
25574  * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
25575  * source code that is used as the source inside a &lt;script> tag.<br><br>
25576  * <p>
25577  * In order for the browser to process the returned data, the server must wrap the data object
25578  * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
25579  * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
25580  * depending on whether the callback name was passed:
25581  * <p>
25582  * <pre><code>
25583 boolean scriptTag = false;
25584 String cb = request.getParameter("callback");
25585 if (cb != null) {
25586     scriptTag = true;
25587     response.setContentType("text/javascript");
25588 } else {
25589     response.setContentType("application/x-json");
25590 }
25591 Writer out = response.getWriter();
25592 if (scriptTag) {
25593     out.write(cb + "(");
25594 }
25595 out.print(dataBlock.toJsonString());
25596 if (scriptTag) {
25597     out.write(");");
25598 }
25599 </pre></code>
25600  *
25601  * @constructor
25602  * @param {Object} config A configuration object.
25603  */
25604 Roo.data.ScriptTagProxy = function(config){
25605     Roo.data.ScriptTagProxy.superclass.constructor.call(this);
25606     Roo.apply(this, config);
25607     this.head = document.getElementsByTagName("head")[0];
25608 };
25609
25610 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
25611
25612 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
25613     /**
25614      * @cfg {String} url The URL from which to request the data object.
25615      */
25616     /**
25617      * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
25618      */
25619     timeout : 30000,
25620     /**
25621      * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
25622      * the server the name of the callback function set up by the load call to process the returned data object.
25623      * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
25624      * javascript output which calls this named function passing the data object as its only parameter.
25625      */
25626     callbackParam : "callback",
25627     /**
25628      *  @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
25629      * name to the request.
25630      */
25631     nocache : true,
25632
25633     /**
25634      * Load data from the configured URL, read the data object into
25635      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
25636      * process that block using the passed callback.
25637      * @param {Object} params An object containing properties which are to be used as HTTP parameters
25638      * for the request to the remote server.
25639      * @param {Roo.data.DataReader} reader The Reader object which converts the data
25640      * object into a block of Roo.data.Records.
25641      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
25642      * The function must be passed <ul>
25643      * <li>The Record block object</li>
25644      * <li>The "arg" argument from the load function</li>
25645      * <li>A boolean success indicator</li>
25646      * </ul>
25647      * @param {Object} scope The scope in which to call the callback
25648      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
25649      */
25650     load : function(params, reader, callback, scope, arg){
25651         if(this.fireEvent("beforeload", this, params) !== false){
25652
25653             var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
25654
25655             var url = this.url;
25656             url += (url.indexOf("?") != -1 ? "&" : "?") + p;
25657             if(this.nocache){
25658                 url += "&_dc=" + (new Date().getTime());
25659             }
25660             var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
25661             var trans = {
25662                 id : transId,
25663                 cb : "stcCallback"+transId,
25664                 scriptId : "stcScript"+transId,
25665                 params : params,
25666                 arg : arg,
25667                 url : url,
25668                 callback : callback,
25669                 scope : scope,
25670                 reader : reader
25671             };
25672             var conn = this;
25673
25674             window[trans.cb] = function(o){
25675                 conn.handleResponse(o, trans);
25676             };
25677
25678             url += String.format("&{0}={1}", this.callbackParam, trans.cb);
25679
25680             if(this.autoAbort !== false){
25681                 this.abort();
25682             }
25683
25684             trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
25685
25686             var script = document.createElement("script");
25687             script.setAttribute("src", url);
25688             script.setAttribute("type", "text/javascript");
25689             script.setAttribute("id", trans.scriptId);
25690             this.head.appendChild(script);
25691
25692             this.trans = trans;
25693         }else{
25694             callback.call(scope||this, null, arg, false);
25695         }
25696     },
25697
25698     // private
25699     isLoading : function(){
25700         return this.trans ? true : false;
25701     },
25702
25703     /**
25704      * Abort the current server request.
25705      */
25706     abort : function(){
25707         if(this.isLoading()){
25708             this.destroyTrans(this.trans);
25709         }
25710     },
25711
25712     // private
25713     destroyTrans : function(trans, isLoaded){
25714         this.head.removeChild(document.getElementById(trans.scriptId));
25715         clearTimeout(trans.timeoutId);
25716         if(isLoaded){
25717             window[trans.cb] = undefined;
25718             try{
25719                 delete window[trans.cb];
25720             }catch(e){}
25721         }else{
25722             // if hasn't been loaded, wait for load to remove it to prevent script error
25723             window[trans.cb] = function(){
25724                 window[trans.cb] = undefined;
25725                 try{
25726                     delete window[trans.cb];
25727                 }catch(e){}
25728             };
25729         }
25730     },
25731
25732     // private
25733     handleResponse : function(o, trans){
25734         this.trans = false;
25735         this.destroyTrans(trans, true);
25736         var result;
25737         try {
25738             result = trans.reader.readRecords(o);
25739         }catch(e){
25740             this.fireEvent("loadexception", this, o, trans.arg, e);
25741             trans.callback.call(trans.scope||window, null, trans.arg, false);
25742             return;
25743         }
25744         this.fireEvent("load", this, o, trans.arg);
25745         trans.callback.call(trans.scope||window, result, trans.arg, true);
25746     },
25747
25748     // private
25749     handleFailure : function(trans){
25750         this.trans = false;
25751         this.destroyTrans(trans, false);
25752         this.fireEvent("loadexception", this, null, trans.arg);
25753         trans.callback.call(trans.scope||window, null, trans.arg, false);
25754     }
25755 });/*
25756  * Based on:
25757  * Ext JS Library 1.1.1
25758  * Copyright(c) 2006-2007, Ext JS, LLC.
25759  *
25760  * Originally Released Under LGPL - original licence link has changed is not relivant.
25761  *
25762  * Fork - LGPL
25763  * <script type="text/javascript">
25764  */
25765
25766 /**
25767  * @class Roo.data.JsonReader
25768  * @extends Roo.data.DataReader
25769  * Data reader class to create an Array of Roo.data.Record objects from a JSON response
25770  * based on mappings in a provided Roo.data.Record constructor.
25771  * 
25772  * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
25773  * in the reply previously. 
25774  * 
25775  * <p>
25776  * Example code:
25777  * <pre><code>
25778 var RecordDef = Roo.data.Record.create([
25779     {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
25780     {name: 'occupation'}                 // This field will use "occupation" as the mapping.
25781 ]);
25782 var myReader = new Roo.data.JsonReader({
25783     totalProperty: "results",    // The property which contains the total dataset size (optional)
25784     root: "rows",                // The property which contains an Array of row objects
25785     id: "id"                     // The property within each row object that provides an ID for the record (optional)
25786 }, RecordDef);
25787 </code></pre>
25788  * <p>
25789  * This would consume a JSON file like this:
25790  * <pre><code>
25791 { 'results': 2, 'rows': [
25792     { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
25793     { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
25794 }
25795 </code></pre>
25796  * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
25797  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
25798  * paged from the remote server.
25799  * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
25800  * @cfg {String} root name of the property which contains the Array of row objects.
25801  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
25802  * @cfg {Array} fields Array of field definition objects
25803  * @constructor
25804  * Create a new JsonReader
25805  * @param {Object} meta Metadata configuration options
25806  * @param {Object} recordType Either an Array of field definition objects,
25807  * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
25808  */
25809 Roo.data.JsonReader = function(meta, recordType){
25810     
25811     meta = meta || {};
25812     // set some defaults:
25813     Roo.applyIf(meta, {
25814         totalProperty: 'total',
25815         successProperty : 'success',
25816         root : 'data',
25817         id : 'id'
25818     });
25819     
25820     Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
25821 };
25822 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
25823     
25824     readerType : 'Json',
25825     
25826     /**
25827      * @prop {Boolean} metaFromRemote  - if the meta data was loaded from the remote source.
25828      * Used by Store query builder to append _requestMeta to params.
25829      * 
25830      */
25831     metaFromRemote : false,
25832     /**
25833      * This method is only used by a DataProxy which has retrieved data from a remote server.
25834      * @param {Object} response The XHR object which contains the JSON data in its responseText.
25835      * @return {Object} data A data block which is used by an Roo.data.Store object as
25836      * a cache of Roo.data.Records.
25837      */
25838     read : function(response){
25839         var json = response.responseText;
25840        
25841         var o = /* eval:var:o */ eval("("+json+")");
25842         if(!o) {
25843             throw {message: "JsonReader.read: Json object not found"};
25844         }
25845         
25846         if(o.metaData){
25847             
25848             delete this.ef;
25849             this.metaFromRemote = true;
25850             this.meta = o.metaData;
25851             this.recordType = Roo.data.Record.create(o.metaData.fields);
25852             this.onMetaChange(this.meta, this.recordType, o);
25853         }
25854         return this.readRecords(o);
25855     },
25856
25857     // private function a store will implement
25858     onMetaChange : function(meta, recordType, o){
25859
25860     },
25861
25862     /**
25863          * @ignore
25864          */
25865     simpleAccess: function(obj, subsc) {
25866         return obj[subsc];
25867     },
25868
25869         /**
25870          * @ignore
25871          */
25872     getJsonAccessor: function(){
25873         var re = /[\[\.]/;
25874         return function(expr) {
25875             try {
25876                 return(re.test(expr))
25877                     ? new Function("obj", "return obj." + expr)
25878                     : function(obj){
25879                         return obj[expr];
25880                     };
25881             } catch(e){}
25882             return Roo.emptyFn;
25883         };
25884     }(),
25885
25886     /**
25887      * Create a data block containing Roo.data.Records from an XML document.
25888      * @param {Object} o An object which contains an Array of row objects in the property specified
25889      * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
25890      * which contains the total size of the dataset.
25891      * @return {Object} data A data block which is used by an Roo.data.Store object as
25892      * a cache of Roo.data.Records.
25893      */
25894     readRecords : function(o){
25895         /**
25896          * After any data loads, the raw JSON data is available for further custom processing.
25897          * @type Object
25898          */
25899         this.o = o;
25900         var s = this.meta, Record = this.recordType,
25901             f = Record ? Record.prototype.fields : null, fi = f ? f.items : [], fl = f ? f.length : 0;
25902
25903 //      Generate extraction functions for the totalProperty, the root, the id, and for each field
25904         if (!this.ef) {
25905             if(s.totalProperty) {
25906                     this.getTotal = this.getJsonAccessor(s.totalProperty);
25907                 }
25908                 if(s.successProperty) {
25909                     this.getSuccess = this.getJsonAccessor(s.successProperty);
25910                 }
25911                 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
25912                 if (s.id) {
25913                         var g = this.getJsonAccessor(s.id);
25914                         this.getId = function(rec) {
25915                                 var r = g(rec);  
25916                                 return (r === undefined || r === "") ? null : r;
25917                         };
25918                 } else {
25919                         this.getId = function(){return null;};
25920                 }
25921             this.ef = [];
25922             for(var jj = 0; jj < fl; jj++){
25923                 f = fi[jj];
25924                 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
25925                 this.ef[jj] = this.getJsonAccessor(map);
25926             }
25927         }
25928
25929         var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
25930         if(s.totalProperty){
25931             var vt = parseInt(this.getTotal(o), 10);
25932             if(!isNaN(vt)){
25933                 totalRecords = vt;
25934             }
25935         }
25936         if(s.successProperty){
25937             var vs = this.getSuccess(o);
25938             if(vs === false || vs === 'false'){
25939                 success = false;
25940             }
25941         }
25942         var records = [];
25943         for(var i = 0; i < c; i++){
25944             var n = root[i];
25945             var values = {};
25946             var id = this.getId(n);
25947             for(var j = 0; j < fl; j++){
25948                 f = fi[j];
25949                                 var v = this.ef[j](n);
25950                                 if (!f.convert) {
25951                                         Roo.log('missing convert for ' + f.name);
25952                                         Roo.log(f);
25953                                         continue;
25954                                 }
25955                                 values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
25956             }
25957                         if (!Record) {
25958                                 return {
25959                                         raw : { errorMsg : "JSON Reader Error: fields or metadata not available to create Record" },
25960                                         success : false,
25961                                         records : [],
25962                                         totalRecords : 0
25963                                 };
25964                         }
25965             var record = new Record(values, id);
25966             record.json = n;
25967             records[i] = record;
25968         }
25969         return {
25970             raw : o,
25971             success : success,
25972             records : records,
25973             totalRecords : totalRecords
25974         };
25975     },
25976     // used when loading children.. @see loadDataFromChildren
25977     toLoadData: function(rec)
25978     {
25979         // expect rec just to be an array.. eg [a,b,c, [...] << cn ]
25980         var data = typeof(rec.data.cn) == 'undefined' ? [] : rec.data.cn;
25981         return { data : data, total : data.length };
25982         
25983     }
25984 });/*
25985  * Based on:
25986  * Ext JS Library 1.1.1
25987  * Copyright(c) 2006-2007, Ext JS, LLC.
25988  *
25989  * Originally Released Under LGPL - original licence link has changed is not relivant.
25990  *
25991  * Fork - LGPL
25992  * <script type="text/javascript">
25993  */
25994
25995 /**
25996  * @class Roo.data.XmlReader
25997  * @extends Roo.data.DataReader
25998  * Data reader class to create an Array of {@link Roo.data.Record} objects from an XML document
25999  * based on mappings in a provided Roo.data.Record constructor.<br><br>
26000  * <p>
26001  * <em>Note that in order for the browser to parse a returned XML document, the Content-Type
26002  * header in the HTTP response must be set to "text/xml".</em>
26003  * <p>
26004  * Example code:
26005  * <pre><code>
26006 var RecordDef = Roo.data.Record.create([
26007    {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
26008    {name: 'occupation'}                 // This field will use "occupation" as the mapping.
26009 ]);
26010 var myReader = new Roo.data.XmlReader({
26011    totalRecords: "results", // The element which contains the total dataset size (optional)
26012    record: "row",           // The repeated element which contains row information
26013    id: "id"                 // The element within the row that provides an ID for the record (optional)
26014 }, RecordDef);
26015 </code></pre>
26016  * <p>
26017  * This would consume an XML file like this:
26018  * <pre><code>
26019 &lt;?xml?>
26020 &lt;dataset>
26021  &lt;results>2&lt;/results>
26022  &lt;row>
26023    &lt;id>1&lt;/id>
26024    &lt;name>Bill&lt;/name>
26025    &lt;occupation>Gardener&lt;/occupation>
26026  &lt;/row>
26027  &lt;row>
26028    &lt;id>2&lt;/id>
26029    &lt;name>Ben&lt;/name>
26030    &lt;occupation>Horticulturalist&lt;/occupation>
26031  &lt;/row>
26032 &lt;/dataset>
26033 </code></pre>
26034  * @cfg {String} totalRecords The DomQuery path from which to retrieve the total number of records
26035  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
26036  * paged from the remote server.
26037  * @cfg {String} record The DomQuery path to the repeated element which contains record information.
26038  * @cfg {String} success The DomQuery path to the success attribute used by forms.
26039  * @cfg {String} id The DomQuery path relative from the record element to the element that contains
26040  * a record identifier value.
26041  * @constructor
26042  * Create a new XmlReader
26043  * @param {Object} meta Metadata configuration options
26044  * @param {Mixed} recordType The definition of the data record type to produce.  This can be either a valid
26045  * Record subclass created with {@link Roo.data.Record#create}, or an array of objects with which to call
26046  * Roo.data.Record.create.  See the {@link Roo.data.Record} class for more details.
26047  */
26048 Roo.data.XmlReader = function(meta, recordType){
26049     meta = meta || {};
26050     Roo.data.XmlReader.superclass.constructor.call(this, meta, recordType||meta.fields);
26051 };
26052 Roo.extend(Roo.data.XmlReader, Roo.data.DataReader, {
26053     
26054     readerType : 'Xml',
26055     
26056     /**
26057      * This method is only used by a DataProxy which has retrieved data from a remote server.
26058          * @param {Object} response The XHR object which contains the parsed XML document.  The response is expected
26059          * to contain a method called 'responseXML' that returns an XML document object.
26060      * @return {Object} records A data block which is used by an {@link Roo.data.Store} as
26061      * a cache of Roo.data.Records.
26062      */
26063     read : function(response){
26064         var doc = response.responseXML;
26065         if(!doc) {
26066             throw {message: "XmlReader.read: XML Document not available"};
26067         }
26068         return this.readRecords(doc);
26069     },
26070
26071     /**
26072      * Create a data block containing Roo.data.Records from an XML document.
26073          * @param {Object} doc A parsed XML document.
26074      * @return {Object} records A data block which is used by an {@link Roo.data.Store} as
26075      * a cache of Roo.data.Records.
26076      */
26077     readRecords : function(doc){
26078         /**
26079          * After any data loads/reads, the raw XML Document is available for further custom processing.
26080          * @type XMLDocument
26081          */
26082         this.xmlData = doc;
26083         var root = doc.documentElement || doc;
26084         var q = Roo.DomQuery;
26085         var recordType = this.recordType, fields = recordType.prototype.fields;
26086         var sid = this.meta.id;
26087         var totalRecords = 0, success = true;
26088         if(this.meta.totalRecords){
26089             totalRecords = q.selectNumber(this.meta.totalRecords, root, 0);
26090         }
26091         
26092         if(this.meta.success){
26093             var sv = q.selectValue(this.meta.success, root, true);
26094             success = sv !== false && sv !== 'false';
26095         }
26096         var records = [];
26097         var ns = q.select(this.meta.record, root);
26098         for(var i = 0, len = ns.length; i < len; i++) {
26099                 var n = ns[i];
26100                 var values = {};
26101                 var id = sid ? q.selectValue(sid, n) : undefined;
26102                 for(var j = 0, jlen = fields.length; j < jlen; j++){
26103                     var f = fields.items[j];
26104                 var v = q.selectValue(f.mapping || f.name, n, f.defaultValue);
26105                     v = f.convert(v);
26106                     values[f.name] = v;
26107                 }
26108                 var record = new recordType(values, id);
26109                 record.node = n;
26110                 records[records.length] = record;
26111             }
26112
26113             return {
26114                 success : success,
26115                 records : records,
26116                 totalRecords : totalRecords || records.length
26117             };
26118     }
26119 });/*
26120  * Based on:
26121  * Ext JS Library 1.1.1
26122  * Copyright(c) 2006-2007, Ext JS, LLC.
26123  *
26124  * Originally Released Under LGPL - original licence link has changed is not relivant.
26125  *
26126  * Fork - LGPL
26127  * <script type="text/javascript">
26128  */
26129
26130 /**
26131  * @class Roo.data.ArrayReader
26132  * @extends Roo.data.DataReader
26133  * Data reader class to create an Array of Roo.data.Record objects from an Array.
26134  * Each element of that Array represents a row of data fields. The
26135  * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
26136  * of the field definition if it exists, or the field's ordinal position in the definition.<br>
26137  * <p>
26138  * Example code:.
26139  * <pre><code>
26140 var RecordDef = Roo.data.Record.create([
26141     {name: 'name', mapping: 1},         // "mapping" only needed if an "id" field is present which
26142     {name: 'occupation', mapping: 2}    // precludes using the ordinal position as the index.
26143 ]);
26144 var myReader = new Roo.data.ArrayReader({
26145     id: 0                     // The subscript within row Array that provides an ID for the Record (optional)
26146 }, RecordDef);
26147 </code></pre>
26148  * <p>
26149  * This would consume an Array like this:
26150  * <pre><code>
26151 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
26152   </code></pre>
26153  
26154  * @constructor
26155  * Create a new JsonReader
26156  * @param {Object} meta Metadata configuration options.
26157  * @param {Object|Array} recordType Either an Array of field definition objects
26158  * 
26159  * @cfg {Array} fields Array of field definition objects
26160  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
26161  * as specified to {@link Roo.data.Record#create},
26162  * or an {@link Roo.data.Record} object
26163  *
26164  * 
26165  * created using {@link Roo.data.Record#create}.
26166  */
26167 Roo.data.ArrayReader = function(meta, recordType)
26168 {    
26169     Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType||meta.fields);
26170 };
26171
26172 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
26173     
26174       /**
26175      * Create a data block containing Roo.data.Records from an XML document.
26176      * @param {Object} o An Array of row objects which represents the dataset.
26177      * @return {Object} A data block which is used by an {@link Roo.data.Store} object as
26178      * a cache of Roo.data.Records.
26179      */
26180     readRecords : function(o)
26181     {
26182         var sid = this.meta ? this.meta.id : null;
26183         var recordType = this.recordType, fields = recordType.prototype.fields;
26184         var records = [];
26185         var root = o;
26186         for(var i = 0; i < root.length; i++){
26187             var n = root[i];
26188             var values = {};
26189             var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
26190             for(var j = 0, jlen = fields.length; j < jlen; j++){
26191                 var f = fields.items[j];
26192                 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
26193                 var v = n[k] !== undefined ? n[k] : f.defaultValue;
26194                 v = f.convert(v);
26195                 values[f.name] = v;
26196             }
26197             var record = new recordType(values, id);
26198             record.json = n;
26199             records[records.length] = record;
26200         }
26201         return {
26202             records : records,
26203             totalRecords : records.length
26204         };
26205     },
26206     // used when loading children.. @see loadDataFromChildren
26207     toLoadData: function(rec)
26208     {
26209         // expect rec just to be an array.. eg [a,b,c, [...] << cn ]
26210         return typeof(rec.data.cn) == 'undefined' ? [] : rec.data.cn;
26211         
26212     }
26213     
26214     
26215 });/*
26216  * Based on:
26217  * Ext JS Library 1.1.1
26218  * Copyright(c) 2006-2007, Ext JS, LLC.
26219  *
26220  * Originally Released Under LGPL - original licence link has changed is not relivant.
26221  *
26222  * Fork - LGPL
26223  * <script type="text/javascript">
26224  */
26225
26226
26227 /**
26228  * @class Roo.data.Tree
26229  * @extends Roo.util.Observable
26230  * Represents a tree data structure and bubbles all the events for its nodes. The nodes
26231  * in the tree have most standard DOM functionality.
26232  * @constructor
26233  * @param {Node} root (optional) The root node
26234  */
26235 Roo.data.Tree = function(root){
26236    this.nodeHash = {};
26237    /**
26238     * The root node for this tree
26239     * @type Node
26240     */
26241    this.root = null;
26242    if(root){
26243        this.setRootNode(root);
26244    }
26245    this.addEvents({
26246        /**
26247         * @event append
26248         * Fires when a new child node is appended to a node in this tree.
26249         * @param {Tree} tree The owner tree
26250         * @param {Node} parent The parent node
26251         * @param {Node} node The newly appended node
26252         * @param {Number} index The index of the newly appended node
26253         */
26254        "append" : true,
26255        /**
26256         * @event remove
26257         * Fires when a child node is removed from a node in this tree.
26258         * @param {Tree} tree The owner tree
26259         * @param {Node} parent The parent node
26260         * @param {Node} node The child node removed
26261         */
26262        "remove" : true,
26263        /**
26264         * @event move
26265         * Fires when a node is moved to a new location in the tree
26266         * @param {Tree} tree The owner tree
26267         * @param {Node} node The node moved
26268         * @param {Node} oldParent The old parent of this node
26269         * @param {Node} newParent The new parent of this node
26270         * @param {Number} index The index it was moved to
26271         */
26272        "move" : true,
26273        /**
26274         * @event insert
26275         * Fires when a new child node is inserted in a node in this tree.
26276         * @param {Tree} tree The owner tree
26277         * @param {Node} parent The parent node
26278         * @param {Node} node The child node inserted
26279         * @param {Node} refNode The child node the node was inserted before
26280         */
26281        "insert" : true,
26282        /**
26283         * @event beforeappend
26284         * Fires before a new child is appended to a node in this tree, return false to cancel the append.
26285         * @param {Tree} tree The owner tree
26286         * @param {Node} parent The parent node
26287         * @param {Node} node The child node to be appended
26288         */
26289        "beforeappend" : true,
26290        /**
26291         * @event beforeremove
26292         * Fires before a child is removed from a node in this tree, return false to cancel the remove.
26293         * @param {Tree} tree The owner tree
26294         * @param {Node} parent The parent node
26295         * @param {Node} node The child node to be removed
26296         */
26297        "beforeremove" : true,
26298        /**
26299         * @event beforemove
26300         * Fires before a node is moved to a new location in the tree. Return false to cancel the move.
26301         * @param {Tree} tree The owner tree
26302         * @param {Node} node The node being moved
26303         * @param {Node} oldParent The parent of the node
26304         * @param {Node} newParent The new parent the node is moving to
26305         * @param {Number} index The index it is being moved to
26306         */
26307        "beforemove" : true,
26308        /**
26309         * @event beforeinsert
26310         * Fires before a new child is inserted in a node in this tree, return false to cancel the insert.
26311         * @param {Tree} tree The owner tree
26312         * @param {Node} parent The parent node
26313         * @param {Node} node The child node to be inserted
26314         * @param {Node} refNode The child node the node is being inserted before
26315         */
26316        "beforeinsert" : true
26317    });
26318
26319     Roo.data.Tree.superclass.constructor.call(this);
26320 };
26321
26322 Roo.extend(Roo.data.Tree, Roo.util.Observable, {
26323     pathSeparator: "/",
26324
26325     proxyNodeEvent : function(){
26326         return this.fireEvent.apply(this, arguments);
26327     },
26328
26329     /**
26330      * Returns the root node for this tree.
26331      * @return {Node}
26332      */
26333     getRootNode : function(){
26334         return this.root;
26335     },
26336
26337     /**
26338      * Sets the root node for this tree.
26339      * @param {Node} node
26340      * @return {Node}
26341      */
26342     setRootNode : function(node){
26343         this.root = node;
26344         node.ownerTree = this;
26345         node.isRoot = true;
26346         this.registerNode(node);
26347         return node;
26348     },
26349
26350     /**
26351      * Gets a node in this tree by its id.
26352      * @param {String} id
26353      * @return {Node}
26354      */
26355     getNodeById : function(id){
26356         return this.nodeHash[id];
26357     },
26358
26359     registerNode : function(node){
26360         this.nodeHash[node.id] = node;
26361     },
26362
26363     unregisterNode : function(node){
26364         delete this.nodeHash[node.id];
26365     },
26366
26367     toString : function(){
26368         return "[Tree"+(this.id?" "+this.id:"")+"]";
26369     }
26370 });
26371
26372 /**
26373  * @class Roo.data.Node
26374  * @extends Roo.util.Observable
26375  * @cfg {Boolean} leaf true if this node is a leaf and does not have children
26376  * @cfg {String} id The id for this node. If one is not specified, one is generated.
26377  * @constructor
26378  * @param {Object} attributes The attributes/config for the node
26379  */
26380 Roo.data.Node = function(attributes){
26381     /**
26382      * The attributes supplied for the node. You can use this property to access any custom attributes you supplied.
26383      * @type {Object}
26384      */
26385     this.attributes = attributes || {};
26386     this.leaf = this.attributes.leaf;
26387     /**
26388      * The node id. @type String
26389      */
26390     this.id = this.attributes.id;
26391     if(!this.id){
26392         this.id = Roo.id(null, "ynode-");
26393         this.attributes.id = this.id;
26394     }
26395      
26396     
26397     /**
26398      * All child nodes of this node. @type Array
26399      */
26400     this.childNodes = [];
26401     if(!this.childNodes.indexOf){ // indexOf is a must
26402         this.childNodes.indexOf = function(o){
26403             for(var i = 0, len = this.length; i < len; i++){
26404                 if(this[i] == o) {
26405                     return i;
26406                 }
26407             }
26408             return -1;
26409         };
26410     }
26411     /**
26412      * The parent node for this node. @type Node
26413      */
26414     this.parentNode = null;
26415     /**
26416      * The first direct child node of this node, or null if this node has no child nodes. @type Node
26417      */
26418     this.firstChild = null;
26419     /**
26420      * The last direct child node of this node, or null if this node has no child nodes. @type Node
26421      */
26422     this.lastChild = null;
26423     /**
26424      * The node immediately preceding this node in the tree, or null if there is no sibling node. @type Node
26425      */
26426     this.previousSibling = null;
26427     /**
26428      * The node immediately following this node in the tree, or null if there is no sibling node. @type Node
26429      */
26430     this.nextSibling = null;
26431
26432     this.addEvents({
26433        /**
26434         * @event append
26435         * Fires when a new child node is appended
26436         * @param {Tree} tree The owner tree
26437         * @param {Node} this This node
26438         * @param {Node} node The newly appended node
26439         * @param {Number} index The index of the newly appended node
26440         */
26441        "append" : true,
26442        /**
26443         * @event remove
26444         * Fires when a child node is removed
26445         * @param {Tree} tree The owner tree
26446         * @param {Node} this This node
26447         * @param {Node} node The removed node
26448         */
26449        "remove" : true,
26450        /**
26451         * @event move
26452         * Fires when this node is moved to a new location in the tree
26453         * @param {Tree} tree The owner tree
26454         * @param {Node} this This node
26455         * @param {Node} oldParent The old parent of this node
26456         * @param {Node} newParent The new parent of this node
26457         * @param {Number} index The index it was moved to
26458         */
26459        "move" : true,
26460        /**
26461         * @event insert
26462         * Fires when a new child node is inserted.
26463         * @param {Tree} tree The owner tree
26464         * @param {Node} this This node
26465         * @param {Node} node The child node inserted
26466         * @param {Node} refNode The child node the node was inserted before
26467         */
26468        "insert" : true,
26469        /**
26470         * @event beforeappend
26471         * Fires before a new child is appended, return false to cancel the append.
26472         * @param {Tree} tree The owner tree
26473         * @param {Node} this This node
26474         * @param {Node} node The child node to be appended
26475         */
26476        "beforeappend" : true,
26477        /**
26478         * @event beforeremove
26479         * Fires before a child is removed, return false to cancel the remove.
26480         * @param {Tree} tree The owner tree
26481         * @param {Node} this This node
26482         * @param {Node} node The child node to be removed
26483         */
26484        "beforeremove" : true,
26485        /**
26486         * @event beforemove
26487         * Fires before this node is moved to a new location in the tree. Return false to cancel the move.
26488         * @param {Tree} tree The owner tree
26489         * @param {Node} this This node
26490         * @param {Node} oldParent The parent of this node
26491         * @param {Node} newParent The new parent this node is moving to
26492         * @param {Number} index The index it is being moved to
26493         */
26494        "beforemove" : true,
26495        /**
26496         * @event beforeinsert
26497         * Fires before a new child is inserted, return false to cancel the insert.
26498         * @param {Tree} tree The owner tree
26499         * @param {Node} this This node
26500         * @param {Node} node The child node to be inserted
26501         * @param {Node} refNode The child node the node is being inserted before
26502         */
26503        "beforeinsert" : true
26504    });
26505     this.listeners = this.attributes.listeners;
26506     Roo.data.Node.superclass.constructor.call(this);
26507 };
26508
26509 Roo.extend(Roo.data.Node, Roo.util.Observable, {
26510     fireEvent : function(evtName){
26511         // first do standard event for this node
26512         if(Roo.data.Node.superclass.fireEvent.apply(this, arguments) === false){
26513             return false;
26514         }
26515         // then bubble it up to the tree if the event wasn't cancelled
26516         var ot = this.getOwnerTree();
26517         if(ot){
26518             if(ot.proxyNodeEvent.apply(ot, arguments) === false){
26519                 return false;
26520             }
26521         }
26522         return true;
26523     },
26524
26525     /**
26526      * Returns true if this node is a leaf
26527      * @return {Boolean}
26528      */
26529     isLeaf : function(){
26530         return this.leaf === true;
26531     },
26532
26533     // private
26534     setFirstChild : function(node){
26535         this.firstChild = node;
26536     },
26537
26538     //private
26539     setLastChild : function(node){
26540         this.lastChild = node;
26541     },
26542
26543
26544     /**
26545      * Returns true if this node is the last child of its parent
26546      * @return {Boolean}
26547      */
26548     isLast : function(){
26549        return (!this.parentNode ? true : this.parentNode.lastChild == this);
26550     },
26551
26552     /**
26553      * Returns true if this node is the first child of its parent
26554      * @return {Boolean}
26555      */
26556     isFirst : function(){
26557        return (!this.parentNode ? true : this.parentNode.firstChild == this);
26558     },
26559
26560     hasChildNodes : function(){
26561         return !this.isLeaf() && this.childNodes.length > 0;
26562     },
26563
26564     /**
26565      * Insert node(s) as the last child node of this node.
26566      * @param {Node/Array} node The node or Array of nodes to append
26567      * @return {Node} The appended node if single append, or null if an array was passed
26568      */
26569     appendChild : function(node){
26570         var multi = false;
26571         if(node instanceof Array){
26572             multi = node;
26573         }else if(arguments.length > 1){
26574             multi = arguments;
26575         }
26576         
26577         // if passed an array or multiple args do them one by one
26578         if(multi){
26579             for(var i = 0, len = multi.length; i < len; i++) {
26580                 this.appendChild(multi[i]);
26581             }
26582         }else{
26583             if(this.fireEvent("beforeappend", this.ownerTree, this, node) === false){
26584                 return false;
26585             }
26586             var index = this.childNodes.length;
26587             var oldParent = node.parentNode;
26588             // it's a move, make sure we move it cleanly
26589             if(oldParent){
26590                 if(node.fireEvent("beforemove", node.getOwnerTree(), node, oldParent, this, index) === false){
26591                     return false;
26592                 }
26593                 oldParent.removeChild(node);
26594             }
26595             
26596             index = this.childNodes.length;
26597             if(index == 0){
26598                 this.setFirstChild(node);
26599             }
26600             this.childNodes.push(node);
26601             node.parentNode = this;
26602             var ps = this.childNodes[index-1];
26603             if(ps){
26604                 node.previousSibling = ps;
26605                 ps.nextSibling = node;
26606             }else{
26607                 node.previousSibling = null;
26608             }
26609             node.nextSibling = null;
26610             this.setLastChild(node);
26611             node.setOwnerTree(this.getOwnerTree());
26612             this.fireEvent("append", this.ownerTree, this, node, index);
26613             if(this.ownerTree) {
26614                 this.ownerTree.fireEvent("appendnode", this, node, index);
26615             }
26616             if(oldParent){
26617                 node.fireEvent("move", this.ownerTree, node, oldParent, this, index);
26618             }
26619             return node;
26620         }
26621     },
26622
26623     /**
26624      * Removes a child node from this node.
26625      * @param {Node} node The node to remove
26626      * @return {Node} The removed node
26627      */
26628     removeChild : function(node){
26629         var index = this.childNodes.indexOf(node);
26630         if(index == -1){
26631             return false;
26632         }
26633         if(this.fireEvent("beforeremove", this.ownerTree, this, node) === false){
26634             return false;
26635         }
26636
26637         // remove it from childNodes collection
26638         this.childNodes.splice(index, 1);
26639
26640         // update siblings
26641         if(node.previousSibling){
26642             node.previousSibling.nextSibling = node.nextSibling;
26643         }
26644         if(node.nextSibling){
26645             node.nextSibling.previousSibling = node.previousSibling;
26646         }
26647
26648         // update child refs
26649         if(this.firstChild == node){
26650             this.setFirstChild(node.nextSibling);
26651         }
26652         if(this.lastChild == node){
26653             this.setLastChild(node.previousSibling);
26654         }
26655
26656         node.setOwnerTree(null);
26657         // clear any references from the node
26658         node.parentNode = null;
26659         node.previousSibling = null;
26660         node.nextSibling = null;
26661         this.fireEvent("remove", this.ownerTree, this, node);
26662         return node;
26663     },
26664
26665     /**
26666      * Inserts the first node before the second node in this nodes childNodes collection.
26667      * @param {Node} node The node to insert
26668      * @param {Node} refNode The node to insert before (if null the node is appended)
26669      * @return {Node} The inserted node
26670      */
26671     insertBefore : function(node, refNode){
26672         if(!refNode){ // like standard Dom, refNode can be null for append
26673             return this.appendChild(node);
26674         }
26675         // nothing to do
26676         if(node == refNode){
26677             return false;
26678         }
26679
26680         if(this.fireEvent("beforeinsert", this.ownerTree, this, node, refNode) === false){
26681             return false;
26682         }
26683         var index = this.childNodes.indexOf(refNode);
26684         var oldParent = node.parentNode;
26685         var refIndex = index;
26686
26687         // when moving internally, indexes will change after remove
26688         if(oldParent == this && this.childNodes.indexOf(node) < index){
26689             refIndex--;
26690         }
26691
26692         // it's a move, make sure we move it cleanly
26693         if(oldParent){
26694             if(node.fireEvent("beforemove", node.getOwnerTree(), node, oldParent, this, index, refNode) === false){
26695                 return false;
26696             }
26697             oldParent.removeChild(node);
26698         }
26699         if(refIndex == 0){
26700             this.setFirstChild(node);
26701         }
26702         this.childNodes.splice(refIndex, 0, node);
26703         node.parentNode = this;
26704         var ps = this.childNodes[refIndex-1];
26705         if(ps){
26706             node.previousSibling = ps;
26707             ps.nextSibling = node;
26708         }else{
26709             node.previousSibling = null;
26710         }
26711         node.nextSibling = refNode;
26712         refNode.previousSibling = node;
26713         node.setOwnerTree(this.getOwnerTree());
26714         this.fireEvent("insert", this.ownerTree, this, node, refNode);
26715         if(oldParent){
26716             node.fireEvent("move", this.ownerTree, node, oldParent, this, refIndex, refNode);
26717         }
26718         return node;
26719     },
26720
26721     /**
26722      * Returns the child node at the specified index.
26723      * @param {Number} index
26724      * @return {Node}
26725      */
26726     item : function(index){
26727         return this.childNodes[index];
26728     },
26729
26730     /**
26731      * Replaces one child node in this node with another.
26732      * @param {Node} newChild The replacement node
26733      * @param {Node} oldChild The node to replace
26734      * @return {Node} The replaced node
26735      */
26736     replaceChild : function(newChild, oldChild){
26737         this.insertBefore(newChild, oldChild);
26738         this.removeChild(oldChild);
26739         return oldChild;
26740     },
26741
26742     /**
26743      * Returns the index of a child node
26744      * @param {Node} node
26745      * @return {Number} The index of the node or -1 if it was not found
26746      */
26747     indexOf : function(child){
26748         return this.childNodes.indexOf(child);
26749     },
26750
26751     /**
26752      * Returns the tree this node is in.
26753      * @return {Tree}
26754      */
26755     getOwnerTree : function(){
26756         // if it doesn't have one, look for one
26757         if(!this.ownerTree){
26758             var p = this;
26759             while(p){
26760                 if(p.ownerTree){
26761                     this.ownerTree = p.ownerTree;
26762                     break;
26763                 }
26764                 p = p.parentNode;
26765             }
26766         }
26767         return this.ownerTree;
26768     },
26769
26770     /**
26771      * Returns depth of this node (the root node has a depth of 0)
26772      * @return {Number}
26773      */
26774     getDepth : function(){
26775         var depth = 0;
26776         var p = this;
26777         while(p.parentNode){
26778             ++depth;
26779             p = p.parentNode;
26780         }
26781         return depth;
26782     },
26783
26784     // private
26785     setOwnerTree : function(tree){
26786         // if it's move, we need to update everyone
26787         if(tree != this.ownerTree){
26788             if(this.ownerTree){
26789                 this.ownerTree.unregisterNode(this);
26790             }
26791             this.ownerTree = tree;
26792             var cs = this.childNodes;
26793             for(var i = 0, len = cs.length; i < len; i++) {
26794                 cs[i].setOwnerTree(tree);
26795             }
26796             if(tree){
26797                 tree.registerNode(this);
26798             }
26799         }
26800     },
26801
26802     /**
26803      * Returns the path for this node. The path can be used to expand or select this node programmatically.
26804      * @param {String} attr (optional) The attr to use for the path (defaults to the node's id)
26805      * @return {String} The path
26806      */
26807     getPath : function(attr){
26808         attr = attr || "id";
26809         var p = this.parentNode;
26810         var b = [this.attributes[attr]];
26811         while(p){
26812             b.unshift(p.attributes[attr]);
26813             p = p.parentNode;
26814         }
26815         var sep = this.getOwnerTree().pathSeparator;
26816         return sep + b.join(sep);
26817     },
26818
26819     /**
26820      * Bubbles up the tree from this node, calling the specified function with each node. The scope (<i>this</i>) of
26821      * function call will be the scope provided or the current node. The arguments to the function
26822      * will be the args provided or the current node. If the function returns false at any point,
26823      * the bubble is stopped.
26824      * @param {Function} fn The function to call
26825      * @param {Object} scope (optional) The scope of the function (defaults to current node)
26826      * @param {Array} args (optional) The args to call the function with (default to passing the current node)
26827      */
26828     bubble : function(fn, scope, args){
26829         var p = this;
26830         while(p){
26831             if(fn.call(scope || p, args || p) === false){
26832                 break;
26833             }
26834             p = p.parentNode;
26835         }
26836     },
26837
26838     /**
26839      * Cascades down the tree from this node, calling the specified function with each node. The scope (<i>this</i>) of
26840      * function call will be the scope provided or the current node. The arguments to the function
26841      * will be the args provided or the current node. If the function returns false at any point,
26842      * the cascade is stopped on that branch.
26843      * @param {Function} fn The function to call
26844      * @param {Object} scope (optional) The scope of the function (defaults to current node)
26845      * @param {Array} args (optional) The args to call the function with (default to passing the current node)
26846      */
26847     cascade : function(fn, scope, args){
26848         if(fn.call(scope || this, args || this) !== false){
26849             var cs = this.childNodes;
26850             for(var i = 0, len = cs.length; i < len; i++) {
26851                 cs[i].cascade(fn, scope, args);
26852             }
26853         }
26854     },
26855
26856     /**
26857      * Interates the child nodes of this node, calling the specified function with each node. The scope (<i>this</i>) of
26858      * function call will be the scope provided or the current node. The arguments to the function
26859      * will be the args provided or the current node. If the function returns false at any point,
26860      * the iteration stops.
26861      * @param {Function} fn The function to call
26862      * @param {Object} scope (optional) The scope of the function (defaults to current node)
26863      * @param {Array} args (optional) The args to call the function with (default to passing the current node)
26864      */
26865     eachChild : function(fn, scope, args){
26866         var cs = this.childNodes;
26867         for(var i = 0, len = cs.length; i < len; i++) {
26868                 if(fn.call(scope || this, args || cs[i]) === false){
26869                     break;
26870                 }
26871         }
26872     },
26873
26874     /**
26875      * Finds the first child that has the attribute with the specified value.
26876      * @param {String} attribute The attribute name
26877      * @param {Mixed} value The value to search for
26878      * @return {Node} The found child or null if none was found
26879      */
26880     findChild : function(attribute, value){
26881         var cs = this.childNodes;
26882         for(var i = 0, len = cs.length; i < len; i++) {
26883                 if(cs[i].attributes[attribute] == value){
26884                     return cs[i];
26885                 }
26886         }
26887         return null;
26888     },
26889
26890     /**
26891      * Finds the first child by a custom function. The child matches if the function passed
26892      * returns true.
26893      * @param {Function} fn
26894      * @param {Object} scope (optional)
26895      * @return {Node} The found child or null if none was found
26896      */
26897     findChildBy : function(fn, scope){
26898         var cs = this.childNodes;
26899         for(var i = 0, len = cs.length; i < len; i++) {
26900                 if(fn.call(scope||cs[i], cs[i]) === true){
26901                     return cs[i];
26902                 }
26903         }
26904         return null;
26905     },
26906
26907     /**
26908      * Sorts this nodes children using the supplied sort function
26909      * @param {Function} fn
26910      * @param {Object} scope (optional)
26911      */
26912     sort : function(fn, scope){
26913         var cs = this.childNodes;
26914         var len = cs.length;
26915         if(len > 0){
26916             var sortFn = scope ? function(){fn.apply(scope, arguments);} : fn;
26917             cs.sort(sortFn);
26918             for(var i = 0; i < len; i++){
26919                 var n = cs[i];
26920                 n.previousSibling = cs[i-1];
26921                 n.nextSibling = cs[i+1];
26922                 if(i == 0){
26923                     this.setFirstChild(n);
26924                 }
26925                 if(i == len-1){
26926                     this.setLastChild(n);
26927                 }
26928             }
26929         }
26930     },
26931
26932     /**
26933      * Returns true if this node is an ancestor (at any point) of the passed node.
26934      * @param {Node} node
26935      * @return {Boolean}
26936      */
26937     contains : function(node){
26938         return node.isAncestor(this);
26939     },
26940
26941     /**
26942      * Returns true if the passed node is an ancestor (at any point) of this node.
26943      * @param {Node} node
26944      * @return {Boolean}
26945      */
26946     isAncestor : function(node){
26947         var p = this.parentNode;
26948         while(p){
26949             if(p == node){
26950                 return true;
26951             }
26952             p = p.parentNode;
26953         }
26954         return false;
26955     },
26956
26957     toString : function(){
26958         return "[Node"+(this.id?" "+this.id:"")+"]";
26959     }
26960 });/*
26961  * Based on:
26962  * Ext JS Library 1.1.1
26963  * Copyright(c) 2006-2007, Ext JS, LLC.
26964  *
26965  * Originally Released Under LGPL - original licence link has changed is not relivant.
26966  *
26967  * Fork - LGPL
26968  * <script type="text/javascript">
26969  */
26970
26971
26972 /**
26973  * @class Roo.Shadow
26974  * Simple class that can provide a shadow effect for any element.  Note that the element MUST be absolutely positioned,
26975  * and the shadow does not provide any shimming.  This should be used only in simple cases -- for more advanced
26976  * functionality that can also provide the same shadow effect, see the {@link Roo.Layer} class.
26977  * @constructor
26978  * Create a new Shadow
26979  * @param {Object} config The config object
26980  */
26981 Roo.Shadow = function(config){
26982     Roo.apply(this, config);
26983     if(typeof this.mode != "string"){
26984         this.mode = this.defaultMode;
26985     }
26986     var o = this.offset, a = {h: 0};
26987     var rad = Math.floor(this.offset/2);
26988     switch(this.mode.toLowerCase()){ // all this hideous nonsense calculates the various offsets for shadows
26989         case "drop":
26990             a.w = 0;
26991             a.l = a.t = o;
26992             a.t -= 1;
26993             if(Roo.isIE){
26994                 a.l -= this.offset + rad;
26995                 a.t -= this.offset + rad;
26996                 a.w -= rad;
26997                 a.h -= rad;
26998                 a.t += 1;
26999             }
27000         break;
27001         case "sides":
27002             a.w = (o*2);
27003             a.l = -o;
27004             a.t = o-1;
27005             if(Roo.isIE){
27006                 a.l -= (this.offset - rad);
27007                 a.t -= this.offset + rad;
27008                 a.l += 1;
27009                 a.w -= (this.offset - rad)*2;
27010                 a.w -= rad + 1;
27011                 a.h -= 1;
27012             }
27013         break;
27014         case "frame":
27015             a.w = a.h = (o*2);
27016             a.l = a.t = -o;
27017             a.t += 1;
27018             a.h -= 2;
27019             if(Roo.isIE){
27020                 a.l -= (this.offset - rad);
27021                 a.t -= (this.offset - rad);
27022                 a.l += 1;
27023                 a.w -= (this.offset + rad + 1);
27024                 a.h -= (this.offset + rad);
27025                 a.h += 1;
27026             }
27027         break;
27028     };
27029
27030     this.adjusts = a;
27031 };
27032
27033 Roo.Shadow.prototype = {
27034     /**
27035      * @cfg {String} mode
27036      * The shadow display mode.  Supports the following options:<br />
27037      * sides: Shadow displays on both sides and bottom only<br />
27038      * frame: Shadow displays equally on all four sides<br />
27039      * drop: Traditional bottom-right drop shadow (default)
27040      */
27041     mode: false,
27042     /**
27043      * @cfg {String} offset
27044      * The number of pixels to offset the shadow from the element (defaults to 4)
27045      */
27046     offset: 4,
27047
27048     // private
27049     defaultMode: "drop",
27050
27051     /**
27052      * Displays the shadow under the target element
27053      * @param {String/HTMLElement/Element} targetEl The id or element under which the shadow should display
27054      */
27055     show : function(target){
27056         target = Roo.get(target);
27057         if(!this.el){
27058             this.el = Roo.Shadow.Pool.pull();
27059             if(this.el.dom.nextSibling != target.dom){
27060                 this.el.insertBefore(target);
27061             }
27062         }
27063         this.el.setStyle("z-index", this.zIndex || parseInt(target.getStyle("z-index"), 10)-1);
27064         if(Roo.isIE){
27065             this.el.dom.style.filter="progid:DXImageTransform.Microsoft.alpha(opacity=50) progid:DXImageTransform.Microsoft.Blur(pixelradius="+(this.offset)+")";
27066         }
27067         this.realign(
27068             target.getLeft(true),
27069             target.getTop(true),
27070             target.getWidth(),
27071             target.getHeight()
27072         );
27073         this.el.dom.style.display = "block";
27074     },
27075
27076     /**
27077      * Returns true if the shadow is visible, else false
27078      */
27079     isVisible : function(){
27080         return this.el ? true : false;  
27081     },
27082
27083     /**
27084      * Direct alignment when values are already available. Show must be called at least once before
27085      * calling this method to ensure it is initialized.
27086      * @param {Number} left The target element left position
27087      * @param {Number} top The target element top position
27088      * @param {Number} width The target element width
27089      * @param {Number} height The target element height
27090      */
27091     realign : function(l, t, w, h){
27092         if(!this.el){
27093             return;
27094         }
27095         var a = this.adjusts, d = this.el.dom, s = d.style;
27096         var iea = 0;
27097         s.left = (l+a.l)+"px";
27098         s.top = (t+a.t)+"px";
27099         var sw = (w+a.w), sh = (h+a.h), sws = sw +"px", shs = sh + "px";
27100  
27101         if(s.width != sws || s.height != shs){
27102             s.width = sws;
27103             s.height = shs;
27104             if(!Roo.isIE){
27105                 var cn = d.childNodes;
27106                 var sww = Math.max(0, (sw-12))+"px";
27107                 cn[0].childNodes[1].style.width = sww;
27108                 cn[1].childNodes[1].style.width = sww;
27109                 cn[2].childNodes[1].style.width = sww;
27110                 cn[1].style.height = Math.max(0, (sh-12))+"px";
27111             }
27112         }
27113     },
27114
27115     /**
27116      * Hides this shadow
27117      */
27118     hide : function(){
27119         if(this.el){
27120             this.el.dom.style.display = "none";
27121             Roo.Shadow.Pool.push(this.el);
27122             delete this.el;
27123         }
27124     },
27125
27126     /**
27127      * Adjust the z-index of this shadow
27128      * @param {Number} zindex The new z-index
27129      */
27130     setZIndex : function(z){
27131         this.zIndex = z;
27132         if(this.el){
27133             this.el.setStyle("z-index", z);
27134         }
27135     }
27136 };
27137
27138 // Private utility class that manages the internal Shadow cache
27139 Roo.Shadow.Pool = function(){
27140     var p = [];
27141     var markup = Roo.isIE ?
27142                  '<div class="x-ie-shadow"></div>' :
27143                  '<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>';
27144     return {
27145         pull : function(){
27146             var sh = p.shift();
27147             if(!sh){
27148                 sh = Roo.get(Roo.DomHelper.insertHtml("beforeBegin", document.body.firstChild, markup));
27149                 sh.autoBoxAdjust = false;
27150             }
27151             return sh;
27152         },
27153
27154         push : function(sh){
27155             p.push(sh);
27156         }
27157     };
27158 }();/*
27159  * Based on:
27160  * Ext JS Library 1.1.1
27161  * Copyright(c) 2006-2007, Ext JS, LLC.
27162  *
27163  * Originally Released Under LGPL - original licence link has changed is not relivant.
27164  *
27165  * Fork - LGPL
27166  * <script type="text/javascript">
27167  */
27168
27169
27170 /**
27171  * @class Roo.SplitBar
27172  * @extends Roo.util.Observable
27173  * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
27174  * <br><br>
27175  * Usage:
27176  * <pre><code>
27177 var split = new Roo.SplitBar("elementToDrag", "elementToSize",
27178                    Roo.SplitBar.HORIZONTAL, Roo.SplitBar.LEFT);
27179 split.setAdapter(new Roo.SplitBar.AbsoluteLayoutAdapter("container"));
27180 split.minSize = 100;
27181 split.maxSize = 600;
27182 split.animate = true;
27183 split.on('moved', splitterMoved);
27184 </code></pre>
27185  * @constructor
27186  * Create a new SplitBar
27187  * @param {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar. 
27188  * @param {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged 
27189  * @param {Number} orientation (optional) Either Roo.SplitBar.HORIZONTAL or Roo.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
27190  * @param {Number} placement (optional) Either Roo.SplitBar.LEFT or Roo.SplitBar.RIGHT for horizontal or  
27191                         Roo.SplitBar.TOP or Roo.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
27192                         position of the SplitBar).
27193  */
27194 Roo.SplitBar = function(dragElement, resizingElement, orientation, placement, existingProxy){
27195     
27196     /** @private */
27197     this.el = Roo.get(dragElement, true);
27198     this.el.dom.unselectable = "on";
27199     /** @private */
27200     this.resizingEl = Roo.get(resizingElement, true);
27201
27202     /**
27203      * @private
27204      * The orientation of the split. Either Roo.SplitBar.HORIZONTAL or Roo.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
27205      * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
27206      * @type Number
27207      */
27208     this.orientation = orientation || Roo.SplitBar.HORIZONTAL;
27209     
27210     /**
27211      * The minimum size of the resizing element. (Defaults to 0)
27212      * @type Number
27213      */
27214     this.minSize = 0;
27215     
27216     /**
27217      * The maximum size of the resizing element. (Defaults to 2000)
27218      * @type Number
27219      */
27220     this.maxSize = 2000;
27221     
27222     /**
27223      * Whether to animate the transition to the new size
27224      * @type Boolean
27225      */
27226     this.animate = false;
27227     
27228     /**
27229      * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
27230      * @type Boolean
27231      */
27232     this.useShim = false;
27233     
27234     /** @private */
27235     this.shim = null;
27236     
27237     if(!existingProxy){
27238         /** @private */
27239         this.proxy = Roo.SplitBar.createProxy(this.orientation);
27240     }else{
27241         this.proxy = Roo.get(existingProxy).dom;
27242     }
27243     /** @private */
27244     this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
27245     
27246     /** @private */
27247     this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
27248     
27249     /** @private */
27250     this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
27251     
27252     /** @private */
27253     this.dragSpecs = {};
27254     
27255     /**
27256      * @private The adapter to use to positon and resize elements
27257      */
27258     this.adapter = new Roo.SplitBar.BasicLayoutAdapter();
27259     this.adapter.init(this);
27260     
27261     if(this.orientation == Roo.SplitBar.HORIZONTAL){
27262         /** @private */
27263         this.placement = placement || (this.el.getX() > this.resizingEl.getX() ? Roo.SplitBar.LEFT : Roo.SplitBar.RIGHT);
27264         this.el.addClass("x-splitbar-h");
27265     }else{
27266         /** @private */
27267         this.placement = placement || (this.el.getY() > this.resizingEl.getY() ? Roo.SplitBar.TOP : Roo.SplitBar.BOTTOM);
27268         this.el.addClass("x-splitbar-v");
27269     }
27270     
27271     this.addEvents({
27272         /**
27273          * @event resize
27274          * Fires when the splitter is moved (alias for {@link #event-moved})
27275          * @param {Roo.SplitBar} this
27276          * @param {Number} newSize the new width or height
27277          */
27278         "resize" : true,
27279         /**
27280          * @event moved
27281          * Fires when the splitter is moved
27282          * @param {Roo.SplitBar} this
27283          * @param {Number} newSize the new width or height
27284          */
27285         "moved" : true,
27286         /**
27287          * @event beforeresize
27288          * Fires before the splitter is dragged
27289          * @param {Roo.SplitBar} this
27290          */
27291         "beforeresize" : true,
27292
27293         "beforeapply" : true
27294     });
27295
27296     Roo.util.Observable.call(this);
27297 };
27298
27299 Roo.extend(Roo.SplitBar, Roo.util.Observable, {
27300     onStartProxyDrag : function(x, y){
27301         this.fireEvent("beforeresize", this);
27302         if(!this.overlay){
27303             var o = Roo.DomHelper.insertFirst(document.body,  {cls: "x-drag-overlay", html: "&#160;"}, true);
27304             o.unselectable();
27305             o.enableDisplayMode("block");
27306             // all splitbars share the same overlay
27307             Roo.SplitBar.prototype.overlay = o;
27308         }
27309         this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
27310         this.overlay.show();
27311         Roo.get(this.proxy).setDisplayed("block");
27312         var size = this.adapter.getElementSize(this);
27313         this.activeMinSize = this.getMinimumSize();;
27314         this.activeMaxSize = this.getMaximumSize();;
27315         var c1 = size - this.activeMinSize;
27316         var c2 = Math.max(this.activeMaxSize - size, 0);
27317         if(this.orientation == Roo.SplitBar.HORIZONTAL){
27318             this.dd.resetConstraints();
27319             this.dd.setXConstraint(
27320                 this.placement == Roo.SplitBar.LEFT ? c1 : c2, 
27321                 this.placement == Roo.SplitBar.LEFT ? c2 : c1
27322             );
27323             this.dd.setYConstraint(0, 0);
27324         }else{
27325             this.dd.resetConstraints();
27326             this.dd.setXConstraint(0, 0);
27327             this.dd.setYConstraint(
27328                 this.placement == Roo.SplitBar.TOP ? c1 : c2, 
27329                 this.placement == Roo.SplitBar.TOP ? c2 : c1
27330             );
27331          }
27332         this.dragSpecs.startSize = size;
27333         this.dragSpecs.startPoint = [x, y];
27334         Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
27335     },
27336     
27337     /** 
27338      * @private Called after the drag operation by the DDProxy
27339      */
27340     onEndProxyDrag : function(e){
27341         Roo.get(this.proxy).setDisplayed(false);
27342         var endPoint = Roo.lib.Event.getXY(e);
27343         if(this.overlay){
27344             this.overlay.hide();
27345         }
27346         var newSize;
27347         if(this.orientation == Roo.SplitBar.HORIZONTAL){
27348             newSize = this.dragSpecs.startSize + 
27349                 (this.placement == Roo.SplitBar.LEFT ?
27350                     endPoint[0] - this.dragSpecs.startPoint[0] :
27351                     this.dragSpecs.startPoint[0] - endPoint[0]
27352                 );
27353         }else{
27354             newSize = this.dragSpecs.startSize + 
27355                 (this.placement == Roo.SplitBar.TOP ?
27356                     endPoint[1] - this.dragSpecs.startPoint[1] :
27357                     this.dragSpecs.startPoint[1] - endPoint[1]
27358                 );
27359         }
27360         newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
27361         if(newSize != this.dragSpecs.startSize){
27362             if(this.fireEvent('beforeapply', this, newSize) !== false){
27363                 this.adapter.setElementSize(this, newSize);
27364                 this.fireEvent("moved", this, newSize);
27365                 this.fireEvent("resize", this, newSize);
27366             }
27367         }
27368     },
27369     
27370     /**
27371      * Get the adapter this SplitBar uses
27372      * @return The adapter object
27373      */
27374     getAdapter : function(){
27375         return this.adapter;
27376     },
27377     
27378     /**
27379      * Set the adapter this SplitBar uses
27380      * @param {Object} adapter A SplitBar adapter object
27381      */
27382     setAdapter : function(adapter){
27383         this.adapter = adapter;
27384         this.adapter.init(this);
27385     },
27386     
27387     /**
27388      * Gets the minimum size for the resizing element
27389      * @return {Number} The minimum size
27390      */
27391     getMinimumSize : function(){
27392         return this.minSize;
27393     },
27394     
27395     /**
27396      * Sets the minimum size for the resizing element
27397      * @param {Number} minSize The minimum size
27398      */
27399     setMinimumSize : function(minSize){
27400         this.minSize = minSize;
27401     },
27402     
27403     /**
27404      * Gets the maximum size for the resizing element
27405      * @return {Number} The maximum size
27406      */
27407     getMaximumSize : function(){
27408         return this.maxSize;
27409     },
27410     
27411     /**
27412      * Sets the maximum size for the resizing element
27413      * @param {Number} maxSize The maximum size
27414      */
27415     setMaximumSize : function(maxSize){
27416         this.maxSize = maxSize;
27417     },
27418     
27419     /**
27420      * Sets the initialize size for the resizing element
27421      * @param {Number} size The initial size
27422      */
27423     setCurrentSize : function(size){
27424         var oldAnimate = this.animate;
27425         this.animate = false;
27426         this.adapter.setElementSize(this, size);
27427         this.animate = oldAnimate;
27428     },
27429     
27430     /**
27431      * Destroy this splitbar. 
27432      * @param {Boolean} removeEl True to remove the element
27433      */
27434     destroy : function(removeEl){
27435         if(this.shim){
27436             this.shim.remove();
27437         }
27438         this.dd.unreg();
27439         this.proxy.parentNode.removeChild(this.proxy);
27440         if(removeEl){
27441             this.el.remove();
27442         }
27443     }
27444 });
27445
27446 /**
27447  * @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.
27448  */
27449 Roo.SplitBar.createProxy = function(dir){
27450     var proxy = new Roo.Element(document.createElement("div"));
27451     proxy.unselectable();
27452     var cls = 'x-splitbar-proxy';
27453     proxy.addClass(cls + ' ' + (dir == Roo.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
27454     document.body.appendChild(proxy.dom);
27455     return proxy.dom;
27456 };
27457
27458 /** 
27459  * @class Roo.SplitBar.BasicLayoutAdapter
27460  * Default Adapter. It assumes the splitter and resizing element are not positioned
27461  * elements and only gets/sets the width of the element. Generally used for table based layouts.
27462  */
27463 Roo.SplitBar.BasicLayoutAdapter = function(){
27464 };
27465
27466 Roo.SplitBar.BasicLayoutAdapter.prototype = {
27467     // do nothing for now
27468     init : function(s){
27469     
27470     },
27471     /**
27472      * Called before drag operations to get the current size of the resizing element. 
27473      * @param {Roo.SplitBar} s The SplitBar using this adapter
27474      */
27475      getElementSize : function(s){
27476         if(s.orientation == Roo.SplitBar.HORIZONTAL){
27477             return s.resizingEl.getWidth();
27478         }else{
27479             return s.resizingEl.getHeight();
27480         }
27481     },
27482     
27483     /**
27484      * Called after drag operations to set the size of the resizing element.
27485      * @param {Roo.SplitBar} s The SplitBar using this adapter
27486      * @param {Number} newSize The new size to set
27487      * @param {Function} onComplete A function to be invoked when resizing is complete
27488      */
27489     setElementSize : function(s, newSize, onComplete){
27490         if(s.orientation == Roo.SplitBar.HORIZONTAL){
27491             if(!s.animate){
27492                 s.resizingEl.setWidth(newSize);
27493                 if(onComplete){
27494                     onComplete(s, newSize);
27495                 }
27496             }else{
27497                 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
27498             }
27499         }else{
27500             
27501             if(!s.animate){
27502                 s.resizingEl.setHeight(newSize);
27503                 if(onComplete){
27504                     onComplete(s, newSize);
27505                 }
27506             }else{
27507                 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
27508             }
27509         }
27510     }
27511 };
27512
27513 /** 
27514  *@class Roo.SplitBar.AbsoluteLayoutAdapter
27515  * @extends Roo.SplitBar.BasicLayoutAdapter
27516  * Adapter that  moves the splitter element to align with the resized sizing element. 
27517  * Used with an absolute positioned SplitBar.
27518  * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
27519  * document.body, make sure you assign an id to the body element.
27520  */
27521 Roo.SplitBar.AbsoluteLayoutAdapter = function(container){
27522     this.basic = new Roo.SplitBar.BasicLayoutAdapter();
27523     this.container = Roo.get(container);
27524 };
27525
27526 Roo.SplitBar.AbsoluteLayoutAdapter.prototype = {
27527     init : function(s){
27528         this.basic.init(s);
27529     },
27530     
27531     getElementSize : function(s){
27532         return this.basic.getElementSize(s);
27533     },
27534     
27535     setElementSize : function(s, newSize, onComplete){
27536         this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
27537     },
27538     
27539     moveSplitter : function(s){
27540         var yes = Roo.SplitBar;
27541         switch(s.placement){
27542             case yes.LEFT:
27543                 s.el.setX(s.resizingEl.getRight());
27544                 break;
27545             case yes.RIGHT:
27546                 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
27547                 break;
27548             case yes.TOP:
27549                 s.el.setY(s.resizingEl.getBottom());
27550                 break;
27551             case yes.BOTTOM:
27552                 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
27553                 break;
27554         }
27555     }
27556 };
27557
27558 /**
27559  * Orientation constant - Create a vertical SplitBar
27560  * @static
27561  * @type Number
27562  */
27563 Roo.SplitBar.VERTICAL = 1;
27564
27565 /**
27566  * Orientation constant - Create a horizontal SplitBar
27567  * @static
27568  * @type Number
27569  */
27570 Roo.SplitBar.HORIZONTAL = 2;
27571
27572 /**
27573  * Placement constant - The resizing element is to the left of the splitter element
27574  * @static
27575  * @type Number
27576  */
27577 Roo.SplitBar.LEFT = 1;
27578
27579 /**
27580  * Placement constant - The resizing element is to the right of the splitter element
27581  * @static
27582  * @type Number
27583  */
27584 Roo.SplitBar.RIGHT = 2;
27585
27586 /**
27587  * Placement constant - The resizing element is positioned above the splitter element
27588  * @static
27589  * @type Number
27590  */
27591 Roo.SplitBar.TOP = 3;
27592
27593 /**
27594  * Placement constant - The resizing element is positioned under splitter element
27595  * @static
27596  * @type Number
27597  */
27598 Roo.SplitBar.BOTTOM = 4;
27599 /*
27600  * Based on:
27601  * Ext JS Library 1.1.1
27602  * Copyright(c) 2006-2007, Ext JS, LLC.
27603  *
27604  * Originally Released Under LGPL - original licence link has changed is not relivant.
27605  *
27606  * Fork - LGPL
27607  * <script type="text/javascript">
27608  */
27609
27610 /**
27611  * @class Roo.View
27612  * @extends Roo.util.Observable
27613  * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template. 
27614  * This class also supports single and multi selection modes. <br>
27615  * Create a data model bound view:
27616  <pre><code>
27617  var store = new Roo.data.Store(...);
27618
27619  var view = new Roo.View({
27620     el : "my-element",
27621     tpl : '&lt;div id="{0}"&gt;{2} - {1}&lt;/div&gt;', // auto create template
27622  
27623     singleSelect: true,
27624     selectedClass: "ydataview-selected",
27625     store: store
27626  });
27627
27628  // listen for node click?
27629  view.on("click", function(vw, index, node, e){
27630  alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
27631  });
27632
27633  // load XML data
27634  dataModel.load("foobar.xml");
27635  </code></pre>
27636  For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
27637  * <br><br>
27638  * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
27639  * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
27640  * 
27641  * Note: old style constructor is still suported (container, template, config)
27642  * 
27643  * @constructor
27644  * Create a new View
27645  * @param {Object} config The config object
27646  * 
27647  */
27648 Roo.View = function(config, depreciated_tpl, depreciated_config){
27649     
27650     this.parent = false;
27651     
27652     if (typeof(depreciated_tpl) == 'undefined') {
27653         // new way.. - universal constructor.
27654         Roo.apply(this, config);
27655         this.el  = Roo.get(this.el);
27656     } else {
27657         // old format..
27658         this.el  = Roo.get(config);
27659         this.tpl = depreciated_tpl;
27660         Roo.apply(this, depreciated_config);
27661     }
27662     this.wrapEl  = this.el.wrap().wrap();
27663     ///this.el = this.wrapEla.appendChild(document.createElement("div"));
27664     
27665     
27666     if(typeof(this.tpl) == "string"){
27667         this.tpl = new Roo.Template(this.tpl);
27668     } else {
27669         // support xtype ctors..
27670         this.tpl = new Roo.factory(this.tpl, Roo);
27671     }
27672     
27673     
27674     this.tpl.compile();
27675     
27676     /** @private */
27677     this.addEvents({
27678         /**
27679          * @event beforeclick
27680          * Fires before a click is processed. Returns false to cancel the default action.
27681          * @param {Roo.View} this
27682          * @param {Number} index The index of the target node
27683          * @param {HTMLElement} node The target node
27684          * @param {Roo.EventObject} e The raw event object
27685          */
27686             "beforeclick" : true,
27687         /**
27688          * @event click
27689          * Fires when a template node is clicked.
27690          * @param {Roo.View} this
27691          * @param {Number} index The index of the target node
27692          * @param {HTMLElement} node The target node
27693          * @param {Roo.EventObject} e The raw event object
27694          */
27695             "click" : true,
27696         /**
27697          * @event dblclick
27698          * Fires when a template node is double clicked.
27699          * @param {Roo.View} this
27700          * @param {Number} index The index of the target node
27701          * @param {HTMLElement} node The target node
27702          * @param {Roo.EventObject} e The raw event object
27703          */
27704             "dblclick" : true,
27705         /**
27706          * @event contextmenu
27707          * Fires when a template node is right clicked.
27708          * @param {Roo.View} this
27709          * @param {Number} index The index of the target node
27710          * @param {HTMLElement} node The target node
27711          * @param {Roo.EventObject} e The raw event object
27712          */
27713             "contextmenu" : true,
27714         /**
27715          * @event selectionchange
27716          * Fires when the selected nodes change.
27717          * @param {Roo.View} this
27718          * @param {Array} selections Array of the selected nodes
27719          */
27720             "selectionchange" : true,
27721     
27722         /**
27723          * @event beforeselect
27724          * Fires before a selection is made. If any handlers return false, the selection is cancelled.
27725          * @param {Roo.View} this
27726          * @param {HTMLElement} node The node to be selected
27727          * @param {Array} selections Array of currently selected nodes
27728          */
27729             "beforeselect" : true,
27730         /**
27731          * @event preparedata
27732          * Fires on every row to render, to allow you to change the data.
27733          * @param {Roo.View} this
27734          * @param {Object} data to be rendered (change this)
27735          */
27736           "preparedata" : true
27737           
27738           
27739         });
27740
27741
27742
27743     this.el.on({
27744         "click": this.onClick,
27745         "dblclick": this.onDblClick,
27746         "contextmenu": this.onContextMenu,
27747         scope:this
27748     });
27749
27750     this.selections = [];
27751     this.nodes = [];
27752     this.cmp = new Roo.CompositeElementLite([]);
27753     if(this.store){
27754         this.store = Roo.factory(this.store, Roo.data);
27755         this.setStore(this.store, true);
27756     }
27757     
27758     if ( this.footer && this.footer.xtype) {
27759            
27760          var fctr = this.wrapEl.appendChild(document.createElement("div"));
27761         
27762         this.footer.dataSource = this.store;
27763         this.footer.container = fctr;
27764         this.footer = Roo.factory(this.footer, Roo);
27765         fctr.insertFirst(this.el);
27766         
27767         // this is a bit insane - as the paging toolbar seems to detach the el..
27768 //        dom.parentNode.parentNode.parentNode
27769          // they get detached?
27770     }
27771     
27772     
27773     Roo.View.superclass.constructor.call(this);
27774     
27775     
27776 };
27777
27778 Roo.extend(Roo.View, Roo.util.Observable, {
27779     
27780      /**
27781      * @cfg {Roo.data.Store} store Data store to load data from.
27782      */
27783     store : false,
27784     
27785     /**
27786      * @cfg {String|Roo.Element} el The container element.
27787      */
27788     el : '',
27789     
27790     /**
27791      * @cfg {String|Roo.Template} tpl The template used by this View 
27792      */
27793     tpl : false,
27794     /**
27795      * @cfg {String} dataName the named area of the template to use as the data area
27796      *                          Works with domtemplates roo-name="name"
27797      */
27798     dataName: false,
27799     /**
27800      * @cfg {String} selectedClass The css class to add to selected nodes
27801      */
27802     selectedClass : "x-view-selected",
27803      /**
27804      * @cfg {String} emptyText The empty text to show when nothing is loaded.
27805      */
27806     emptyText : "",
27807     
27808     /**
27809      * @cfg {String} text to display on mask (default Loading)
27810      */
27811     mask : false,
27812     /**
27813      * @cfg {Boolean} multiSelect Allow multiple selection
27814      */
27815     multiSelect : false,
27816     /**
27817      * @cfg {Boolean} singleSelect Allow single selection
27818      */
27819     singleSelect:  false,
27820     
27821     /**
27822      * @cfg {Boolean} toggleSelect - selecting 
27823      */
27824     toggleSelect : false,
27825     
27826     /**
27827      * @cfg {Boolean} tickable - selecting 
27828      */
27829     tickable : false,
27830     
27831     /**
27832      * Returns the element this view is bound to.
27833      * @return {Roo.Element}
27834      */
27835     getEl : function(){
27836         return this.wrapEl;
27837     },
27838     
27839     
27840
27841     /**
27842      * Refreshes the view. - called by datachanged on the store. - do not call directly.
27843      */
27844     refresh : function(){
27845         //Roo.log('refresh');
27846         var t = this.tpl;
27847         
27848         // if we are using something like 'domtemplate', then
27849         // the what gets used is:
27850         // t.applySubtemplate(NAME, data, wrapping data..)
27851         // the outer template then get' applied with
27852         //     the store 'extra data'
27853         // and the body get's added to the
27854         //      roo-name="data" node?
27855         //      <span class='roo-tpl-{name}'></span> ?????
27856         
27857         
27858         
27859         this.clearSelections();
27860         this.el.update("");
27861         var html = [];
27862         var records = this.store.getRange();
27863         if(records.length < 1) {
27864             
27865             // is this valid??  = should it render a template??
27866             
27867             this.el.update(this.emptyText);
27868             return;
27869         }
27870         var el = this.el;
27871         if (this.dataName) {
27872             this.el.update(t.apply(this.store.meta)); //????
27873             el = this.el.child('.roo-tpl-' + this.dataName);
27874         }
27875         
27876         for(var i = 0, len = records.length; i < len; i++){
27877             var data = this.prepareData(records[i].data, i, records[i]);
27878             this.fireEvent("preparedata", this, data, i, records[i]);
27879             
27880             var d = Roo.apply({}, data);
27881             
27882             if(this.tickable){
27883                 Roo.apply(d, {'roo-id' : Roo.id()});
27884                 
27885                 var _this = this;
27886             
27887                 Roo.each(this.parent.item, function(item){
27888                     if(item[_this.parent.valueField] != data[_this.parent.valueField]){
27889                         return;
27890                     }
27891                     Roo.apply(d, {'roo-data-checked' : 'checked'});
27892                 });
27893             }
27894             
27895             html[html.length] = Roo.util.Format.trim(
27896                 this.dataName ?
27897                     t.applySubtemplate(this.dataName, d, this.store.meta) :
27898                     t.apply(d)
27899             );
27900         }
27901         
27902         
27903         
27904         el.update(html.join(""));
27905         this.nodes = el.dom.childNodes;
27906         this.updateIndexes(0);
27907     },
27908     
27909
27910     /**
27911      * Function to override to reformat the data that is sent to
27912      * the template for each node.
27913      * DEPRICATED - use the preparedata event handler.
27914      * @param {Array/Object} data The raw data (array of colData for a data model bound view or
27915      * a JSON object for an UpdateManager bound view).
27916      */
27917     prepareData : function(data, index, record)
27918     {
27919         this.fireEvent("preparedata", this, data, index, record);
27920         return data;
27921     },
27922
27923     onUpdate : function(ds, record){
27924         // Roo.log('on update');   
27925         this.clearSelections();
27926         var index = this.store.indexOf(record);
27927         var n = this.nodes[index];
27928         this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
27929         n.parentNode.removeChild(n);
27930         this.updateIndexes(index, index);
27931     },
27932
27933     
27934     
27935 // --------- FIXME     
27936     onAdd : function(ds, records, index)
27937     {
27938         //Roo.log(['on Add', ds, records, index] );        
27939         this.clearSelections();
27940         if(this.nodes.length == 0){
27941             this.refresh();
27942             return;
27943         }
27944         var n = this.nodes[index];
27945         for(var i = 0, len = records.length; i < len; i++){
27946             var d = this.prepareData(records[i].data, i, records[i]);
27947             if(n){
27948                 this.tpl.insertBefore(n, d);
27949             }else{
27950                 
27951                 this.tpl.append(this.el, d);
27952             }
27953         }
27954         this.updateIndexes(index);
27955     },
27956
27957     onRemove : function(ds, record, index){
27958        // Roo.log('onRemove');
27959         this.clearSelections();
27960         var el = this.dataName  ?
27961             this.el.child('.roo-tpl-' + this.dataName) :
27962             this.el; 
27963         
27964         el.dom.removeChild(this.nodes[index]);
27965         this.updateIndexes(index);
27966     },
27967
27968     /**
27969      * Refresh an individual node.
27970      * @param {Number} index
27971      */
27972     refreshNode : function(index){
27973         this.onUpdate(this.store, this.store.getAt(index));
27974     },
27975
27976     updateIndexes : function(startIndex, endIndex){
27977         var ns = this.nodes;
27978         startIndex = startIndex || 0;
27979         endIndex = endIndex || ns.length - 1;
27980         for(var i = startIndex; i <= endIndex; i++){
27981             ns[i].nodeIndex = i;
27982         }
27983     },
27984
27985     /**
27986      * Changes the data store this view uses and refresh the view.
27987      * @param {Store} store
27988      */
27989     setStore : function(store, initial){
27990         if(!initial && this.store){
27991             this.store.un("datachanged", this.refresh);
27992             this.store.un("add", this.onAdd);
27993             this.store.un("remove", this.onRemove);
27994             this.store.un("update", this.onUpdate);
27995             this.store.un("clear", this.refresh);
27996             this.store.un("beforeload", this.onBeforeLoad);
27997             this.store.un("load", this.onLoad);
27998             this.store.un("loadexception", this.onLoad);
27999         }
28000         if(store){
28001           
28002             store.on("datachanged", this.refresh, this);
28003             store.on("add", this.onAdd, this);
28004             store.on("remove", this.onRemove, this);
28005             store.on("update", this.onUpdate, this);
28006             store.on("clear", this.refresh, this);
28007             store.on("beforeload", this.onBeforeLoad, this);
28008             store.on("load", this.onLoad, this);
28009             store.on("loadexception", this.onLoad, this);
28010         }
28011         
28012         if(store){
28013             this.refresh();
28014         }
28015     },
28016     /**
28017      * onbeforeLoad - masks the loading area.
28018      *
28019      */
28020     onBeforeLoad : function(store,opts)
28021     {
28022          //Roo.log('onBeforeLoad');   
28023         if (!opts.add) {
28024             this.el.update("");
28025         }
28026         this.el.mask(this.mask ? this.mask : "Loading" ); 
28027     },
28028     onLoad : function ()
28029     {
28030         this.el.unmask();
28031     },
28032     
28033
28034     /**
28035      * Returns the template node the passed child belongs to or null if it doesn't belong to one.
28036      * @param {HTMLElement} node
28037      * @return {HTMLElement} The template node
28038      */
28039     findItemFromChild : function(node){
28040         var el = this.dataName  ?
28041             this.el.child('.roo-tpl-' + this.dataName,true) :
28042             this.el.dom; 
28043         
28044         if(!node || node.parentNode == el){
28045                     return node;
28046             }
28047             var p = node.parentNode;
28048             while(p && p != el){
28049             if(p.parentNode == el){
28050                 return p;
28051             }
28052             p = p.parentNode;
28053         }
28054             return null;
28055     },
28056
28057     /** @ignore */
28058     onClick : function(e){
28059         var item = this.findItemFromChild(e.getTarget());
28060         if(item){
28061             var index = this.indexOf(item);
28062             if(this.onItemClick(item, index, e) !== false){
28063                 this.fireEvent("click", this, index, item, e);
28064             }
28065         }else{
28066             this.clearSelections();
28067         }
28068     },
28069
28070     /** @ignore */
28071     onContextMenu : function(e){
28072         var item = this.findItemFromChild(e.getTarget());
28073         if(item){
28074             this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
28075         }
28076     },
28077
28078     /** @ignore */
28079     onDblClick : function(e){
28080         var item = this.findItemFromChild(e.getTarget());
28081         if(item){
28082             this.fireEvent("dblclick", this, this.indexOf(item), item, e);
28083         }
28084     },
28085
28086     onItemClick : function(item, index, e)
28087     {
28088         if(this.fireEvent("beforeclick", this, index, item, e) === false){
28089             return false;
28090         }
28091         if (this.toggleSelect) {
28092             var m = this.isSelected(item) ? 'unselect' : 'select';
28093             //Roo.log(m);
28094             var _t = this;
28095             _t[m](item, true, false);
28096             return true;
28097         }
28098         if(this.multiSelect || this.singleSelect){
28099             if(this.multiSelect && e.shiftKey && this.lastSelection){
28100                 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
28101             }else{
28102                 this.select(item, this.multiSelect && e.ctrlKey);
28103                 this.lastSelection = item;
28104             }
28105             
28106             if(!this.tickable){
28107                 e.preventDefault();
28108             }
28109             
28110         }
28111         return true;
28112     },
28113
28114     /**
28115      * Get the number of selected nodes.
28116      * @return {Number}
28117      */
28118     getSelectionCount : function(){
28119         return this.selections.length;
28120     },
28121
28122     /**
28123      * Get the currently selected nodes.
28124      * @return {Array} An array of HTMLElements
28125      */
28126     getSelectedNodes : function(){
28127         return this.selections;
28128     },
28129
28130     /**
28131      * Get the indexes of the selected nodes.
28132      * @return {Array}
28133      */
28134     getSelectedIndexes : function(){
28135         var indexes = [], s = this.selections;
28136         for(var i = 0, len = s.length; i < len; i++){
28137             indexes.push(s[i].nodeIndex);
28138         }
28139         return indexes;
28140     },
28141
28142     /**
28143      * Clear all selections
28144      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
28145      */
28146     clearSelections : function(suppressEvent){
28147         if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
28148             this.cmp.elements = this.selections;
28149             this.cmp.removeClass(this.selectedClass);
28150             this.selections = [];
28151             if(!suppressEvent){
28152                 this.fireEvent("selectionchange", this, this.selections);
28153             }
28154         }
28155     },
28156
28157     /**
28158      * Returns true if the passed node is selected
28159      * @param {HTMLElement/Number} node The node or node index
28160      * @return {Boolean}
28161      */
28162     isSelected : function(node){
28163         var s = this.selections;
28164         if(s.length < 1){
28165             return false;
28166         }
28167         node = this.getNode(node);
28168         return s.indexOf(node) !== -1;
28169     },
28170
28171     /**
28172      * Selects nodes.
28173      * @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
28174      * @param {Boolean} keepExisting (optional) true to keep existing selections
28175      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
28176      */
28177     select : function(nodeInfo, keepExisting, suppressEvent){
28178         if(nodeInfo instanceof Array){
28179             if(!keepExisting){
28180                 this.clearSelections(true);
28181             }
28182             for(var i = 0, len = nodeInfo.length; i < len; i++){
28183                 this.select(nodeInfo[i], true, true);
28184             }
28185             return;
28186         } 
28187         var node = this.getNode(nodeInfo);
28188         if(!node || this.isSelected(node)){
28189             return; // already selected.
28190         }
28191         if(!keepExisting){
28192             this.clearSelections(true);
28193         }
28194         
28195         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
28196             Roo.fly(node).addClass(this.selectedClass);
28197             this.selections.push(node);
28198             if(!suppressEvent){
28199                 this.fireEvent("selectionchange", this, this.selections);
28200             }
28201         }
28202         
28203         
28204     },
28205       /**
28206      * Unselects nodes.
28207      * @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
28208      * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
28209      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
28210      */
28211     unselect : function(nodeInfo, keepExisting, suppressEvent)
28212     {
28213         if(nodeInfo instanceof Array){
28214             Roo.each(this.selections, function(s) {
28215                 this.unselect(s, nodeInfo);
28216             }, this);
28217             return;
28218         }
28219         var node = this.getNode(nodeInfo);
28220         if(!node || !this.isSelected(node)){
28221             //Roo.log("not selected");
28222             return; // not selected.
28223         }
28224         // fireevent???
28225         var ns = [];
28226         Roo.each(this.selections, function(s) {
28227             if (s == node ) {
28228                 Roo.fly(node).removeClass(this.selectedClass);
28229
28230                 return;
28231             }
28232             ns.push(s);
28233         },this);
28234         
28235         this.selections= ns;
28236         this.fireEvent("selectionchange", this, this.selections);
28237     },
28238
28239     /**
28240      * Gets a template node.
28241      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
28242      * @return {HTMLElement} The node or null if it wasn't found
28243      */
28244     getNode : function(nodeInfo){
28245         if(typeof nodeInfo == "string"){
28246             return document.getElementById(nodeInfo);
28247         }else if(typeof nodeInfo == "number"){
28248             return this.nodes[nodeInfo];
28249         }
28250         return nodeInfo;
28251     },
28252
28253     /**
28254      * Gets a range template nodes.
28255      * @param {Number} startIndex
28256      * @param {Number} endIndex
28257      * @return {Array} An array of nodes
28258      */
28259     getNodes : function(start, end){
28260         var ns = this.nodes;
28261         start = start || 0;
28262         end = typeof end == "undefined" ? ns.length - 1 : end;
28263         var nodes = [];
28264         if(start <= end){
28265             for(var i = start; i <= end; i++){
28266                 nodes.push(ns[i]);
28267             }
28268         } else{
28269             for(var i = start; i >= end; i--){
28270                 nodes.push(ns[i]);
28271             }
28272         }
28273         return nodes;
28274     },
28275
28276     /**
28277      * Finds the index of the passed node
28278      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
28279      * @return {Number} The index of the node or -1
28280      */
28281     indexOf : function(node){
28282         node = this.getNode(node);
28283         if(typeof node.nodeIndex == "number"){
28284             return node.nodeIndex;
28285         }
28286         var ns = this.nodes;
28287         for(var i = 0, len = ns.length; i < len; i++){
28288             if(ns[i] == node){
28289                 return i;
28290             }
28291         }
28292         return -1;
28293     }
28294 });
28295 /*
28296  * Based on:
28297  * Ext JS Library 1.1.1
28298  * Copyright(c) 2006-2007, Ext JS, LLC.
28299  *
28300  * Originally Released Under LGPL - original licence link has changed is not relivant.
28301  *
28302  * Fork - LGPL
28303  * <script type="text/javascript">
28304  */
28305
28306 /**
28307  * @class Roo.JsonView
28308  * @extends Roo.View
28309  * Shortcut class to create a JSON + {@link Roo.UpdateManager} template view. Usage:
28310 <pre><code>
28311 var view = new Roo.JsonView({
28312     container: "my-element",
28313     tpl: '&lt;div id="{id}"&gt;{foo} - {bar}&lt;/div&gt;', // auto create template
28314     multiSelect: true, 
28315     jsonRoot: "data" 
28316 });
28317
28318 // listen for node click?
28319 view.on("click", function(vw, index, node, e){
28320     alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
28321 });
28322
28323 // direct load of JSON data
28324 view.load("foobar.php");
28325
28326 // Example from my blog list
28327 var tpl = new Roo.Template(
28328     '&lt;div class="entry"&gt;' +
28329     '&lt;a class="entry-title" href="{link}"&gt;{title}&lt;/a&gt;' +
28330     "&lt;h4&gt;{date} by {author} | {comments} Comments&lt;/h4&gt;{description}" +
28331     "&lt;/div&gt;&lt;hr /&gt;"
28332 );
28333
28334 var moreView = new Roo.JsonView({
28335     container :  "entry-list", 
28336     template : tpl,
28337     jsonRoot: "posts"
28338 });
28339 moreView.on("beforerender", this.sortEntries, this);
28340 moreView.load({
28341     url: "/blog/get-posts.php",
28342     params: "allposts=true",
28343     text: "Loading Blog Entries..."
28344 });
28345 </code></pre>
28346
28347 * Note: old code is supported with arguments : (container, template, config)
28348
28349
28350  * @constructor
28351  * Create a new JsonView
28352  * 
28353  * @param {Object} config The config object
28354  * 
28355  */
28356 Roo.JsonView = function(config, depreciated_tpl, depreciated_config){
28357     
28358     
28359     Roo.JsonView.superclass.constructor.call(this, config, depreciated_tpl, depreciated_config);
28360
28361     var um = this.el.getUpdateManager();
28362     um.setRenderer(this);
28363     um.on("update", this.onLoad, this);
28364     um.on("failure", this.onLoadException, this);
28365
28366     /**
28367      * @event beforerender
28368      * Fires before rendering of the downloaded JSON data.
28369      * @param {Roo.JsonView} this
28370      * @param {Object} data The JSON data loaded
28371      */
28372     /**
28373      * @event load
28374      * Fires when data is loaded.
28375      * @param {Roo.JsonView} this
28376      * @param {Object} data The JSON data loaded
28377      * @param {Object} response The raw Connect response object
28378      */
28379     /**
28380      * @event loadexception
28381      * Fires when loading fails.
28382      * @param {Roo.JsonView} this
28383      * @param {Object} response The raw Connect response object
28384      */
28385     this.addEvents({
28386         'beforerender' : true,
28387         'load' : true,
28388         'loadexception' : true
28389     });
28390 };
28391 Roo.extend(Roo.JsonView, Roo.View, {
28392     /**
28393      * @type {String} The root property in the loaded JSON object that contains the data
28394      */
28395     jsonRoot : "",
28396
28397     /**
28398      * Refreshes the view.
28399      */
28400     refresh : function(){
28401         this.clearSelections();
28402         this.el.update("");
28403         var html = [];
28404         var o = this.jsonData;
28405         if(o && o.length > 0){
28406             for(var i = 0, len = o.length; i < len; i++){
28407                 var data = this.prepareData(o[i], i, o);
28408                 html[html.length] = this.tpl.apply(data);
28409             }
28410         }else{
28411             html.push(this.emptyText);
28412         }
28413         this.el.update(html.join(""));
28414         this.nodes = this.el.dom.childNodes;
28415         this.updateIndexes(0);
28416     },
28417
28418     /**
28419      * 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.
28420      * @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:
28421      <pre><code>
28422      view.load({
28423          url: "your-url.php",
28424          params: {param1: "foo", param2: "bar"}, // or a URL encoded string
28425          callback: yourFunction,
28426          scope: yourObject, //(optional scope)
28427          discardUrl: false,
28428          nocache: false,
28429          text: "Loading...",
28430          timeout: 30,
28431          scripts: false
28432      });
28433      </code></pre>
28434      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
28435      * 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.
28436      * @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}
28437      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess)
28438      * @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.
28439      */
28440     load : function(){
28441         var um = this.el.getUpdateManager();
28442         um.update.apply(um, arguments);
28443     },
28444
28445     // note - render is a standard framework call...
28446     // using it for the response is really flaky... - it's called by UpdateManager normally, except when called by the XComponent/addXtype.
28447     render : function(el, response){
28448         
28449         this.clearSelections();
28450         this.el.update("");
28451         var o;
28452         try{
28453             if (response != '') {
28454                 o = Roo.util.JSON.decode(response.responseText);
28455                 if(this.jsonRoot){
28456                     
28457                     o = o[this.jsonRoot];
28458                 }
28459             }
28460         } catch(e){
28461         }
28462         /**
28463          * The current JSON data or null
28464          */
28465         this.jsonData = o;
28466         this.beforeRender();
28467         this.refresh();
28468     },
28469
28470 /**
28471  * Get the number of records in the current JSON dataset
28472  * @return {Number}
28473  */
28474     getCount : function(){
28475         return this.jsonData ? this.jsonData.length : 0;
28476     },
28477
28478 /**
28479  * Returns the JSON object for the specified node(s)
28480  * @param {HTMLElement/Array} node The node or an array of nodes
28481  * @return {Object/Array} If you pass in an array, you get an array back, otherwise
28482  * you get the JSON object for the node
28483  */
28484     getNodeData : function(node){
28485         if(node instanceof Array){
28486             var data = [];
28487             for(var i = 0, len = node.length; i < len; i++){
28488                 data.push(this.getNodeData(node[i]));
28489             }
28490             return data;
28491         }
28492         return this.jsonData[this.indexOf(node)] || null;
28493     },
28494
28495     beforeRender : function(){
28496         this.snapshot = this.jsonData;
28497         if(this.sortInfo){
28498             this.sort.apply(this, this.sortInfo);
28499         }
28500         this.fireEvent("beforerender", this, this.jsonData);
28501     },
28502
28503     onLoad : function(el, o){
28504         this.fireEvent("load", this, this.jsonData, o);
28505     },
28506
28507     onLoadException : function(el, o){
28508         this.fireEvent("loadexception", this, o);
28509     },
28510
28511 /**
28512  * Filter the data by a specific property.
28513  * @param {String} property A property on your JSON objects
28514  * @param {String/RegExp} value Either string that the property values
28515  * should start with, or a RegExp to test against the property
28516  */
28517     filter : function(property, value){
28518         if(this.jsonData){
28519             var data = [];
28520             var ss = this.snapshot;
28521             if(typeof value == "string"){
28522                 var vlen = value.length;
28523                 if(vlen == 0){
28524                     this.clearFilter();
28525                     return;
28526                 }
28527                 value = value.toLowerCase();
28528                 for(var i = 0, len = ss.length; i < len; i++){
28529                     var o = ss[i];
28530                     if(o[property].substr(0, vlen).toLowerCase() == value){
28531                         data.push(o);
28532                     }
28533                 }
28534             } else if(value.exec){ // regex?
28535                 for(var i = 0, len = ss.length; i < len; i++){
28536                     var o = ss[i];
28537                     if(value.test(o[property])){
28538                         data.push(o);
28539                     }
28540                 }
28541             } else{
28542                 return;
28543             }
28544             this.jsonData = data;
28545             this.refresh();
28546         }
28547     },
28548
28549 /**
28550  * Filter by a function. The passed function will be called with each
28551  * object in the current dataset. If the function returns true the value is kept,
28552  * otherwise it is filtered.
28553  * @param {Function} fn
28554  * @param {Object} scope (optional) The scope of the function (defaults to this JsonView)
28555  */
28556     filterBy : function(fn, scope){
28557         if(this.jsonData){
28558             var data = [];
28559             var ss = this.snapshot;
28560             for(var i = 0, len = ss.length; i < len; i++){
28561                 var o = ss[i];
28562                 if(fn.call(scope || this, o)){
28563                     data.push(o);
28564                 }
28565             }
28566             this.jsonData = data;
28567             this.refresh();
28568         }
28569     },
28570
28571 /**
28572  * Clears the current filter.
28573  */
28574     clearFilter : function(){
28575         if(this.snapshot && this.jsonData != this.snapshot){
28576             this.jsonData = this.snapshot;
28577             this.refresh();
28578         }
28579     },
28580
28581
28582 /**
28583  * Sorts the data for this view and refreshes it.
28584  * @param {String} property A property on your JSON objects to sort on
28585  * @param {String} direction (optional) "desc" or "asc" (defaults to "asc")
28586  * @param {Function} sortType (optional) A function to call to convert the data to a sortable value.
28587  */
28588     sort : function(property, dir, sortType){
28589         this.sortInfo = Array.prototype.slice.call(arguments, 0);
28590         if(this.jsonData){
28591             var p = property;
28592             var dsc = dir && dir.toLowerCase() == "desc";
28593             var f = function(o1, o2){
28594                 var v1 = sortType ? sortType(o1[p]) : o1[p];
28595                 var v2 = sortType ? sortType(o2[p]) : o2[p];
28596                 ;
28597                 if(v1 < v2){
28598                     return dsc ? +1 : -1;
28599                 } else if(v1 > v2){
28600                     return dsc ? -1 : +1;
28601                 } else{
28602                     return 0;
28603                 }
28604             };
28605             this.jsonData.sort(f);
28606             this.refresh();
28607             if(this.jsonData != this.snapshot){
28608                 this.snapshot.sort(f);
28609             }
28610         }
28611     }
28612 });/*
28613  * Based on:
28614  * Ext JS Library 1.1.1
28615  * Copyright(c) 2006-2007, Ext JS, LLC.
28616  *
28617  * Originally Released Under LGPL - original licence link has changed is not relivant.
28618  *
28619  * Fork - LGPL
28620  * <script type="text/javascript">
28621  */
28622  
28623
28624 /**
28625  * @class Roo.ColorPalette
28626  * @extends Roo.Component
28627  * Simple color palette class for choosing colors.  The palette can be rendered to any container.<br />
28628  * Here's an example of typical usage:
28629  * <pre><code>
28630 var cp = new Roo.ColorPalette({value:'993300'});  // initial selected color
28631 cp.render('my-div');
28632
28633 cp.on('select', function(palette, selColor){
28634     // do something with selColor
28635 });
28636 </code></pre>
28637  * @constructor
28638  * Create a new ColorPalette
28639  * @param {Object} config The config object
28640  */
28641 Roo.ColorPalette = function(config){
28642     Roo.ColorPalette.superclass.constructor.call(this, config);
28643     this.addEvents({
28644         /**
28645              * @event select
28646              * Fires when a color is selected
28647              * @param {ColorPalette} this
28648              * @param {String} color The 6-digit color hex code (without the # symbol)
28649              */
28650         select: true
28651     });
28652
28653     if(this.handler){
28654         this.on("select", this.handler, this.scope, true);
28655     }
28656 };
28657 Roo.extend(Roo.ColorPalette, Roo.Component, {
28658     /**
28659      * @cfg {String} itemCls
28660      * The CSS class to apply to the containing element (defaults to "x-color-palette")
28661      */
28662     itemCls : "x-color-palette",
28663     /**
28664      * @cfg {String} value
28665      * The initial color to highlight (should be a valid 6-digit color hex code without the # symbol).  Note that
28666      * the hex codes are case-sensitive.
28667      */
28668     value : null,
28669     clickEvent:'click',
28670     // private
28671     ctype: "Roo.ColorPalette",
28672
28673     /**
28674      * @cfg {Boolean} allowReselect If set to true then reselecting a color that is already selected fires the selection event
28675      */
28676     allowReselect : false,
28677
28678     /**
28679      * <p>An array of 6-digit color hex code strings (without the # symbol).  This array can contain any number
28680      * of colors, and each hex code should be unique.  The width of the palette is controlled via CSS by adjusting
28681      * the width property of the 'x-color-palette' class (or assigning a custom class), so you can balance the number
28682      * of colors with the width setting until the box is symmetrical.</p>
28683      * <p>You can override individual colors if needed:</p>
28684      * <pre><code>
28685 var cp = new Roo.ColorPalette();
28686 cp.colors[0] = "FF0000";  // change the first box to red
28687 </code></pre>
28688
28689 Or you can provide a custom array of your own for complete control:
28690 <pre><code>
28691 var cp = new Roo.ColorPalette();
28692 cp.colors = ["000000", "993300", "333300"];
28693 </code></pre>
28694      * @type Array
28695      */
28696     colors : [
28697         "000000", "993300", "333300", "003300", "003366", "000080", "333399", "333333",
28698         "800000", "FF6600", "808000", "008000", "008080", "0000FF", "666699", "808080",
28699         "FF0000", "FF9900", "99CC00", "339966", "33CCCC", "3366FF", "800080", "969696",
28700         "FF00FF", "FFCC00", "FFFF00", "00FF00", "00FFFF", "00CCFF", "993366", "C0C0C0",
28701         "FF99CC", "FFCC99", "FFFF99", "CCFFCC", "CCFFFF", "99CCFF", "CC99FF", "FFFFFF"
28702     ],
28703
28704     // private
28705     onRender : function(container, position){
28706         var t = new Roo.MasterTemplate(
28707             '<tpl><a href="#" class="color-{0}" hidefocus="on"><em><span style="background:#{0}" unselectable="on">&#160;</span></em></a></tpl>'
28708         );
28709         var c = this.colors;
28710         for(var i = 0, len = c.length; i < len; i++){
28711             t.add([c[i]]);
28712         }
28713         var el = document.createElement("div");
28714         el.className = this.itemCls;
28715         t.overwrite(el);
28716         container.dom.insertBefore(el, position);
28717         this.el = Roo.get(el);
28718         this.el.on(this.clickEvent, this.handleClick,  this, {delegate: "a"});
28719         if(this.clickEvent != 'click'){
28720             this.el.on('click', Roo.emptyFn,  this, {delegate: "a", preventDefault:true});
28721         }
28722     },
28723
28724     // private
28725     afterRender : function(){
28726         Roo.ColorPalette.superclass.afterRender.call(this);
28727         if(this.value){
28728             var s = this.value;
28729             this.value = null;
28730             this.select(s);
28731         }
28732     },
28733
28734     // private
28735     handleClick : function(e, t){
28736         e.preventDefault();
28737         if(!this.disabled){
28738             var c = t.className.match(/(?:^|\s)color-(.{6})(?:\s|$)/)[1];
28739             this.select(c.toUpperCase());
28740         }
28741     },
28742
28743     /**
28744      * Selects the specified color in the palette (fires the select event)
28745      * @param {String} color A valid 6-digit color hex code (# will be stripped if included)
28746      */
28747     select : function(color){
28748         color = color.replace("#", "");
28749         if(color != this.value || this.allowReselect){
28750             var el = this.el;
28751             if(this.value){
28752                 el.child("a.color-"+this.value).removeClass("x-color-palette-sel");
28753             }
28754             el.child("a.color-"+color).addClass("x-color-palette-sel");
28755             this.value = color;
28756             this.fireEvent("select", this, color);
28757         }
28758     }
28759 });/*
28760  * Based on:
28761  * Ext JS Library 1.1.1
28762  * Copyright(c) 2006-2007, Ext JS, LLC.
28763  *
28764  * Originally Released Under LGPL - original licence link has changed is not relivant.
28765  *
28766  * Fork - LGPL
28767  * <script type="text/javascript">
28768  */
28769  
28770 /**
28771  * @class Roo.DatePicker
28772  * @extends Roo.Component
28773  * Simple date picker class.
28774  * @constructor
28775  * Create a new DatePicker
28776  * @param {Object} config The config object
28777  */
28778 Roo.DatePicker = function(config){
28779     Roo.DatePicker.superclass.constructor.call(this, config);
28780
28781     this.value = config && config.value ?
28782                  config.value.clearTime() : new Date().clearTime();
28783
28784     this.addEvents({
28785         /**
28786              * @event select
28787              * Fires when a date is selected
28788              * @param {DatePicker} this
28789              * @param {Date} date The selected date
28790              */
28791         'select': true,
28792         /**
28793              * @event monthchange
28794              * Fires when the displayed month changes 
28795              * @param {DatePicker} this
28796              * @param {Date} date The selected month
28797              */
28798         'monthchange': true
28799     });
28800
28801     if(this.handler){
28802         this.on("select", this.handler,  this.scope || this);
28803     }
28804     // build the disabledDatesRE
28805     if(!this.disabledDatesRE && this.disabledDates){
28806         var dd = this.disabledDates;
28807         var re = "(?:";
28808         for(var i = 0; i < dd.length; i++){
28809             re += dd[i];
28810             if(i != dd.length-1) {
28811                 re += "|";
28812             }
28813         }
28814         this.disabledDatesRE = new RegExp(re + ")");
28815     }
28816 };
28817
28818 Roo.extend(Roo.DatePicker, Roo.Component, {
28819     /**
28820      * @cfg {String} todayText
28821      * The text to display on the button that selects the current date (defaults to "Today")
28822      */
28823     todayText : "Today",
28824     /**
28825      * @cfg {String} okText
28826      * The text to display on the ok button
28827      */
28828     okText : "&#160;OK&#160;", // &#160; to give the user extra clicking room
28829     /**
28830      * @cfg {String} cancelText
28831      * The text to display on the cancel button
28832      */
28833     cancelText : "Cancel",
28834     /**
28835      * @cfg {String} todayTip
28836      * The tooltip to display for the button that selects the current date (defaults to "{current date} (Spacebar)")
28837      */
28838     todayTip : "{0} (Spacebar)",
28839     /**
28840      * @cfg {Date} minDate
28841      * Minimum allowable date (JavaScript date object, defaults to null)
28842      */
28843     minDate : null,
28844     /**
28845      * @cfg {Date} maxDate
28846      * Maximum allowable date (JavaScript date object, defaults to null)
28847      */
28848     maxDate : null,
28849     /**
28850      * @cfg {String} minText
28851      * The error text to display if the minDate validation fails (defaults to "This date is before the minimum date")
28852      */
28853     minText : "This date is before the minimum date",
28854     /**
28855      * @cfg {String} maxText
28856      * The error text to display if the maxDate validation fails (defaults to "This date is after the maximum date")
28857      */
28858     maxText : "This date is after the maximum date",
28859     /**
28860      * @cfg {String} format
28861      * The default date format string which can be overriden for localization support.  The format must be
28862      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
28863      */
28864     format : "m/d/y",
28865     /**
28866      * @cfg {Array} disabledDays
28867      * An array of days to disable, 0-based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
28868      */
28869     disabledDays : null,
28870     /**
28871      * @cfg {String} disabledDaysText
28872      * The tooltip to display when the date falls on a disabled day (defaults to "")
28873      */
28874     disabledDaysText : "",
28875     /**
28876      * @cfg {RegExp} disabledDatesRE
28877      * JavaScript regular expression used to disable a pattern of dates (defaults to null)
28878      */
28879     disabledDatesRE : null,
28880     /**
28881      * @cfg {String} disabledDatesText
28882      * The tooltip text to display when the date falls on a disabled date (defaults to "")
28883      */
28884     disabledDatesText : "",
28885     /**
28886      * @cfg {Boolean} constrainToViewport
28887      * True to constrain the date picker to the viewport (defaults to true)
28888      */
28889     constrainToViewport : true,
28890     /**
28891      * @cfg {Array} monthNames
28892      * An array of textual month names which can be overriden for localization support (defaults to Date.monthNames)
28893      */
28894     monthNames : Date.monthNames,
28895     /**
28896      * @cfg {Array} dayNames
28897      * An array of textual day names which can be overriden for localization support (defaults to Date.dayNames)
28898      */
28899     dayNames : Date.dayNames,
28900     /**
28901      * @cfg {String} nextText
28902      * The next month navigation button tooltip (defaults to 'Next Month (Control+Right)')
28903      */
28904     nextText: 'Next Month (Control+Right)',
28905     /**
28906      * @cfg {String} prevText
28907      * The previous month navigation button tooltip (defaults to 'Previous Month (Control+Left)')
28908      */
28909     prevText: 'Previous Month (Control+Left)',
28910     /**
28911      * @cfg {String} monthYearText
28912      * The header month selector tooltip (defaults to 'Choose a month (Control+Up/Down to move years)')
28913      */
28914     monthYearText: 'Choose a month (Control+Up/Down to move years)',
28915     /**
28916      * @cfg {Number} startDay
28917      * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
28918      */
28919     startDay : 0,
28920     /**
28921      * @cfg {Bool} showClear
28922      * Show a clear button (usefull for date form elements that can be blank.)
28923      */
28924     
28925     showClear: false,
28926     
28927     /**
28928      * Sets the value of the date field
28929      * @param {Date} value The date to set
28930      */
28931     setValue : function(value){
28932         var old = this.value;
28933         
28934         if (typeof(value) == 'string') {
28935          
28936             value = Date.parseDate(value, this.format);
28937         }
28938         if (!value) {
28939             value = new Date();
28940         }
28941         
28942         this.value = value.clearTime(true);
28943         if(this.el){
28944             this.update(this.value);
28945         }
28946     },
28947
28948     /**
28949      * Gets the current selected value of the date field
28950      * @return {Date} The selected date
28951      */
28952     getValue : function(){
28953         return this.value;
28954     },
28955
28956     // private
28957     focus : function(){
28958         if(this.el){
28959             this.update(this.activeDate);
28960         }
28961     },
28962
28963     // privateval
28964     onRender : function(container, position){
28965         
28966         var m = [
28967              '<table cellspacing="0">',
28968                 '<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>',
28969                 '<tr><td colspan="3"><table class="x-date-inner" cellspacing="0"><thead><tr>'];
28970         var dn = this.dayNames;
28971         for(var i = 0; i < 7; i++){
28972             var d = this.startDay+i;
28973             if(d > 6){
28974                 d = d-7;
28975             }
28976             m.push("<th><span>", dn[d].substr(0,1), "</span></th>");
28977         }
28978         m[m.length] = "</tr></thead><tbody><tr>";
28979         for(var i = 0; i < 42; i++) {
28980             if(i % 7 == 0 && i != 0){
28981                 m[m.length] = "</tr><tr>";
28982             }
28983             m[m.length] = '<td><a href="#" hidefocus="on" class="x-date-date" tabIndex="1"><em><span></span></em></a></td>';
28984         }
28985         m[m.length] = '</tr></tbody></table></td></tr><tr>'+
28986             '<td colspan="3" class="x-date-bottom" align="center"></td></tr></table><div class="x-date-mp"></div>';
28987
28988         var el = document.createElement("div");
28989         el.className = "x-date-picker";
28990         el.innerHTML = m.join("");
28991
28992         container.dom.insertBefore(el, position);
28993
28994         this.el = Roo.get(el);
28995         this.eventEl = Roo.get(el.firstChild);
28996
28997         new Roo.util.ClickRepeater(this.el.child("td.x-date-left a"), {
28998             handler: this.showPrevMonth,
28999             scope: this,
29000             preventDefault:true,
29001             stopDefault:true
29002         });
29003
29004         new Roo.util.ClickRepeater(this.el.child("td.x-date-right a"), {
29005             handler: this.showNextMonth,
29006             scope: this,
29007             preventDefault:true,
29008             stopDefault:true
29009         });
29010
29011         this.eventEl.on("mousewheel", this.handleMouseWheel,  this);
29012
29013         this.monthPicker = this.el.down('div.x-date-mp');
29014         this.monthPicker.enableDisplayMode('block');
29015         
29016         var kn = new Roo.KeyNav(this.eventEl, {
29017             "left" : function(e){
29018                 e.ctrlKey ?
29019                     this.showPrevMonth() :
29020                     this.update(this.activeDate.add("d", -1));
29021             },
29022
29023             "right" : function(e){
29024                 e.ctrlKey ?
29025                     this.showNextMonth() :
29026                     this.update(this.activeDate.add("d", 1));
29027             },
29028
29029             "up" : function(e){
29030                 e.ctrlKey ?
29031                     this.showNextYear() :
29032                     this.update(this.activeDate.add("d", -7));
29033             },
29034
29035             "down" : function(e){
29036                 e.ctrlKey ?
29037                     this.showPrevYear() :
29038                     this.update(this.activeDate.add("d", 7));
29039             },
29040
29041             "pageUp" : function(e){
29042                 this.showNextMonth();
29043             },
29044
29045             "pageDown" : function(e){
29046                 this.showPrevMonth();
29047             },
29048
29049             "enter" : function(e){
29050                 e.stopPropagation();
29051                 return true;
29052             },
29053
29054             scope : this
29055         });
29056
29057         this.eventEl.on("click", this.handleDateClick,  this, {delegate: "a.x-date-date"});
29058
29059         this.eventEl.addKeyListener(Roo.EventObject.SPACE, this.selectToday,  this);
29060
29061         this.el.unselectable();
29062         
29063         this.cells = this.el.select("table.x-date-inner tbody td");
29064         this.textNodes = this.el.query("table.x-date-inner tbody span");
29065
29066         this.mbtn = new Roo.Button(this.el.child("td.x-date-middle", true), {
29067             text: "&#160;",
29068             tooltip: this.monthYearText
29069         });
29070
29071         this.mbtn.on('click', this.showMonthPicker, this);
29072         this.mbtn.el.child(this.mbtn.menuClassTarget).addClass("x-btn-with-menu");
29073
29074
29075         var today = (new Date()).dateFormat(this.format);
29076         
29077         var baseTb = new Roo.Toolbar(this.el.child("td.x-date-bottom", true));
29078         if (this.showClear) {
29079             baseTb.add( new Roo.Toolbar.Fill());
29080         }
29081         baseTb.add({
29082             text: String.format(this.todayText, today),
29083             tooltip: String.format(this.todayTip, today),
29084             handler: this.selectToday,
29085             scope: this
29086         });
29087         
29088         //var todayBtn = new Roo.Button(this.el.child("td.x-date-bottom", true), {
29089             
29090         //});
29091         if (this.showClear) {
29092             
29093             baseTb.add( new Roo.Toolbar.Fill());
29094             baseTb.add({
29095                 text: '&#160;',
29096                 cls: 'x-btn-icon x-btn-clear',
29097                 handler: function() {
29098                     //this.value = '';
29099                     this.fireEvent("select", this, '');
29100                 },
29101                 scope: this
29102             });
29103         }
29104         
29105         
29106         if(Roo.isIE){
29107             this.el.repaint();
29108         }
29109         this.update(this.value);
29110     },
29111
29112     createMonthPicker : function(){
29113         if(!this.monthPicker.dom.firstChild){
29114             var buf = ['<table border="0" cellspacing="0">'];
29115             for(var i = 0; i < 6; i++){
29116                 buf.push(
29117                     '<tr><td class="x-date-mp-month"><a href="#">', this.monthNames[i].substr(0, 3), '</a></td>',
29118                     '<td class="x-date-mp-month x-date-mp-sep"><a href="#">', this.monthNames[i+6].substr(0, 3), '</a></td>',
29119                     i == 0 ?
29120                     '<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>' :
29121                     '<td class="x-date-mp-year"><a href="#"></a></td><td class="x-date-mp-year"><a href="#"></a></td></tr>'
29122                 );
29123             }
29124             buf.push(
29125                 '<tr class="x-date-mp-btns"><td colspan="4"><button type="button" class="x-date-mp-ok">',
29126                     this.okText,
29127                     '</button><button type="button" class="x-date-mp-cancel">',
29128                     this.cancelText,
29129                     '</button></td></tr>',
29130                 '</table>'
29131             );
29132             this.monthPicker.update(buf.join(''));
29133             this.monthPicker.on('click', this.onMonthClick, this);
29134             this.monthPicker.on('dblclick', this.onMonthDblClick, this);
29135
29136             this.mpMonths = this.monthPicker.select('td.x-date-mp-month');
29137             this.mpYears = this.monthPicker.select('td.x-date-mp-year');
29138
29139             this.mpMonths.each(function(m, a, i){
29140                 i += 1;
29141                 if((i%2) == 0){
29142                     m.dom.xmonth = 5 + Math.round(i * .5);
29143                 }else{
29144                     m.dom.xmonth = Math.round((i-1) * .5);
29145                 }
29146             });
29147         }
29148     },
29149
29150     showMonthPicker : function(){
29151         this.createMonthPicker();
29152         var size = this.el.getSize();
29153         this.monthPicker.setSize(size);
29154         this.monthPicker.child('table').setSize(size);
29155
29156         this.mpSelMonth = (this.activeDate || this.value).getMonth();
29157         this.updateMPMonth(this.mpSelMonth);
29158         this.mpSelYear = (this.activeDate || this.value).getFullYear();
29159         this.updateMPYear(this.mpSelYear);
29160
29161         this.monthPicker.slideIn('t', {duration:.2});
29162     },
29163
29164     updateMPYear : function(y){
29165         this.mpyear = y;
29166         var ys = this.mpYears.elements;
29167         for(var i = 1; i <= 10; i++){
29168             var td = ys[i-1], y2;
29169             if((i%2) == 0){
29170                 y2 = y + Math.round(i * .5);
29171                 td.firstChild.innerHTML = y2;
29172                 td.xyear = y2;
29173             }else{
29174                 y2 = y - (5-Math.round(i * .5));
29175                 td.firstChild.innerHTML = y2;
29176                 td.xyear = y2;
29177             }
29178             this.mpYears.item(i-1)[y2 == this.mpSelYear ? 'addClass' : 'removeClass']('x-date-mp-sel');
29179         }
29180     },
29181
29182     updateMPMonth : function(sm){
29183         this.mpMonths.each(function(m, a, i){
29184             m[m.dom.xmonth == sm ? 'addClass' : 'removeClass']('x-date-mp-sel');
29185         });
29186     },
29187
29188     selectMPMonth: function(m){
29189         
29190     },
29191
29192     onMonthClick : function(e, t){
29193         e.stopEvent();
29194         var el = new Roo.Element(t), pn;
29195         if(el.is('button.x-date-mp-cancel')){
29196             this.hideMonthPicker();
29197         }
29198         else if(el.is('button.x-date-mp-ok')){
29199             this.update(new Date(this.mpSelYear, this.mpSelMonth, (this.activeDate || this.value).getDate()));
29200             this.hideMonthPicker();
29201         }
29202         else if(pn = el.up('td.x-date-mp-month', 2)){
29203             this.mpMonths.removeClass('x-date-mp-sel');
29204             pn.addClass('x-date-mp-sel');
29205             this.mpSelMonth = pn.dom.xmonth;
29206         }
29207         else if(pn = el.up('td.x-date-mp-year', 2)){
29208             this.mpYears.removeClass('x-date-mp-sel');
29209             pn.addClass('x-date-mp-sel');
29210             this.mpSelYear = pn.dom.xyear;
29211         }
29212         else if(el.is('a.x-date-mp-prev')){
29213             this.updateMPYear(this.mpyear-10);
29214         }
29215         else if(el.is('a.x-date-mp-next')){
29216             this.updateMPYear(this.mpyear+10);
29217         }
29218     },
29219
29220     onMonthDblClick : function(e, t){
29221         e.stopEvent();
29222         var el = new Roo.Element(t), pn;
29223         if(pn = el.up('td.x-date-mp-month', 2)){
29224             this.update(new Date(this.mpSelYear, pn.dom.xmonth, (this.activeDate || this.value).getDate()));
29225             this.hideMonthPicker();
29226         }
29227         else if(pn = el.up('td.x-date-mp-year', 2)){
29228             this.update(new Date(pn.dom.xyear, this.mpSelMonth, (this.activeDate || this.value).getDate()));
29229             this.hideMonthPicker();
29230         }
29231     },
29232
29233     hideMonthPicker : function(disableAnim){
29234         if(this.monthPicker){
29235             if(disableAnim === true){
29236                 this.monthPicker.hide();
29237             }else{
29238                 this.monthPicker.slideOut('t', {duration:.2});
29239             }
29240         }
29241     },
29242
29243     // private
29244     showPrevMonth : function(e){
29245         this.update(this.activeDate.add("mo", -1));
29246     },
29247
29248     // private
29249     showNextMonth : function(e){
29250         this.update(this.activeDate.add("mo", 1));
29251     },
29252
29253     // private
29254     showPrevYear : function(){
29255         this.update(this.activeDate.add("y", -1));
29256     },
29257
29258     // private
29259     showNextYear : function(){
29260         this.update(this.activeDate.add("y", 1));
29261     },
29262
29263     // private
29264     handleMouseWheel : function(e){
29265         var delta = e.getWheelDelta();
29266         if(delta > 0){
29267             this.showPrevMonth();
29268             e.stopEvent();
29269         } else if(delta < 0){
29270             this.showNextMonth();
29271             e.stopEvent();
29272         }
29273     },
29274
29275     // private
29276     handleDateClick : function(e, t){
29277         e.stopEvent();
29278         if(t.dateValue && !Roo.fly(t.parentNode).hasClass("x-date-disabled")){
29279             this.setValue(new Date(t.dateValue));
29280             this.fireEvent("select", this, this.value);
29281         }
29282     },
29283
29284     // private
29285     selectToday : function(){
29286         this.setValue(new Date().clearTime());
29287         this.fireEvent("select", this, this.value);
29288     },
29289
29290     // private
29291     update : function(date)
29292     {
29293         var vd = this.activeDate;
29294         this.activeDate = date;
29295         if(vd && this.el){
29296             var t = date.getTime();
29297             if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
29298                 this.cells.removeClass("x-date-selected");
29299                 this.cells.each(function(c){
29300                    if(c.dom.firstChild.dateValue == t){
29301                        c.addClass("x-date-selected");
29302                        setTimeout(function(){
29303                             try{c.dom.firstChild.focus();}catch(e){}
29304                        }, 50);
29305                        return false;
29306                    }
29307                 });
29308                 return;
29309             }
29310         }
29311         
29312         var days = date.getDaysInMonth();
29313         var firstOfMonth = date.getFirstDateOfMonth();
29314         var startingPos = firstOfMonth.getDay()-this.startDay;
29315
29316         if(startingPos <= this.startDay){
29317             startingPos += 7;
29318         }
29319
29320         var pm = date.add("mo", -1);
29321         var prevStart = pm.getDaysInMonth()-startingPos;
29322
29323         var cells = this.cells.elements;
29324         var textEls = this.textNodes;
29325         days += startingPos;
29326
29327         // convert everything to numbers so it's fast
29328         var day = 86400000;
29329         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
29330         var today = new Date().clearTime().getTime();
29331         var sel = date.clearTime().getTime();
29332         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
29333         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
29334         var ddMatch = this.disabledDatesRE;
29335         var ddText = this.disabledDatesText;
29336         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
29337         var ddaysText = this.disabledDaysText;
29338         var format = this.format;
29339
29340         var setCellClass = function(cal, cell){
29341             cell.title = "";
29342             var t = d.getTime();
29343             cell.firstChild.dateValue = t;
29344             if(t == today){
29345                 cell.className += " x-date-today";
29346                 cell.title = cal.todayText;
29347             }
29348             if(t == sel){
29349                 cell.className += " x-date-selected";
29350                 setTimeout(function(){
29351                     try{cell.firstChild.focus();}catch(e){}
29352                 }, 50);
29353             }
29354             // disabling
29355             if(t < min) {
29356                 cell.className = " x-date-disabled";
29357                 cell.title = cal.minText;
29358                 return;
29359             }
29360             if(t > max) {
29361                 cell.className = " x-date-disabled";
29362                 cell.title = cal.maxText;
29363                 return;
29364             }
29365             if(ddays){
29366                 if(ddays.indexOf(d.getDay()) != -1){
29367                     cell.title = ddaysText;
29368                     cell.className = " x-date-disabled";
29369                 }
29370             }
29371             if(ddMatch && format){
29372                 var fvalue = d.dateFormat(format);
29373                 if(ddMatch.test(fvalue)){
29374                     cell.title = ddText.replace("%0", fvalue);
29375                     cell.className = " x-date-disabled";
29376                 }
29377             }
29378         };
29379
29380         var i = 0;
29381         for(; i < startingPos; i++) {
29382             textEls[i].innerHTML = (++prevStart);
29383             d.setDate(d.getDate()+1);
29384             cells[i].className = "x-date-prevday";
29385             setCellClass(this, cells[i]);
29386         }
29387         for(; i < days; i++){
29388             intDay = i - startingPos + 1;
29389             textEls[i].innerHTML = (intDay);
29390             d.setDate(d.getDate()+1);
29391             cells[i].className = "x-date-active";
29392             setCellClass(this, cells[i]);
29393         }
29394         var extraDays = 0;
29395         for(; i < 42; i++) {
29396              textEls[i].innerHTML = (++extraDays);
29397              d.setDate(d.getDate()+1);
29398              cells[i].className = "x-date-nextday";
29399              setCellClass(this, cells[i]);
29400         }
29401
29402         this.mbtn.setText(this.monthNames[date.getMonth()] + " " + date.getFullYear());
29403         this.fireEvent('monthchange', this, date);
29404         
29405         if(!this.internalRender){
29406             var main = this.el.dom.firstChild;
29407             var w = main.offsetWidth;
29408             this.el.setWidth(w + this.el.getBorderWidth("lr"));
29409             Roo.fly(main).setWidth(w);
29410             this.internalRender = true;
29411             // opera does not respect the auto grow header center column
29412             // then, after it gets a width opera refuses to recalculate
29413             // without a second pass
29414             if(Roo.isOpera && !this.secondPass){
29415                 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
29416                 this.secondPass = true;
29417                 this.update.defer(10, this, [date]);
29418             }
29419         }
29420         
29421         
29422     }
29423 });        /*
29424  * Based on:
29425  * Ext JS Library 1.1.1
29426  * Copyright(c) 2006-2007, Ext JS, LLC.
29427  *
29428  * Originally Released Under LGPL - original licence link has changed is not relivant.
29429  *
29430  * Fork - LGPL
29431  * <script type="text/javascript">
29432  */
29433 /**
29434  * @class Roo.TabPanel
29435  * @extends Roo.util.Observable
29436  * A lightweight tab container.
29437  * <br><br>
29438  * Usage:
29439  * <pre><code>
29440 // basic tabs 1, built from existing content
29441 var tabs = new Roo.TabPanel("tabs1");
29442 tabs.addTab("script", "View Script");
29443 tabs.addTab("markup", "View Markup");
29444 tabs.activate("script");
29445
29446 // more advanced tabs, built from javascript
29447 var jtabs = new Roo.TabPanel("jtabs");
29448 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
29449
29450 // set up the UpdateManager
29451 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
29452 var updater = tab2.getUpdateManager();
29453 updater.setDefaultUrl("ajax1.htm");
29454 tab2.on('activate', updater.refresh, updater, true);
29455
29456 // Use setUrl for Ajax loading
29457 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
29458 tab3.setUrl("ajax2.htm", null, true);
29459
29460 // Disabled tab
29461 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
29462 tab4.disable();
29463
29464 jtabs.activate("jtabs-1");
29465  * </code></pre>
29466  * @constructor
29467  * Create a new TabPanel.
29468  * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
29469  * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
29470  */
29471 Roo.TabPanel = function(container, config){
29472     /**
29473     * The container element for this TabPanel.
29474     * @type Roo.Element
29475     */
29476     this.el = Roo.get(container, true);
29477     if(config){
29478         if(typeof config == "boolean"){
29479             this.tabPosition = config ? "bottom" : "top";
29480         }else{
29481             Roo.apply(this, config);
29482         }
29483     }
29484     if(this.tabPosition == "bottom"){
29485         this.bodyEl = Roo.get(this.createBody(this.el.dom));
29486         this.el.addClass("x-tabs-bottom");
29487     }
29488     this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
29489     this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
29490     this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
29491     if(Roo.isIE){
29492         Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
29493     }
29494     if(this.tabPosition != "bottom"){
29495         /** The body element that contains {@link Roo.TabPanelItem} bodies. +
29496          * @type Roo.Element
29497          */
29498         this.bodyEl = Roo.get(this.createBody(this.el.dom));
29499         this.el.addClass("x-tabs-top");
29500     }
29501     this.items = [];
29502
29503     this.bodyEl.setStyle("position", "relative");
29504
29505     this.active = null;
29506     this.activateDelegate = this.activate.createDelegate(this);
29507
29508     this.addEvents({
29509         /**
29510          * @event tabchange
29511          * Fires when the active tab changes
29512          * @param {Roo.TabPanel} this
29513          * @param {Roo.TabPanelItem} activePanel The new active tab
29514          */
29515         "tabchange": true,
29516         /**
29517          * @event beforetabchange
29518          * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
29519          * @param {Roo.TabPanel} this
29520          * @param {Object} e Set cancel to true on this object to cancel the tab change
29521          * @param {Roo.TabPanelItem} tab The tab being changed to
29522          */
29523         "beforetabchange" : true
29524     });
29525
29526     Roo.EventManager.onWindowResize(this.onResize, this);
29527     this.cpad = this.el.getPadding("lr");
29528     this.hiddenCount = 0;
29529
29530
29531     // toolbar on the tabbar support...
29532     if (this.toolbar) {
29533         var tcfg = this.toolbar;
29534         tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');  
29535         this.toolbar = new Roo.Toolbar(tcfg);
29536         if (Roo.isSafari) {
29537             var tbl = tcfg.container.child('table', true);
29538             tbl.setAttribute('width', '100%');
29539         }
29540         
29541     }
29542    
29543
29544
29545     Roo.TabPanel.superclass.constructor.call(this);
29546 };
29547
29548 Roo.extend(Roo.TabPanel, Roo.util.Observable, {
29549     /*
29550      *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
29551      */
29552     tabPosition : "top",
29553     /*
29554      *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
29555      */
29556     currentTabWidth : 0,
29557     /*
29558      *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
29559      */
29560     minTabWidth : 40,
29561     /*
29562      *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
29563      */
29564     maxTabWidth : 250,
29565     /*
29566      *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
29567      */
29568     preferredTabWidth : 175,
29569     /*
29570      *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
29571      */
29572     resizeTabs : false,
29573     /*
29574      *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
29575      */
29576     monitorResize : true,
29577     /*
29578      *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar. 
29579      */
29580     toolbar : false,
29581
29582     /**
29583      * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
29584      * @param {String} id The id of the div to use <b>or create</b>
29585      * @param {String} text The text for the tab
29586      * @param {String} content (optional) Content to put in the TabPanelItem body
29587      * @param {Boolean} closable (optional) True to create a close icon on the tab
29588      * @return {Roo.TabPanelItem} The created TabPanelItem
29589      */
29590     addTab : function(id, text, content, closable){
29591         var item = new Roo.TabPanelItem(this, id, text, closable);
29592         this.addTabItem(item);
29593         if(content){
29594             item.setContent(content);
29595         }
29596         return item;
29597     },
29598
29599     /**
29600      * Returns the {@link Roo.TabPanelItem} with the specified id/index
29601      * @param {String/Number} id The id or index of the TabPanelItem to fetch.
29602      * @return {Roo.TabPanelItem}
29603      */
29604     getTab : function(id){
29605         return this.items[id];
29606     },
29607
29608     /**
29609      * Hides the {@link Roo.TabPanelItem} with the specified id/index
29610      * @param {String/Number} id The id or index of the TabPanelItem to hide.
29611      */
29612     hideTab : function(id){
29613         var t = this.items[id];
29614         if(!t.isHidden()){
29615            t.setHidden(true);
29616            this.hiddenCount++;
29617            this.autoSizeTabs();
29618         }
29619     },
29620
29621     /**
29622      * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
29623      * @param {String/Number} id The id or index of the TabPanelItem to unhide.
29624      */
29625     unhideTab : function(id){
29626         var t = this.items[id];
29627         if(t.isHidden()){
29628            t.setHidden(false);
29629            this.hiddenCount--;
29630            this.autoSizeTabs();
29631         }
29632     },
29633
29634     /**
29635      * Adds an existing {@link Roo.TabPanelItem}.
29636      * @param {Roo.TabPanelItem} item The TabPanelItem to add
29637      */
29638     addTabItem : function(item){
29639         this.items[item.id] = item;
29640         this.items.push(item);
29641         if(this.resizeTabs){
29642            item.setWidth(this.currentTabWidth || this.preferredTabWidth);
29643            this.autoSizeTabs();
29644         }else{
29645             item.autoSize();
29646         }
29647     },
29648
29649     /**
29650      * Removes a {@link Roo.TabPanelItem}.
29651      * @param {String/Number} id The id or index of the TabPanelItem to remove.
29652      */
29653     removeTab : function(id){
29654         var items = this.items;
29655         var tab = items[id];
29656         if(!tab) { return; }
29657         var index = items.indexOf(tab);
29658         if(this.active == tab && items.length > 1){
29659             var newTab = this.getNextAvailable(index);
29660             if(newTab) {
29661                 newTab.activate();
29662             }
29663         }
29664         this.stripEl.dom.removeChild(tab.pnode.dom);
29665         if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
29666             this.bodyEl.dom.removeChild(tab.bodyEl.dom);
29667         }
29668         items.splice(index, 1);
29669         delete this.items[tab.id];
29670         tab.fireEvent("close", tab);
29671         tab.purgeListeners();
29672         this.autoSizeTabs();
29673     },
29674
29675     getNextAvailable : function(start){
29676         var items = this.items;
29677         var index = start;
29678         // look for a next tab that will slide over to
29679         // replace the one being removed
29680         while(index < items.length){
29681             var item = items[++index];
29682             if(item && !item.isHidden()){
29683                 return item;
29684             }
29685         }
29686         // if one isn't found select the previous tab (on the left)
29687         index = start;
29688         while(index >= 0){
29689             var item = items[--index];
29690             if(item && !item.isHidden()){
29691                 return item;
29692             }
29693         }
29694         return null;
29695     },
29696
29697     /**
29698      * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
29699      * @param {String/Number} id The id or index of the TabPanelItem to disable.
29700      */
29701     disableTab : function(id){
29702         var tab = this.items[id];
29703         if(tab && this.active != tab){
29704             tab.disable();
29705         }
29706     },
29707
29708     /**
29709      * Enables a {@link Roo.TabPanelItem} that is disabled.
29710      * @param {String/Number} id The id or index of the TabPanelItem to enable.
29711      */
29712     enableTab : function(id){
29713         var tab = this.items[id];
29714         tab.enable();
29715     },
29716
29717     /**
29718      * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
29719      * @param {String/Number} id The id or index of the TabPanelItem to activate.
29720      * @return {Roo.TabPanelItem} The TabPanelItem.
29721      */
29722     activate : function(id){
29723         var tab = this.items[id];
29724         if(!tab){
29725             return null;
29726         }
29727         if(tab == this.active || tab.disabled){
29728             return tab;
29729         }
29730         var e = {};
29731         this.fireEvent("beforetabchange", this, e, tab);
29732         if(e.cancel !== true && !tab.disabled){
29733             if(this.active){
29734                 this.active.hide();
29735             }
29736             this.active = this.items[id];
29737             this.active.show();
29738             this.fireEvent("tabchange", this, this.active);
29739         }
29740         return tab;
29741     },
29742
29743     /**
29744      * Gets the active {@link Roo.TabPanelItem}.
29745      * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
29746      */
29747     getActiveTab : function(){
29748         return this.active;
29749     },
29750
29751     /**
29752      * Updates the tab body element to fit the height of the container element
29753      * for overflow scrolling
29754      * @param {Number} targetHeight (optional) Override the starting height from the elements height
29755      */
29756     syncHeight : function(targetHeight){
29757         var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
29758         var bm = this.bodyEl.getMargins();
29759         var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
29760         this.bodyEl.setHeight(newHeight);
29761         return newHeight;
29762     },
29763
29764     onResize : function(){
29765         if(this.monitorResize){
29766             this.autoSizeTabs();
29767         }
29768     },
29769
29770     /**
29771      * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
29772      */
29773     beginUpdate : function(){
29774         this.updating = true;
29775     },
29776
29777     /**
29778      * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
29779      */
29780     endUpdate : function(){
29781         this.updating = false;
29782         this.autoSizeTabs();
29783     },
29784
29785     /**
29786      * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
29787      */
29788     autoSizeTabs : function(){
29789         var count = this.items.length;
29790         var vcount = count - this.hiddenCount;
29791         if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) {
29792             return;
29793         }
29794         var w = Math.max(this.el.getWidth() - this.cpad, 10);
29795         var availWidth = Math.floor(w / vcount);
29796         var b = this.stripBody;
29797         if(b.getWidth() > w){
29798             var tabs = this.items;
29799             this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
29800             if(availWidth < this.minTabWidth){
29801                 /*if(!this.sleft){    // incomplete scrolling code
29802                     this.createScrollButtons();
29803                 }
29804                 this.showScroll();
29805                 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
29806             }
29807         }else{
29808             if(this.currentTabWidth < this.preferredTabWidth){
29809                 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
29810             }
29811         }
29812     },
29813
29814     /**
29815      * Returns the number of tabs in this TabPanel.
29816      * @return {Number}
29817      */
29818      getCount : function(){
29819          return this.items.length;
29820      },
29821
29822     /**
29823      * Resizes all the tabs to the passed width
29824      * @param {Number} The new width
29825      */
29826     setTabWidth : function(width){
29827         this.currentTabWidth = width;
29828         for(var i = 0, len = this.items.length; i < len; i++) {
29829                 if(!this.items[i].isHidden()) {
29830                 this.items[i].setWidth(width);
29831             }
29832         }
29833     },
29834
29835     /**
29836      * Destroys this TabPanel
29837      * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
29838      */
29839     destroy : function(removeEl){
29840         Roo.EventManager.removeResizeListener(this.onResize, this);
29841         for(var i = 0, len = this.items.length; i < len; i++){
29842             this.items[i].purgeListeners();
29843         }
29844         if(removeEl === true){
29845             this.el.update("");
29846             this.el.remove();
29847         }
29848     }
29849 });
29850
29851 /**
29852  * @class Roo.TabPanelItem
29853  * @extends Roo.util.Observable
29854  * Represents an individual item (tab plus body) in a TabPanel.
29855  * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
29856  * @param {String} id The id of this TabPanelItem
29857  * @param {String} text The text for the tab of this TabPanelItem
29858  * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
29859  */
29860 Roo.TabPanelItem = function(tabPanel, id, text, closable){
29861     /**
29862      * The {@link Roo.TabPanel} this TabPanelItem belongs to
29863      * @type Roo.TabPanel
29864      */
29865     this.tabPanel = tabPanel;
29866     /**
29867      * The id for this TabPanelItem
29868      * @type String
29869      */
29870     this.id = id;
29871     /** @private */
29872     this.disabled = false;
29873     /** @private */
29874     this.text = text;
29875     /** @private */
29876     this.loaded = false;
29877     this.closable = closable;
29878
29879     /**
29880      * The body element for this TabPanelItem.
29881      * @type Roo.Element
29882      */
29883     this.bodyEl = Roo.get(tabPanel.createItemBody(tabPanel.bodyEl.dom, id));
29884     this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
29885     this.bodyEl.setStyle("display", "block");
29886     this.bodyEl.setStyle("zoom", "1");
29887     this.hideAction();
29888
29889     var els = tabPanel.createStripElements(tabPanel.stripEl.dom, text, closable);
29890     /** @private */
29891     this.el = Roo.get(els.el, true);
29892     this.inner = Roo.get(els.inner, true);
29893     this.textEl = Roo.get(this.el.dom.firstChild.firstChild.firstChild, true);
29894     this.pnode = Roo.get(els.el.parentNode, true);
29895     this.el.on("mousedown", this.onTabMouseDown, this);
29896     this.el.on("click", this.onTabClick, this);
29897     /** @private */
29898     if(closable){
29899         var c = Roo.get(els.close, true);
29900         c.dom.title = this.closeText;
29901         c.addClassOnOver("close-over");
29902         c.on("click", this.closeClick, this);
29903      }
29904
29905     this.addEvents({
29906          /**
29907          * @event activate
29908          * Fires when this tab becomes the active tab.
29909          * @param {Roo.TabPanel} tabPanel The parent TabPanel
29910          * @param {Roo.TabPanelItem} this
29911          */
29912         "activate": true,
29913         /**
29914          * @event beforeclose
29915          * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
29916          * @param {Roo.TabPanelItem} this
29917          * @param {Object} e Set cancel to true on this object to cancel the close.
29918          */
29919         "beforeclose": true,
29920         /**
29921          * @event close
29922          * Fires when this tab is closed.
29923          * @param {Roo.TabPanelItem} this
29924          */
29925          "close": true,
29926         /**
29927          * @event deactivate
29928          * Fires when this tab is no longer the active tab.
29929          * @param {Roo.TabPanel} tabPanel The parent TabPanel
29930          * @param {Roo.TabPanelItem} this
29931          */
29932          "deactivate" : true
29933     });
29934     this.hidden = false;
29935
29936     Roo.TabPanelItem.superclass.constructor.call(this);
29937 };
29938
29939 Roo.extend(Roo.TabPanelItem, Roo.util.Observable, {
29940     purgeListeners : function(){
29941        Roo.util.Observable.prototype.purgeListeners.call(this);
29942        this.el.removeAllListeners();
29943     },
29944     /**
29945      * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
29946      */
29947     show : function(){
29948         this.pnode.addClass("on");
29949         this.showAction();
29950         if(Roo.isOpera){
29951             this.tabPanel.stripWrap.repaint();
29952         }
29953         this.fireEvent("activate", this.tabPanel, this);
29954     },
29955
29956     /**
29957      * Returns true if this tab is the active tab.
29958      * @return {Boolean}
29959      */
29960     isActive : function(){
29961         return this.tabPanel.getActiveTab() == this;
29962     },
29963
29964     /**
29965      * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
29966      */
29967     hide : function(){
29968         this.pnode.removeClass("on");
29969         this.hideAction();
29970         this.fireEvent("deactivate", this.tabPanel, this);
29971     },
29972
29973     hideAction : function(){
29974         this.bodyEl.hide();
29975         this.bodyEl.setStyle("position", "absolute");
29976         this.bodyEl.setLeft("-20000px");
29977         this.bodyEl.setTop("-20000px");
29978     },
29979
29980     showAction : function(){
29981         this.bodyEl.setStyle("position", "relative");
29982         this.bodyEl.setTop("");
29983         this.bodyEl.setLeft("");
29984         this.bodyEl.show();
29985     },
29986
29987     /**
29988      * Set the tooltip for the tab.
29989      * @param {String} tooltip The tab's tooltip
29990      */
29991     setTooltip : function(text){
29992         if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
29993             this.textEl.dom.qtip = text;
29994             this.textEl.dom.removeAttribute('title');
29995         }else{
29996             this.textEl.dom.title = text;
29997         }
29998     },
29999
30000     onTabClick : function(e){
30001         e.preventDefault();
30002         this.tabPanel.activate(this.id);
30003     },
30004
30005     onTabMouseDown : function(e){
30006         e.preventDefault();
30007         this.tabPanel.activate(this.id);
30008     },
30009
30010     getWidth : function(){
30011         return this.inner.getWidth();
30012     },
30013
30014     setWidth : function(width){
30015         var iwidth = width - this.pnode.getPadding("lr");
30016         this.inner.setWidth(iwidth);
30017         this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
30018         this.pnode.setWidth(width);
30019     },
30020
30021     /**
30022      * Show or hide the tab
30023      * @param {Boolean} hidden True to hide or false to show.
30024      */
30025     setHidden : function(hidden){
30026         this.hidden = hidden;
30027         this.pnode.setStyle("display", hidden ? "none" : "");
30028     },
30029
30030     /**
30031      * Returns true if this tab is "hidden"
30032      * @return {Boolean}
30033      */
30034     isHidden : function(){
30035         return this.hidden;
30036     },
30037
30038     /**
30039      * Returns the text for this tab
30040      * @return {String}
30041      */
30042     getText : function(){
30043         return this.text;
30044     },
30045
30046     autoSize : function(){
30047         //this.el.beginMeasure();
30048         this.textEl.setWidth(1);
30049         /*
30050          *  #2804 [new] Tabs in Roojs
30051          *  increase the width by 2-4 pixels to prevent the ellipssis showing in chrome
30052          */
30053         this.setWidth(this.textEl.dom.scrollWidth+this.pnode.getPadding("lr")+this.inner.getPadding("lr") + 2);
30054         //this.el.endMeasure();
30055     },
30056
30057     /**
30058      * Sets the text for the tab (Note: this also sets the tooltip text)
30059      * @param {String} text The tab's text and tooltip
30060      */
30061     setText : function(text){
30062         this.text = text;
30063         this.textEl.update(text);
30064         this.setTooltip(text);
30065         if(!this.tabPanel.resizeTabs){
30066             this.autoSize();
30067         }
30068     },
30069     /**
30070      * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
30071      */
30072     activate : function(){
30073         this.tabPanel.activate(this.id);
30074     },
30075
30076     /**
30077      * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
30078      */
30079     disable : function(){
30080         if(this.tabPanel.active != this){
30081             this.disabled = true;
30082             this.pnode.addClass("disabled");
30083         }
30084     },
30085
30086     /**
30087      * Enables this TabPanelItem if it was previously disabled.
30088      */
30089     enable : function(){
30090         this.disabled = false;
30091         this.pnode.removeClass("disabled");
30092     },
30093
30094     /**
30095      * Sets the content for this TabPanelItem.
30096      * @param {String} content The content
30097      * @param {Boolean} loadScripts true to look for and load scripts
30098      */
30099     setContent : function(content, loadScripts){
30100         this.bodyEl.update(content, loadScripts);
30101     },
30102
30103     /**
30104      * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
30105      * @return {Roo.UpdateManager} The UpdateManager
30106      */
30107     getUpdateManager : function(){
30108         return this.bodyEl.getUpdateManager();
30109     },
30110
30111     /**
30112      * Set a URL to be used to load the content for this TabPanelItem.
30113      * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
30114      * @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)
30115      * @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)
30116      * @return {Roo.UpdateManager} The UpdateManager
30117      */
30118     setUrl : function(url, params, loadOnce){
30119         if(this.refreshDelegate){
30120             this.un('activate', this.refreshDelegate);
30121         }
30122         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
30123         this.on("activate", this.refreshDelegate);
30124         return this.bodyEl.getUpdateManager();
30125     },
30126
30127     /** @private */
30128     _handleRefresh : function(url, params, loadOnce){
30129         if(!loadOnce || !this.loaded){
30130             var updater = this.bodyEl.getUpdateManager();
30131             updater.update(url, params, this._setLoaded.createDelegate(this));
30132         }
30133     },
30134
30135     /**
30136      *   Forces a content refresh from the URL specified in the {@link #setUrl} method.
30137      *   Will fail silently if the setUrl method has not been called.
30138      *   This does not activate the panel, just updates its content.
30139      */
30140     refresh : function(){
30141         if(this.refreshDelegate){
30142            this.loaded = false;
30143            this.refreshDelegate();
30144         }
30145     },
30146
30147     /** @private */
30148     _setLoaded : function(){
30149         this.loaded = true;
30150     },
30151
30152     /** @private */
30153     closeClick : function(e){
30154         var o = {};
30155         e.stopEvent();
30156         this.fireEvent("beforeclose", this, o);
30157         if(o.cancel !== true){
30158             this.tabPanel.removeTab(this.id);
30159         }
30160     },
30161     /**
30162      * The text displayed in the tooltip for the close icon.
30163      * @type String
30164      */
30165     closeText : "Close this tab"
30166 });
30167
30168 /** @private */
30169 Roo.TabPanel.prototype.createStrip = function(container){
30170     var strip = document.createElement("div");
30171     strip.className = "x-tabs-wrap";
30172     container.appendChild(strip);
30173     return strip;
30174 };
30175 /** @private */
30176 Roo.TabPanel.prototype.createStripList = function(strip){
30177     // div wrapper for retard IE
30178     // returns the "tr" element.
30179     strip.innerHTML = '<div class="x-tabs-strip-wrap">'+
30180         '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
30181         '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
30182     return strip.firstChild.firstChild.firstChild.firstChild;
30183 };
30184 /** @private */
30185 Roo.TabPanel.prototype.createBody = function(container){
30186     var body = document.createElement("div");
30187     Roo.id(body, "tab-body");
30188     Roo.fly(body).addClass("x-tabs-body");
30189     container.appendChild(body);
30190     return body;
30191 };
30192 /** @private */
30193 Roo.TabPanel.prototype.createItemBody = function(bodyEl, id){
30194     var body = Roo.getDom(id);
30195     if(!body){
30196         body = document.createElement("div");
30197         body.id = id;
30198     }
30199     Roo.fly(body).addClass("x-tabs-item-body");
30200     bodyEl.insertBefore(body, bodyEl.firstChild);
30201     return body;
30202 };
30203 /** @private */
30204 Roo.TabPanel.prototype.createStripElements = function(stripEl, text, closable){
30205     var td = document.createElement("td");
30206     stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
30207     //stripEl.appendChild(td);
30208     if(closable){
30209         td.className = "x-tabs-closable";
30210         if(!this.closeTpl){
30211             this.closeTpl = new Roo.Template(
30212                '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
30213                '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
30214                '<div unselectable="on" class="close-icon">&#160;</div></em></span></a>'
30215             );
30216         }
30217         var el = this.closeTpl.overwrite(td, {"text": text});
30218         var close = el.getElementsByTagName("div")[0];
30219         var inner = el.getElementsByTagName("em")[0];
30220         return {"el": el, "close": close, "inner": inner};
30221     } else {
30222         if(!this.tabTpl){
30223             this.tabTpl = new Roo.Template(
30224                '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
30225                '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
30226             );
30227         }
30228         var el = this.tabTpl.overwrite(td, {"text": text});
30229         var inner = el.getElementsByTagName("em")[0];
30230         return {"el": el, "inner": inner};
30231     }
30232 };/*
30233  * Based on:
30234  * Ext JS Library 1.1.1
30235  * Copyright(c) 2006-2007, Ext JS, LLC.
30236  *
30237  * Originally Released Under LGPL - original licence link has changed is not relivant.
30238  *
30239  * Fork - LGPL
30240  * <script type="text/javascript">
30241  */
30242
30243 /**
30244  * @class Roo.Button
30245  * @extends Roo.util.Observable
30246  * Simple Button class
30247  * @cfg {String} text The button text
30248  * @cfg {String} icon The path to an image to display in the button (the image will be set as the background-image
30249  * CSS property of the button by default, so if you want a mixed icon/text button, set cls:"x-btn-text-icon")
30250  * @cfg {Function} handler A function called when the button is clicked (can be used instead of click event)
30251  * @cfg {Object} scope The scope of the handler
30252  * @cfg {Number} minWidth The minimum width for this button (used to give a set of buttons a common width)
30253  * @cfg {String/Object} tooltip The tooltip for the button - can be a string or QuickTips config object
30254  * @cfg {Boolean} hidden True to start hidden (defaults to false)
30255  * @cfg {Boolean} disabled True to start disabled (defaults to false)
30256  * @cfg {Boolean} pressed True to start pressed (only if enableToggle = true)
30257  * @cfg {String} toggleGroup The group this toggle button is a member of (only 1 per group can be pressed, only
30258    applies if enableToggle = true)
30259  * @cfg {String/HTMLElement/Element} renderTo The element to append the button to
30260  * @cfg {Boolean/Object} repeat True to repeat fire the click event while the mouse is down. This can also be
30261   an {@link Roo.util.ClickRepeater} config object (defaults to false).
30262  * @constructor
30263  * Create a new button
30264  * @param {Object} config The config object
30265  */
30266 Roo.Button = function(renderTo, config)
30267 {
30268     if (!config) {
30269         config = renderTo;
30270         renderTo = config.renderTo || false;
30271     }
30272     
30273     Roo.apply(this, config);
30274     this.addEvents({
30275         /**
30276              * @event click
30277              * Fires when this button is clicked
30278              * @param {Button} this
30279              * @param {EventObject} e The click event
30280              */
30281             "click" : true,
30282         /**
30283              * @event toggle
30284              * Fires when the "pressed" state of this button changes (only if enableToggle = true)
30285              * @param {Button} this
30286              * @param {Boolean} pressed
30287              */
30288             "toggle" : true,
30289         /**
30290              * @event mouseover
30291              * Fires when the mouse hovers over the button
30292              * @param {Button} this
30293              * @param {Event} e The event object
30294              */
30295         'mouseover' : true,
30296         /**
30297              * @event mouseout
30298              * Fires when the mouse exits the button
30299              * @param {Button} this
30300              * @param {Event} e The event object
30301              */
30302         'mouseout': true,
30303          /**
30304              * @event render
30305              * Fires when the button is rendered
30306              * @param {Button} this
30307              */
30308         'render': true
30309     });
30310     if(this.menu){
30311         this.menu = Roo.menu.MenuMgr.get(this.menu);
30312     }
30313     // register listeners first!!  - so render can be captured..
30314     Roo.util.Observable.call(this);
30315     if(renderTo){
30316         this.render(renderTo);
30317     }
30318     
30319   
30320 };
30321
30322 Roo.extend(Roo.Button, Roo.util.Observable, {
30323     /**
30324      * 
30325      */
30326     
30327     /**
30328      * Read-only. True if this button is hidden
30329      * @type Boolean
30330      */
30331     hidden : false,
30332     /**
30333      * Read-only. True if this button is disabled
30334      * @type Boolean
30335      */
30336     disabled : false,
30337     /**
30338      * Read-only. True if this button is pressed (only if enableToggle = true)
30339      * @type Boolean
30340      */
30341     pressed : false,
30342
30343     /**
30344      * @cfg {Number} tabIndex 
30345      * The DOM tabIndex for this button (defaults to undefined)
30346      */
30347     tabIndex : undefined,
30348
30349     /**
30350      * @cfg {Boolean} enableToggle
30351      * True to enable pressed/not pressed toggling (defaults to false)
30352      */
30353     enableToggle: false,
30354     /**
30355      * @cfg {Roo.menu.Menu} menu
30356      * Standard menu attribute consisting of a reference to a menu object, a menu id or a menu config blob (defaults to undefined).
30357      */
30358     menu : undefined,
30359     /**
30360      * @cfg {String} menuAlign
30361      * The position to align the menu to (see {@link Roo.Element#alignTo} for more details, defaults to 'tl-bl?').
30362      */
30363     menuAlign : "tl-bl?",
30364
30365     /**
30366      * @cfg {String} iconCls
30367      * A css class which sets a background image to be used as the icon for this button (defaults to undefined).
30368      */
30369     iconCls : undefined,
30370     /**
30371      * @cfg {String} type
30372      * The button's type, corresponding to the DOM input element type attribute.  Either "submit," "reset" or "button" (default).
30373      */
30374     type : 'button',
30375
30376     // private
30377     menuClassTarget: 'tr',
30378
30379     /**
30380      * @cfg {String} clickEvent
30381      * The type of event to map to the button's event handler (defaults to 'click')
30382      */
30383     clickEvent : 'click',
30384
30385     /**
30386      * @cfg {Boolean} handleMouseEvents
30387      * False to disable visual cues on mouseover, mouseout and mousedown (defaults to true)
30388      */
30389     handleMouseEvents : true,
30390
30391     /**
30392      * @cfg {String} tooltipType
30393      * The type of tooltip to use. Either "qtip" (default) for QuickTips or "title" for title attribute.
30394      */
30395     tooltipType : 'qtip',
30396
30397     /**
30398      * @cfg {String} cls
30399      * A CSS class to apply to the button's main element.
30400      */
30401     
30402     /**
30403      * @cfg {Roo.Template} template (Optional)
30404      * An {@link Roo.Template} with which to create the Button's main element. This Template must
30405      * contain numeric substitution parameter 0 if it is to display the tRoo property. Changing the template could
30406      * require code modifications if required elements (e.g. a button) aren't present.
30407      */
30408
30409     // private
30410     render : function(renderTo){
30411         var btn;
30412         if(this.hideParent){
30413             this.parentEl = Roo.get(renderTo);
30414         }
30415         if(!this.dhconfig){
30416             if(!this.template){
30417                 if(!Roo.Button.buttonTemplate){
30418                     // hideous table template
30419                     Roo.Button.buttonTemplate = new Roo.Template(
30420                         '<table border="0" cellpadding="0" cellspacing="0" class="x-btn-wrap"><tbody><tr>',
30421                         '<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>',
30422                         "</tr></tbody></table>");
30423                 }
30424                 this.template = Roo.Button.buttonTemplate;
30425             }
30426             btn = this.template.append(renderTo, [this.text || '&#160;', this.type], true);
30427             var btnEl = btn.child("button:first");
30428             btnEl.on('focus', this.onFocus, this);
30429             btnEl.on('blur', this.onBlur, this);
30430             if(this.cls){
30431                 btn.addClass(this.cls);
30432             }
30433             if(this.icon){
30434                 btnEl.setStyle('background-image', 'url(' +this.icon +')');
30435             }
30436             if(this.iconCls){
30437                 btnEl.addClass(this.iconCls);
30438                 if(!this.cls){
30439                     btn.addClass(this.text ? 'x-btn-text-icon' : 'x-btn-icon');
30440                 }
30441             }
30442             if(this.tabIndex !== undefined){
30443                 btnEl.dom.tabIndex = this.tabIndex;
30444             }
30445             if(this.tooltip){
30446                 if(typeof this.tooltip == 'object'){
30447                     Roo.QuickTips.tips(Roo.apply({
30448                           target: btnEl.id
30449                     }, this.tooltip));
30450                 } else {
30451                     btnEl.dom[this.tooltipType] = this.tooltip;
30452                 }
30453             }
30454         }else{
30455             btn = Roo.DomHelper.append(Roo.get(renderTo).dom, this.dhconfig, true);
30456         }
30457         this.el = btn;
30458         if(this.id){
30459             this.el.dom.id = this.el.id = this.id;
30460         }
30461         if(this.menu){
30462             this.el.child(this.menuClassTarget).addClass("x-btn-with-menu");
30463             this.menu.on("show", this.onMenuShow, this);
30464             this.menu.on("hide", this.onMenuHide, this);
30465         }
30466         btn.addClass("x-btn");
30467         if(Roo.isIE && !Roo.isIE7){
30468             this.autoWidth.defer(1, this);
30469         }else{
30470             this.autoWidth();
30471         }
30472         if(this.handleMouseEvents){
30473             btn.on("mouseover", this.onMouseOver, this);
30474             btn.on("mouseout", this.onMouseOut, this);
30475             btn.on("mousedown", this.onMouseDown, this);
30476         }
30477         btn.on(this.clickEvent, this.onClick, this);
30478         //btn.on("mouseup", this.onMouseUp, this);
30479         if(this.hidden){
30480             this.hide();
30481         }
30482         if(this.disabled){
30483             this.disable();
30484         }
30485         Roo.ButtonToggleMgr.register(this);
30486         if(this.pressed){
30487             this.el.addClass("x-btn-pressed");
30488         }
30489         if(this.repeat){
30490             var repeater = new Roo.util.ClickRepeater(btn,
30491                 typeof this.repeat == "object" ? this.repeat : {}
30492             );
30493             repeater.on("click", this.onClick,  this);
30494         }
30495         
30496         this.fireEvent('render', this);
30497         
30498     },
30499     /**
30500      * Returns the button's underlying element
30501      * @return {Roo.Element} The element
30502      */
30503     getEl : function(){
30504         return this.el;  
30505     },
30506     
30507     /**
30508      * Destroys this Button and removes any listeners.
30509      */
30510     destroy : function(){
30511         Roo.ButtonToggleMgr.unregister(this);
30512         this.el.removeAllListeners();
30513         this.purgeListeners();
30514         this.el.remove();
30515     },
30516
30517     // private
30518     autoWidth : function(){
30519         if(this.el){
30520             this.el.setWidth("auto");
30521             if(Roo.isIE7 && Roo.isStrict){
30522                 var ib = this.el.child('button');
30523                 if(ib && ib.getWidth() > 20){
30524                     ib.clip();
30525                     ib.setWidth(Roo.util.TextMetrics.measure(ib, this.text).width+ib.getFrameWidth('lr'));
30526                 }
30527             }
30528             if(this.minWidth){
30529                 if(this.hidden){
30530                     this.el.beginMeasure();
30531                 }
30532                 if(this.el.getWidth() < this.minWidth){
30533                     this.el.setWidth(this.minWidth);
30534                 }
30535                 if(this.hidden){
30536                     this.el.endMeasure();
30537                 }
30538             }
30539         }
30540     },
30541
30542     /**
30543      * Assigns this button's click handler
30544      * @param {Function} handler The function to call when the button is clicked
30545      * @param {Object} scope (optional) Scope for the function passed in
30546      */
30547     setHandler : function(handler, scope){
30548         this.handler = handler;
30549         this.scope = scope;  
30550     },
30551     
30552     /**
30553      * Sets this button's text
30554      * @param {String} text The button text
30555      */
30556     setText : function(text){
30557         this.text = text;
30558         if(this.el){
30559             this.el.child("td.x-btn-center button.x-btn-text").update(text);
30560         }
30561         this.autoWidth();
30562     },
30563     
30564     /**
30565      * Gets the text for this button
30566      * @return {String} The button text
30567      */
30568     getText : function(){
30569         return this.text;  
30570     },
30571     
30572     /**
30573      * Show this button
30574      */
30575     show: function(){
30576         this.hidden = false;
30577         if(this.el){
30578             this[this.hideParent? 'parentEl' : 'el'].setStyle("display", "");
30579         }
30580     },
30581     
30582     /**
30583      * Hide this button
30584      */
30585     hide: function(){
30586         this.hidden = true;
30587         if(this.el){
30588             this[this.hideParent? 'parentEl' : 'el'].setStyle("display", "none");
30589         }
30590     },
30591     
30592     /**
30593      * Convenience function for boolean show/hide
30594      * @param {Boolean} visible True to show, false to hide
30595      */
30596     setVisible: function(visible){
30597         if(visible) {
30598             this.show();
30599         }else{
30600             this.hide();
30601         }
30602     },
30603     
30604     /**
30605      * If a state it passed, it becomes the pressed state otherwise the current state is toggled.
30606      * @param {Boolean} state (optional) Force a particular state
30607      */
30608     toggle : function(state){
30609         state = state === undefined ? !this.pressed : state;
30610         if(state != this.pressed){
30611             if(state){
30612                 this.el.addClass("x-btn-pressed");
30613                 this.pressed = true;
30614                 this.fireEvent("toggle", this, true);
30615             }else{
30616                 this.el.removeClass("x-btn-pressed");
30617                 this.pressed = false;
30618                 this.fireEvent("toggle", this, false);
30619             }
30620             if(this.toggleHandler){
30621                 this.toggleHandler.call(this.scope || this, this, state);
30622             }
30623         }
30624     },
30625     
30626     /**
30627      * Focus the button
30628      */
30629     focus : function(){
30630         this.el.child('button:first').focus();
30631     },
30632     
30633     /**
30634      * Disable this button
30635      */
30636     disable : function(){
30637         if(this.el){
30638             this.el.addClass("x-btn-disabled");
30639         }
30640         this.disabled = true;
30641     },
30642     
30643     /**
30644      * Enable this button
30645      */
30646     enable : function(){
30647         if(this.el){
30648             this.el.removeClass("x-btn-disabled");
30649         }
30650         this.disabled = false;
30651     },
30652
30653     /**
30654      * Convenience function for boolean enable/disable
30655      * @param {Boolean} enabled True to enable, false to disable
30656      */
30657     setDisabled : function(v){
30658         this[v !== true ? "enable" : "disable"]();
30659     },
30660
30661     // private
30662     onClick : function(e)
30663     {
30664         if(e){
30665             e.preventDefault();
30666         }
30667         if(e.button != 0){
30668             return;
30669         }
30670         if(!this.disabled){
30671             if(this.enableToggle){
30672                 this.toggle();
30673             }
30674             if(this.menu && !this.menu.isVisible()){
30675                 this.menu.show(this.el, this.menuAlign);
30676             }
30677             this.fireEvent("click", this, e);
30678             if(this.handler){
30679                 this.el.removeClass("x-btn-over");
30680                 this.handler.call(this.scope || this, this, e);
30681             }
30682         }
30683     },
30684     // private
30685     onMouseOver : function(e){
30686         if(!this.disabled){
30687             this.el.addClass("x-btn-over");
30688             this.fireEvent('mouseover', this, e);
30689         }
30690     },
30691     // private
30692     onMouseOut : function(e){
30693         if(!e.within(this.el,  true)){
30694             this.el.removeClass("x-btn-over");
30695             this.fireEvent('mouseout', this, e);
30696         }
30697     },
30698     // private
30699     onFocus : function(e){
30700         if(!this.disabled){
30701             this.el.addClass("x-btn-focus");
30702         }
30703     },
30704     // private
30705     onBlur : function(e){
30706         this.el.removeClass("x-btn-focus");
30707     },
30708     // private
30709     onMouseDown : function(e){
30710         if(!this.disabled && e.button == 0){
30711             this.el.addClass("x-btn-click");
30712             Roo.get(document).on('mouseup', this.onMouseUp, this);
30713         }
30714     },
30715     // private
30716     onMouseUp : function(e){
30717         if(e.button == 0){
30718             this.el.removeClass("x-btn-click");
30719             Roo.get(document).un('mouseup', this.onMouseUp, this);
30720         }
30721     },
30722     // private
30723     onMenuShow : function(e){
30724         this.el.addClass("x-btn-menu-active");
30725     },
30726     // private
30727     onMenuHide : function(e){
30728         this.el.removeClass("x-btn-menu-active");
30729     }   
30730 });
30731
30732 // Private utility class used by Button
30733 Roo.ButtonToggleMgr = function(){
30734    var groups = {};
30735    
30736    function toggleGroup(btn, state){
30737        if(state){
30738            var g = groups[btn.toggleGroup];
30739            for(var i = 0, l = g.length; i < l; i++){
30740                if(g[i] != btn){
30741                    g[i].toggle(false);
30742                }
30743            }
30744        }
30745    }
30746    
30747    return {
30748        register : function(btn){
30749            if(!btn.toggleGroup){
30750                return;
30751            }
30752            var g = groups[btn.toggleGroup];
30753            if(!g){
30754                g = groups[btn.toggleGroup] = [];
30755            }
30756            g.push(btn);
30757            btn.on("toggle", toggleGroup);
30758        },
30759        
30760        unregister : function(btn){
30761            if(!btn.toggleGroup){
30762                return;
30763            }
30764            var g = groups[btn.toggleGroup];
30765            if(g){
30766                g.remove(btn);
30767                btn.un("toggle", toggleGroup);
30768            }
30769        }
30770    };
30771 }();/*
30772  * Based on:
30773  * Ext JS Library 1.1.1
30774  * Copyright(c) 2006-2007, Ext JS, LLC.
30775  *
30776  * Originally Released Under LGPL - original licence link has changed is not relivant.
30777  *
30778  * Fork - LGPL
30779  * <script type="text/javascript">
30780  */
30781  
30782 /**
30783  * @class Roo.SplitButton
30784  * @extends Roo.Button
30785  * A split button that provides a built-in dropdown arrow that can fire an event separately from the default
30786  * click event of the button.  Typically this would be used to display a dropdown menu that provides additional
30787  * options to the primary button action, but any custom handler can provide the arrowclick implementation.
30788  * @cfg {Function} arrowHandler A function called when the arrow button is clicked (can be used instead of click event)
30789  * @cfg {String} arrowTooltip The title attribute of the arrow
30790  * @constructor
30791  * Create a new menu button
30792  * @param {String/HTMLElement/Element} renderTo The element to append the button to
30793  * @param {Object} config The config object
30794  */
30795 Roo.SplitButton = function(renderTo, config){
30796     Roo.SplitButton.superclass.constructor.call(this, renderTo, config);
30797     /**
30798      * @event arrowclick
30799      * Fires when this button's arrow is clicked
30800      * @param {SplitButton} this
30801      * @param {EventObject} e The click event
30802      */
30803     this.addEvents({"arrowclick":true});
30804 };
30805
30806 Roo.extend(Roo.SplitButton, Roo.Button, {
30807     render : function(renderTo){
30808         // this is one sweet looking template!
30809         var tpl = new Roo.Template(
30810             '<table cellspacing="0" class="x-btn-menu-wrap x-btn"><tr><td>',
30811             '<table cellspacing="0" class="x-btn-wrap x-btn-menu-text-wrap"><tbody>',
30812             '<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>',
30813             "</tbody></table></td><td>",
30814             '<table cellspacing="0" class="x-btn-wrap x-btn-menu-arrow-wrap"><tbody>',
30815             '<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>',
30816             "</tbody></table></td></tr></table>"
30817         );
30818         var btn = tpl.append(renderTo, [this.text, this.type], true);
30819         var btnEl = btn.child("button");
30820         if(this.cls){
30821             btn.addClass(this.cls);
30822         }
30823         if(this.icon){
30824             btnEl.setStyle('background-image', 'url(' +this.icon +')');
30825         }
30826         if(this.iconCls){
30827             btnEl.addClass(this.iconCls);
30828             if(!this.cls){
30829                 btn.addClass(this.text ? 'x-btn-text-icon' : 'x-btn-icon');
30830             }
30831         }
30832         this.el = btn;
30833         if(this.handleMouseEvents){
30834             btn.on("mouseover", this.onMouseOver, this);
30835             btn.on("mouseout", this.onMouseOut, this);
30836             btn.on("mousedown", this.onMouseDown, this);
30837             btn.on("mouseup", this.onMouseUp, this);
30838         }
30839         btn.on(this.clickEvent, this.onClick, this);
30840         if(this.tooltip){
30841             if(typeof this.tooltip == 'object'){
30842                 Roo.QuickTips.tips(Roo.apply({
30843                       target: btnEl.id
30844                 }, this.tooltip));
30845             } else {
30846                 btnEl.dom[this.tooltipType] = this.tooltip;
30847             }
30848         }
30849         if(this.arrowTooltip){
30850             btn.child("button:nth(2)").dom[this.tooltipType] = this.arrowTooltip;
30851         }
30852         if(this.hidden){
30853             this.hide();
30854         }
30855         if(this.disabled){
30856             this.disable();
30857         }
30858         if(this.pressed){
30859             this.el.addClass("x-btn-pressed");
30860         }
30861         if(Roo.isIE && !Roo.isIE7){
30862             this.autoWidth.defer(1, this);
30863         }else{
30864             this.autoWidth();
30865         }
30866         if(this.menu){
30867             this.menu.on("show", this.onMenuShow, this);
30868             this.menu.on("hide", this.onMenuHide, this);
30869         }
30870         this.fireEvent('render', this);
30871     },
30872
30873     // private
30874     autoWidth : function(){
30875         if(this.el){
30876             var tbl = this.el.child("table:first");
30877             var tbl2 = this.el.child("table:last");
30878             this.el.setWidth("auto");
30879             tbl.setWidth("auto");
30880             if(Roo.isIE7 && Roo.isStrict){
30881                 var ib = this.el.child('button:first');
30882                 if(ib && ib.getWidth() > 20){
30883                     ib.clip();
30884                     ib.setWidth(Roo.util.TextMetrics.measure(ib, this.text).width+ib.getFrameWidth('lr'));
30885                 }
30886             }
30887             if(this.minWidth){
30888                 if(this.hidden){
30889                     this.el.beginMeasure();
30890                 }
30891                 if((tbl.getWidth()+tbl2.getWidth()) < this.minWidth){
30892                     tbl.setWidth(this.minWidth-tbl2.getWidth());
30893                 }
30894                 if(this.hidden){
30895                     this.el.endMeasure();
30896                 }
30897             }
30898             this.el.setWidth(tbl.getWidth()+tbl2.getWidth());
30899         } 
30900     },
30901     /**
30902      * Sets this button's click handler
30903      * @param {Function} handler The function to call when the button is clicked
30904      * @param {Object} scope (optional) Scope for the function passed above
30905      */
30906     setHandler : function(handler, scope){
30907         this.handler = handler;
30908         this.scope = scope;  
30909     },
30910     
30911     /**
30912      * Sets this button's arrow click handler
30913      * @param {Function} handler The function to call when the arrow is clicked
30914      * @param {Object} scope (optional) Scope for the function passed above
30915      */
30916     setArrowHandler : function(handler, scope){
30917         this.arrowHandler = handler;
30918         this.scope = scope;  
30919     },
30920     
30921     /**
30922      * Focus the button
30923      */
30924     focus : function(){
30925         if(this.el){
30926             this.el.child("button:first").focus();
30927         }
30928     },
30929
30930     // private
30931     onClick : function(e){
30932         e.preventDefault();
30933         if(!this.disabled){
30934             if(e.getTarget(".x-btn-menu-arrow-wrap")){
30935                 if(this.menu && !this.menu.isVisible()){
30936                     this.menu.show(this.el, this.menuAlign);
30937                 }
30938                 this.fireEvent("arrowclick", this, e);
30939                 if(this.arrowHandler){
30940                     this.arrowHandler.call(this.scope || this, this, e);
30941                 }
30942             }else{
30943                 this.fireEvent("click", this, e);
30944                 if(this.handler){
30945                     this.handler.call(this.scope || this, this, e);
30946                 }
30947             }
30948         }
30949     },
30950     // private
30951     onMouseDown : function(e){
30952         if(!this.disabled){
30953             Roo.fly(e.getTarget("table")).addClass("x-btn-click");
30954         }
30955     },
30956     // private
30957     onMouseUp : function(e){
30958         Roo.fly(e.getTarget("table")).removeClass("x-btn-click");
30959     }   
30960 });
30961
30962
30963 // backwards compat
30964 Roo.MenuButton = Roo.SplitButton;/*
30965  * Based on:
30966  * Ext JS Library 1.1.1
30967  * Copyright(c) 2006-2007, Ext JS, LLC.
30968  *
30969  * Originally Released Under LGPL - original licence link has changed is not relivant.
30970  *
30971  * Fork - LGPL
30972  * <script type="text/javascript">
30973  */
30974
30975 /**
30976  * @class Roo.Toolbar
30977  * @children   Roo.Toolbar.Item Roo.form.Field
30978  * Basic Toolbar class.
30979  * @constructor
30980  * Creates a new Toolbar
30981  * @param {Object} container The config object
30982  */ 
30983 Roo.Toolbar = function(container, buttons, config)
30984 {
30985     /// old consturctor format still supported..
30986     if(container instanceof Array){ // omit the container for later rendering
30987         buttons = container;
30988         config = buttons;
30989         container = null;
30990     }
30991     if (typeof(container) == 'object' && container.xtype) {
30992         config = container;
30993         container = config.container;
30994         buttons = config.buttons || []; // not really - use items!!
30995     }
30996     var xitems = [];
30997     if (config && config.items) {
30998         xitems = config.items;
30999         delete config.items;
31000     }
31001     Roo.apply(this, config);
31002     this.buttons = buttons;
31003     
31004     if(container){
31005         this.render(container);
31006     }
31007     this.xitems = xitems;
31008     Roo.each(xitems, function(b) {
31009         this.add(b);
31010     }, this);
31011     
31012 };
31013
31014 Roo.Toolbar.prototype = {
31015     /**
31016      * @cfg {Array} items
31017      * array of button configs or elements to add (will be converted to a MixedCollection)
31018      */
31019     items: false,
31020     /**
31021      * @cfg {String/HTMLElement/Element} container
31022      * The id or element that will contain the toolbar
31023      */
31024     // private
31025     render : function(ct){
31026         this.el = Roo.get(ct);
31027         if(this.cls){
31028             this.el.addClass(this.cls);
31029         }
31030         // using a table allows for vertical alignment
31031         // 100% width is needed by Safari...
31032         this.el.update('<div class="x-toolbar x-small-editor"><table cellspacing="0"><tr></tr></table></div>');
31033         this.tr = this.el.child("tr", true);
31034         var autoId = 0;
31035         this.items = new Roo.util.MixedCollection(false, function(o){
31036             return o.id || ("item" + (++autoId));
31037         });
31038         if(this.buttons){
31039             this.add.apply(this, this.buttons);
31040             delete this.buttons;
31041         }
31042     },
31043
31044     /**
31045      * Adds element(s) to the toolbar -- this function takes a variable number of 
31046      * arguments of mixed type and adds them to the toolbar.
31047      * @param {Mixed} arg1 The following types of arguments are all valid:<br />
31048      * <ul>
31049      * <li>{@link Roo.Toolbar.Button} config: A valid button config object (equivalent to {@link #addButton})</li>
31050      * <li>HtmlElement: Any standard HTML element (equivalent to {@link #addElement})</li>
31051      * <li>Field: Any form field (equivalent to {@link #addField})</li>
31052      * <li>Item: Any subclass of {@link Roo.Toolbar.Item} (equivalent to {@link #addItem})</li>
31053      * <li>String: Any generic string (gets wrapped in a {@link Roo.Toolbar.TextItem}, equivalent to {@link #addText}).
31054      * Note that there are a few special strings that are treated differently as explained nRoo.</li>
31055      * <li>'separator' or '-': Creates a separator element (equivalent to {@link #addSeparator})</li>
31056      * <li>' ': Creates a spacer element (equivalent to {@link #addSpacer})</li>
31057      * <li>'->': Creates a fill element (equivalent to {@link #addFill})</li>
31058      * </ul>
31059      * @param {Mixed} arg2
31060      * @param {Mixed} etc.
31061      */
31062     add : function(){
31063         var a = arguments, l = a.length;
31064         for(var i = 0; i < l; i++){
31065             this._add(a[i]);
31066         }
31067     },
31068     // private..
31069     _add : function(el) {
31070         
31071         if (el.xtype) {
31072             el = Roo.factory(el, typeof(Roo.Toolbar[el.xtype]) == 'undefined' ? Roo.form : Roo.Toolbar);
31073         }
31074         
31075         if (el.applyTo){ // some kind of form field
31076             return this.addField(el);
31077         } 
31078         if (el.render){ // some kind of Toolbar.Item
31079             return this.addItem(el);
31080         }
31081         if (typeof el == "string"){ // string
31082             if(el == "separator" || el == "-"){
31083                 return this.addSeparator();
31084             }
31085             if (el == " "){
31086                 return this.addSpacer();
31087             }
31088             if(el == "->"){
31089                 return this.addFill();
31090             }
31091             return this.addText(el);
31092             
31093         }
31094         if(el.tagName){ // element
31095             return this.addElement(el);
31096         }
31097         if(typeof el == "object"){ // must be button config?
31098             return this.addButton(el);
31099         }
31100         // and now what?!?!
31101         return false;
31102         
31103     },
31104     
31105     /**
31106      * Add an Xtype element
31107      * @param {Object} xtype Xtype Object
31108      * @return {Object} created Object
31109      */
31110     addxtype : function(e){
31111         return this.add(e);  
31112     },
31113     
31114     /**
31115      * Returns the Element for this toolbar.
31116      * @return {Roo.Element}
31117      */
31118     getEl : function(){
31119         return this.el;  
31120     },
31121     
31122     /**
31123      * Adds a separator
31124      * @return {Roo.Toolbar.Item} The separator item
31125      */
31126     addSeparator : function(){
31127         return this.addItem(new Roo.Toolbar.Separator());
31128     },
31129
31130     /**
31131      * Adds a spacer element
31132      * @return {Roo.Toolbar.Spacer} The spacer item
31133      */
31134     addSpacer : function(){
31135         return this.addItem(new Roo.Toolbar.Spacer());
31136     },
31137
31138     /**
31139      * Adds a fill element that forces subsequent additions to the right side of the toolbar
31140      * @return {Roo.Toolbar.Fill} The fill item
31141      */
31142     addFill : function(){
31143         return this.addItem(new Roo.Toolbar.Fill());
31144     },
31145
31146     /**
31147      * Adds any standard HTML element to the toolbar
31148      * @param {String/HTMLElement/Element} el The element or id of the element to add
31149      * @return {Roo.Toolbar.Item} The element's item
31150      */
31151     addElement : function(el){
31152         return this.addItem(new Roo.Toolbar.Item(el));
31153     },
31154     /**
31155      * Collection of items on the toolbar.. (only Toolbar Items, so use fields to retrieve fields)
31156      * @type Roo.util.MixedCollection  
31157      */
31158     items : false,
31159      
31160     /**
31161      * Adds any Toolbar.Item or subclass
31162      * @param {Roo.Toolbar.Item} item
31163      * @return {Roo.Toolbar.Item} The item
31164      */
31165     addItem : function(item){
31166         var td = this.nextBlock();
31167         item.render(td);
31168         this.items.add(item);
31169         return item;
31170     },
31171     
31172     /**
31173      * Adds a button (or buttons). See {@link Roo.Toolbar.Button} for more info on the config.
31174      * @param {Object/Array} config A button config or array of configs
31175      * @return {Roo.Toolbar.Button/Array}
31176      */
31177     addButton : function(config){
31178         if(config instanceof Array){
31179             var buttons = [];
31180             for(var i = 0, len = config.length; i < len; i++) {
31181                 buttons.push(this.addButton(config[i]));
31182             }
31183             return buttons;
31184         }
31185         var b = config;
31186         if(!(config instanceof Roo.Toolbar.Button)){
31187             b = config.split ?
31188                 new Roo.Toolbar.SplitButton(config) :
31189                 new Roo.Toolbar.Button(config);
31190         }
31191         var td = this.nextBlock();
31192         b.render(td);
31193         this.items.add(b);
31194         return b;
31195     },
31196     
31197     /**
31198      * Adds text to the toolbar
31199      * @param {String} text The text to add
31200      * @return {Roo.Toolbar.Item} The element's item
31201      */
31202     addText : function(text){
31203         return this.addItem(new Roo.Toolbar.TextItem(text));
31204     },
31205     
31206     /**
31207      * Inserts any {@link Roo.Toolbar.Item}/{@link Roo.Toolbar.Button} at the specified index.
31208      * @param {Number} index The index where the item is to be inserted
31209      * @param {Object/Roo.Toolbar.Item/Roo.Toolbar.Button (may be Array)} item The button, or button config object to be inserted.
31210      * @return {Roo.Toolbar.Button/Item}
31211      */
31212     insertButton : function(index, item){
31213         if(item instanceof Array){
31214             var buttons = [];
31215             for(var i = 0, len = item.length; i < len; i++) {
31216                buttons.push(this.insertButton(index + i, item[i]));
31217             }
31218             return buttons;
31219         }
31220         if (!(item instanceof Roo.Toolbar.Button)){
31221            item = new Roo.Toolbar.Button(item);
31222         }
31223         var td = document.createElement("td");
31224         this.tr.insertBefore(td, this.tr.childNodes[index]);
31225         item.render(td);
31226         this.items.insert(index, item);
31227         return item;
31228     },
31229     
31230     /**
31231      * Adds a new element to the toolbar from the passed {@link Roo.DomHelper} config.
31232      * @param {Object} config
31233      * @return {Roo.Toolbar.Item} The element's item
31234      */
31235     addDom : function(config, returnEl){
31236         var td = this.nextBlock();
31237         Roo.DomHelper.overwrite(td, config);
31238         var ti = new Roo.Toolbar.Item(td.firstChild);
31239         ti.render(td);
31240         this.items.add(ti);
31241         return ti;
31242     },
31243
31244     /**
31245      * Collection of fields on the toolbar.. usefull for quering (value is false if there are no fields)
31246      * @type Roo.util.MixedCollection  
31247      */
31248     fields : false,
31249     
31250     /**
31251      * Adds a dynamically rendered Roo.form field (TextField, ComboBox, etc).
31252      * Note: the field should not have been rendered yet. For a field that has already been
31253      * rendered, use {@link #addElement}.
31254      * @param {Roo.form.Field} field
31255      * @return {Roo.ToolbarItem}
31256      */
31257      
31258       
31259     addField : function(field) {
31260         if (!this.fields) {
31261             var autoId = 0;
31262             this.fields = new Roo.util.MixedCollection(false, function(o){
31263                 return o.id || ("item" + (++autoId));
31264             });
31265
31266         }
31267         
31268         var td = this.nextBlock();
31269         field.render(td);
31270         var ti = new Roo.Toolbar.Item(td.firstChild);
31271         ti.render(td);
31272         this.items.add(ti);
31273         this.fields.add(field);
31274         return ti;
31275     },
31276     /**
31277      * Hide the toolbar
31278      * @method hide
31279      */
31280      
31281       
31282     hide : function()
31283     {
31284         this.el.child('div').setVisibilityMode(Roo.Element.DISPLAY);
31285         this.el.child('div').hide();
31286     },
31287     /**
31288      * Show the toolbar
31289      * @method show
31290      */
31291     show : function()
31292     {
31293         this.el.child('div').show();
31294     },
31295       
31296     // private
31297     nextBlock : function(){
31298         var td = document.createElement("td");
31299         this.tr.appendChild(td);
31300         return td;
31301     },
31302
31303     // private
31304     destroy : function(){
31305         if(this.items){ // rendered?
31306             Roo.destroy.apply(Roo, this.items.items);
31307         }
31308         if(this.fields){ // rendered?
31309             Roo.destroy.apply(Roo, this.fields.items);
31310         }
31311         Roo.Element.uncache(this.el, this.tr);
31312     }
31313 };
31314
31315 /**
31316  * @class Roo.Toolbar.Item
31317  * The base class that other classes should extend in order to get some basic common toolbar item functionality.
31318  * @constructor
31319  * Creates a new Item
31320  * @param {HTMLElement} el 
31321  */
31322 Roo.Toolbar.Item = function(el){
31323     var cfg = {};
31324     if (typeof (el.xtype) != 'undefined') {
31325         cfg = el;
31326         el = cfg.el;
31327     }
31328     
31329     this.el = Roo.getDom(el);
31330     this.id = Roo.id(this.el);
31331     this.hidden = false;
31332     
31333     this.addEvents({
31334          /**
31335              * @event render
31336              * Fires when the button is rendered
31337              * @param {Button} this
31338              */
31339         'render': true
31340     });
31341     Roo.Toolbar.Item.superclass.constructor.call(this,cfg);
31342 };
31343 Roo.extend(Roo.Toolbar.Item, Roo.util.Observable, {
31344 //Roo.Toolbar.Item.prototype = {
31345     
31346     /**
31347      * Get this item's HTML Element
31348      * @return {HTMLElement}
31349      */
31350     getEl : function(){
31351        return this.el;  
31352     },
31353
31354     // private
31355     render : function(td){
31356         
31357          this.td = td;
31358         td.appendChild(this.el);
31359         
31360         this.fireEvent('render', this);
31361     },
31362     
31363     /**
31364      * Removes and destroys this item.
31365      */
31366     destroy : function(){
31367         this.td.parentNode.removeChild(this.td);
31368     },
31369     
31370     /**
31371      * Shows this item.
31372      */
31373     show: function(){
31374         this.hidden = false;
31375         this.td.style.display = "";
31376     },
31377     
31378     /**
31379      * Hides this item.
31380      */
31381     hide: function(){
31382         this.hidden = true;
31383         this.td.style.display = "none";
31384     },
31385     
31386     /**
31387      * Convenience function for boolean show/hide.
31388      * @param {Boolean} visible true to show/false to hide
31389      */
31390     setVisible: function(visible){
31391         if(visible) {
31392             this.show();
31393         }else{
31394             this.hide();
31395         }
31396     },
31397     
31398     /**
31399      * Try to focus this item.
31400      */
31401     focus : function(){
31402         Roo.fly(this.el).focus();
31403     },
31404     
31405     /**
31406      * Disables this item.
31407      */
31408     disable : function(){
31409         Roo.fly(this.td).addClass("x-item-disabled");
31410         this.disabled = true;
31411         this.el.disabled = true;
31412     },
31413     
31414     /**
31415      * Enables this item.
31416      */
31417     enable : function(){
31418         Roo.fly(this.td).removeClass("x-item-disabled");
31419         this.disabled = false;
31420         this.el.disabled = false;
31421     }
31422 });
31423
31424
31425 /**
31426  * @class Roo.Toolbar.Separator
31427  * @extends Roo.Toolbar.Item
31428  * A simple toolbar separator class
31429  * @constructor
31430  * Creates a new Separator
31431  */
31432 Roo.Toolbar.Separator = function(cfg){
31433     
31434     var s = document.createElement("span");
31435     s.className = "ytb-sep";
31436     if (cfg) {
31437         cfg.el = s;
31438     }
31439     
31440     Roo.Toolbar.Separator.superclass.constructor.call(this, cfg || s);
31441 };
31442 Roo.extend(Roo.Toolbar.Separator, Roo.Toolbar.Item, {
31443     enable:Roo.emptyFn,
31444     disable:Roo.emptyFn,
31445     focus:Roo.emptyFn
31446 });
31447
31448 /**
31449  * @class Roo.Toolbar.Spacer
31450  * @extends Roo.Toolbar.Item
31451  * A simple element that adds extra horizontal space to a toolbar.
31452  * @constructor
31453  * Creates a new Spacer
31454  */
31455 Roo.Toolbar.Spacer = function(cfg){
31456     var s = document.createElement("div");
31457     s.className = "ytb-spacer";
31458     if (cfg) {
31459         cfg.el = s;
31460     }
31461     Roo.Toolbar.Spacer.superclass.constructor.call(this, cfg || s);
31462 };
31463 Roo.extend(Roo.Toolbar.Spacer, Roo.Toolbar.Item, {
31464     enable:Roo.emptyFn,
31465     disable:Roo.emptyFn,
31466     focus:Roo.emptyFn
31467 });
31468
31469 /**
31470  * @class Roo.Toolbar.Fill
31471  * @extends Roo.Toolbar.Spacer
31472  * A simple element that adds a greedy (100% width) horizontal space to a toolbar.
31473  * @constructor
31474  * Creates a new Spacer
31475  */
31476 Roo.Toolbar.Fill = Roo.extend(Roo.Toolbar.Spacer, {
31477     // private
31478     render : function(td){
31479         td.style.width = '100%';
31480         Roo.Toolbar.Fill.superclass.render.call(this, td);
31481     }
31482 });
31483
31484 /**
31485  * @class Roo.Toolbar.TextItem
31486  * @extends Roo.Toolbar.Item
31487  * A simple class that renders text directly into a toolbar.
31488  * @constructor
31489  * Creates a new TextItem
31490  * @cfg {string} text 
31491  */
31492 Roo.Toolbar.TextItem = function(cfg){
31493     var  text = cfg || "";
31494     if (typeof(cfg) == 'object') {
31495         text = cfg.text || "";
31496     }  else {
31497         cfg = null;
31498     }
31499     var s = document.createElement("span");
31500     s.className = "ytb-text";
31501     s.innerHTML = text;
31502     if (cfg) {
31503         cfg.el  = s;
31504     }
31505     
31506     Roo.Toolbar.TextItem.superclass.constructor.call(this, cfg ||  s);
31507 };
31508 Roo.extend(Roo.Toolbar.TextItem, Roo.Toolbar.Item, {
31509     
31510      
31511     enable:Roo.emptyFn,
31512     disable:Roo.emptyFn,
31513     focus:Roo.emptyFn
31514 });
31515
31516 /**
31517  * @class Roo.Toolbar.Button
31518  * @extends Roo.Button
31519  * A button that renders into a toolbar.
31520  * @constructor
31521  * Creates a new Button
31522  * @param {Object} config A standard {@link Roo.Button} config object
31523  */
31524 Roo.Toolbar.Button = function(config){
31525     Roo.Toolbar.Button.superclass.constructor.call(this, null, config);
31526 };
31527 Roo.extend(Roo.Toolbar.Button, Roo.Button,
31528 {
31529     
31530     
31531     render : function(td){
31532         this.td = td;
31533         Roo.Toolbar.Button.superclass.render.call(this, td);
31534     },
31535     
31536     /**
31537      * Removes and destroys this button
31538      */
31539     destroy : function(){
31540         Roo.Toolbar.Button.superclass.destroy.call(this);
31541         this.td.parentNode.removeChild(this.td);
31542     },
31543     
31544     /**
31545      * Shows this button
31546      */
31547     show: function(){
31548         this.hidden = false;
31549         this.td.style.display = "";
31550     },
31551     
31552     /**
31553      * Hides this button
31554      */
31555     hide: function(){
31556         this.hidden = true;
31557         this.td.style.display = "none";
31558     },
31559
31560     /**
31561      * Disables this item
31562      */
31563     disable : function(){
31564         Roo.fly(this.td).addClass("x-item-disabled");
31565         this.disabled = true;
31566     },
31567
31568     /**
31569      * Enables this item
31570      */
31571     enable : function(){
31572         Roo.fly(this.td).removeClass("x-item-disabled");
31573         this.disabled = false;
31574     }
31575 });
31576 // backwards compat
31577 Roo.ToolbarButton = Roo.Toolbar.Button;
31578
31579 /**
31580  * @class Roo.Toolbar.SplitButton
31581  * @extends Roo.SplitButton
31582  * A menu button that renders into a toolbar.
31583  * @constructor
31584  * Creates a new SplitButton
31585  * @param {Object} config A standard {@link Roo.SplitButton} config object
31586  */
31587 Roo.Toolbar.SplitButton = function(config){
31588     Roo.Toolbar.SplitButton.superclass.constructor.call(this, null, config);
31589 };
31590 Roo.extend(Roo.Toolbar.SplitButton, Roo.SplitButton, {
31591     render : function(td){
31592         this.td = td;
31593         Roo.Toolbar.SplitButton.superclass.render.call(this, td);
31594     },
31595     
31596     /**
31597      * Removes and destroys this button
31598      */
31599     destroy : function(){
31600         Roo.Toolbar.SplitButton.superclass.destroy.call(this);
31601         this.td.parentNode.removeChild(this.td);
31602     },
31603     
31604     /**
31605      * Shows this button
31606      */
31607     show: function(){
31608         this.hidden = false;
31609         this.td.style.display = "";
31610     },
31611     
31612     /**
31613      * Hides this button
31614      */
31615     hide: function(){
31616         this.hidden = true;
31617         this.td.style.display = "none";
31618     }
31619 });
31620
31621 // backwards compat
31622 Roo.Toolbar.MenuButton = Roo.Toolbar.SplitButton;/*
31623  * Based on:
31624  * Ext JS Library 1.1.1
31625  * Copyright(c) 2006-2007, Ext JS, LLC.
31626  *
31627  * Originally Released Under LGPL - original licence link has changed is not relivant.
31628  *
31629  * Fork - LGPL
31630  * <script type="text/javascript">
31631  */
31632  
31633 /**
31634  * @class Roo.PagingToolbar
31635  * @extends Roo.Toolbar
31636  * @children   Roo.Toolbar.Item Roo.form.Field
31637  * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
31638  * @constructor
31639  * Create a new PagingToolbar
31640  * @param {Object} config The config object
31641  */
31642 Roo.PagingToolbar = function(el, ds, config)
31643 {
31644     // old args format still supported... - xtype is prefered..
31645     if (typeof(el) == 'object' && el.xtype) {
31646         // created from xtype...
31647         config = el;
31648         ds = el.dataSource;
31649         el = config.container;
31650     }
31651     var items = [];
31652     if (config.items) {
31653         items = config.items;
31654         config.items = [];
31655     }
31656     
31657     Roo.PagingToolbar.superclass.constructor.call(this, el, null, config);
31658     this.ds = ds;
31659     this.cursor = 0;
31660     this.renderButtons(this.el);
31661     this.bind(ds);
31662     
31663     // supprot items array.
31664    
31665     Roo.each(items, function(e) {
31666         this.add(Roo.factory(e));
31667     },this);
31668     
31669 };
31670
31671 Roo.extend(Roo.PagingToolbar, Roo.Toolbar, {
31672    
31673     /**
31674      * @cfg {String/HTMLElement/Element} container
31675      * container The id or element that will contain the toolbar
31676      */
31677     /**
31678      * @cfg {Boolean} displayInfo
31679      * True to display the displayMsg (defaults to false)
31680      */
31681     
31682     
31683     /**
31684      * @cfg {Number} pageSize
31685      * The number of records to display per page (defaults to 20)
31686      */
31687     pageSize: 20,
31688     /**
31689      * @cfg {String} displayMsg
31690      * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
31691      */
31692     displayMsg : 'Displaying {0} - {1} of {2}',
31693     /**
31694      * @cfg {String} emptyMsg
31695      * The message to display when no records are found (defaults to "No data to display")
31696      */
31697     emptyMsg : 'No data to display',
31698     /**
31699      * Customizable piece of the default paging text (defaults to "Page")
31700      * @type String
31701      */
31702     beforePageText : "Page",
31703     /**
31704      * Customizable piece of the default paging text (defaults to "of %0")
31705      * @type String
31706      */
31707     afterPageText : "of {0}",
31708     /**
31709      * Customizable piece of the default paging text (defaults to "First Page")
31710      * @type String
31711      */
31712     firstText : "First Page",
31713     /**
31714      * Customizable piece of the default paging text (defaults to "Previous Page")
31715      * @type String
31716      */
31717     prevText : "Previous Page",
31718     /**
31719      * Customizable piece of the default paging text (defaults to "Next Page")
31720      * @type String
31721      */
31722     nextText : "Next Page",
31723     /**
31724      * Customizable piece of the default paging text (defaults to "Last Page")
31725      * @type String
31726      */
31727     lastText : "Last Page",
31728     /**
31729      * Customizable piece of the default paging text (defaults to "Refresh")
31730      * @type String
31731      */
31732     refreshText : "Refresh",
31733
31734     // private
31735     renderButtons : function(el){
31736         Roo.PagingToolbar.superclass.render.call(this, el);
31737         this.first = this.addButton({
31738             tooltip: this.firstText,
31739             cls: "x-btn-icon x-grid-page-first",
31740             disabled: true,
31741             handler: this.onClick.createDelegate(this, ["first"])
31742         });
31743         this.prev = this.addButton({
31744             tooltip: this.prevText,
31745             cls: "x-btn-icon x-grid-page-prev",
31746             disabled: true,
31747             handler: this.onClick.createDelegate(this, ["prev"])
31748         });
31749         //this.addSeparator();
31750         this.add(this.beforePageText);
31751         this.field = Roo.get(this.addDom({
31752            tag: "input",
31753            type: "text",
31754            size: "3",
31755            value: "1",
31756            cls: "x-grid-page-number"
31757         }).el);
31758         this.field.on("keydown", this.onPagingKeydown, this);
31759         this.field.on("focus", function(){this.dom.select();});
31760         this.afterTextEl = this.addText(String.format(this.afterPageText, 1));
31761         this.field.setHeight(18);
31762         //this.addSeparator();
31763         this.next = this.addButton({
31764             tooltip: this.nextText,
31765             cls: "x-btn-icon x-grid-page-next",
31766             disabled: true,
31767             handler: this.onClick.createDelegate(this, ["next"])
31768         });
31769         this.last = this.addButton({
31770             tooltip: this.lastText,
31771             cls: "x-btn-icon x-grid-page-last",
31772             disabled: true,
31773             handler: this.onClick.createDelegate(this, ["last"])
31774         });
31775         //this.addSeparator();
31776         this.loading = this.addButton({
31777             tooltip: this.refreshText,
31778             cls: "x-btn-icon x-grid-loading",
31779             handler: this.onClick.createDelegate(this, ["refresh"])
31780         });
31781
31782         if(this.displayInfo){
31783             this.displayEl = Roo.fly(this.el.dom.firstChild).createChild({cls:'x-paging-info'});
31784         }
31785     },
31786
31787     // private
31788     updateInfo : function(){
31789         if(this.displayEl){
31790             var count = this.ds.getCount();
31791             var msg = count == 0 ?
31792                 this.emptyMsg :
31793                 String.format(
31794                     this.displayMsg,
31795                     this.cursor+1, this.cursor+count, this.ds.getTotalCount()    
31796                 );
31797             this.displayEl.update(msg);
31798         }
31799     },
31800
31801     // private
31802     onLoad : function(ds, r, o){
31803        this.cursor = o.params ? o.params.start : 0;
31804        var d = this.getPageData(), ap = d.activePage, ps = d.pages;
31805
31806        this.afterTextEl.el.innerHTML = String.format(this.afterPageText, d.pages);
31807        this.field.dom.value = ap;
31808        this.first.setDisabled(ap == 1);
31809        this.prev.setDisabled(ap == 1);
31810        this.next.setDisabled(ap == ps);
31811        this.last.setDisabled(ap == ps);
31812        this.loading.enable();
31813        this.updateInfo();
31814     },
31815
31816     // private
31817     getPageData : function(){
31818         var total = this.ds.getTotalCount();
31819         return {
31820             total : total,
31821             activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
31822             pages :  total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
31823         };
31824     },
31825
31826     // private
31827     onLoadError : function(){
31828         this.loading.enable();
31829     },
31830
31831     // private
31832     onPagingKeydown : function(e){
31833         var k = e.getKey();
31834         var d = this.getPageData();
31835         if(k == e.RETURN){
31836             var v = this.field.dom.value, pageNum;
31837             if(!v || isNaN(pageNum = parseInt(v, 10))){
31838                 this.field.dom.value = d.activePage;
31839                 return;
31840             }
31841             pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
31842             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
31843             e.stopEvent();
31844         }
31845         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))
31846         {
31847           var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
31848           this.field.dom.value = pageNum;
31849           this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
31850           e.stopEvent();
31851         }
31852         else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
31853         {
31854           var v = this.field.dom.value, pageNum; 
31855           var increment = (e.shiftKey) ? 10 : 1;
31856           if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN) {
31857             increment *= -1;
31858           }
31859           if(!v || isNaN(pageNum = parseInt(v, 10))) {
31860             this.field.dom.value = d.activePage;
31861             return;
31862           }
31863           else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
31864           {
31865             this.field.dom.value = parseInt(v, 10) + increment;
31866             pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
31867             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
31868           }
31869           e.stopEvent();
31870         }
31871     },
31872
31873     // private
31874     beforeLoad : function(){
31875         if(this.loading){
31876             this.loading.disable();
31877         }
31878     },
31879
31880     // private
31881     onClick : function(which){
31882         var ds = this.ds;
31883         switch(which){
31884             case "first":
31885                 ds.load({params:{start: 0, limit: this.pageSize}});
31886             break;
31887             case "prev":
31888                 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
31889             break;
31890             case "next":
31891                 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
31892             break;
31893             case "last":
31894                 var total = ds.getTotalCount();
31895                 var extra = total % this.pageSize;
31896                 var lastStart = extra ? (total - extra) : total-this.pageSize;
31897                 ds.load({params:{start: lastStart, limit: this.pageSize}});
31898             break;
31899             case "refresh":
31900                 ds.load({params:{start: this.cursor, limit: this.pageSize}});
31901             break;
31902         }
31903     },
31904
31905     /**
31906      * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
31907      * @param {Roo.data.Store} store The data store to unbind
31908      */
31909     unbind : function(ds){
31910         ds.un("beforeload", this.beforeLoad, this);
31911         ds.un("load", this.onLoad, this);
31912         ds.un("loadexception", this.onLoadError, this);
31913         ds.un("remove", this.updateInfo, this);
31914         ds.un("add", this.updateInfo, this);
31915         this.ds = undefined;
31916     },
31917
31918     /**
31919      * Binds the paging toolbar to the specified {@link Roo.data.Store}
31920      * @param {Roo.data.Store} store The data store to bind
31921      */
31922     bind : function(ds){
31923         ds.on("beforeload", this.beforeLoad, this);
31924         ds.on("load", this.onLoad, this);
31925         ds.on("loadexception", this.onLoadError, this);
31926         ds.on("remove", this.updateInfo, this);
31927         ds.on("add", this.updateInfo, this);
31928         this.ds = ds;
31929     }
31930 });/*
31931  * Based on:
31932  * Ext JS Library 1.1.1
31933  * Copyright(c) 2006-2007, Ext JS, LLC.
31934  *
31935  * Originally Released Under LGPL - original licence link has changed is not relivant.
31936  *
31937  * Fork - LGPL
31938  * <script type="text/javascript">
31939  */
31940
31941 /**
31942  * @class Roo.Resizable
31943  * @extends Roo.util.Observable
31944  * <p>Applies drag handles to an element to make it resizable. The drag handles are inserted into the element
31945  * and positioned absolute. Some elements, such as a textarea or image, don't support this. To overcome that, you can wrap
31946  * 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
31947  * the element will be wrapped for you automatically.</p>
31948  * <p>Here is the list of valid resize handles:</p>
31949  * <pre>
31950 Value   Description
31951 ------  -------------------
31952  'n'     north
31953  's'     south
31954  'e'     east
31955  'w'     west
31956  'nw'    northwest
31957  'sw'    southwest
31958  'se'    southeast
31959  'ne'    northeast
31960  'hd'    horizontal drag
31961  'all'   all
31962 </pre>
31963  * <p>Here's an example showing the creation of a typical Resizable:</p>
31964  * <pre><code>
31965 var resizer = new Roo.Resizable("element-id", {
31966     handles: 'all',
31967     minWidth: 200,
31968     minHeight: 100,
31969     maxWidth: 500,
31970     maxHeight: 400,
31971     pinned: true
31972 });
31973 resizer.on("resize", myHandler);
31974 </code></pre>
31975  * <p>To hide a particular handle, set its display to none in CSS, or through script:<br>
31976  * resizer.east.setDisplayed(false);</p>
31977  * @cfg {Boolean/String/Element} resizeChild True to resize the first child, or id/element to resize (defaults to false)
31978  * @cfg {Array/String} adjustments String "auto" or an array [width, height] with values to be <b>added</b> to the
31979  * resize operation's new size (defaults to [0, 0])
31980  * @cfg {Number} minWidth The minimum width for the element (defaults to 5)
31981  * @cfg {Number} minHeight The minimum height for the element (defaults to 5)
31982  * @cfg {Number} maxWidth The maximum width for the element (defaults to 10000)
31983  * @cfg {Number} maxHeight The maximum height for the element (defaults to 10000)
31984  * @cfg {Boolean} enabled False to disable resizing (defaults to true)
31985  * @cfg {Boolean} wrap True to wrap an element with a div if needed (required for textareas and images, defaults to false)
31986  * @cfg {Number} width The width of the element in pixels (defaults to null)
31987  * @cfg {Number} height The height of the element in pixels (defaults to null)
31988  * @cfg {Boolean} animate True to animate the resize (not compatible with dynamic sizing, defaults to false)
31989  * @cfg {Number} duration Animation duration if animate = true (defaults to .35)
31990  * @cfg {Boolean} dynamic True to resize the element while dragging instead of using a proxy (defaults to false)
31991  * @cfg {String} handles String consisting of the resize handles to display (defaults to undefined)
31992  * @cfg {Boolean} multiDirectional <b>Deprecated</b>.  The old style of adding multi-direction resize handles, deprecated
31993  * in favor of the handles config option (defaults to false)
31994  * @cfg {Boolean} disableTrackOver True to disable mouse tracking. This is only applied at config time. (defaults to false)
31995  * @cfg {String} easing Animation easing if animate = true (defaults to 'easingOutStrong')
31996  * @cfg {Number} widthIncrement The increment to snap the width resize in pixels (dynamic must be true, defaults to 0)
31997  * @cfg {Number} heightIncrement The increment to snap the height resize in pixels (dynamic must be true, defaults to 0)
31998  * @cfg {Boolean} pinned True to ensure that the resize handles are always visible, false to display them only when the
31999  * user mouses over the resizable borders. This is only applied at config time. (defaults to false)
32000  * @cfg {Boolean} preserveRatio True to preserve the original ratio between height and width during resize (defaults to false)
32001  * @cfg {Boolean} transparent True for transparent handles. This is only applied at config time. (defaults to false)
32002  * @cfg {Number} minX The minimum allowed page X for the element (only used for west resizing, defaults to 0)
32003  * @cfg {Number} minY The minimum allowed page Y for the element (only used for north resizing, defaults to 0)
32004  * @cfg {Boolean} draggable Convenience to initialize drag drop (defaults to false)
32005  * @constructor
32006  * Create a new resizable component
32007  * @param {String/HTMLElement/Roo.Element} el The id or element to resize
32008  * @param {Object} config configuration options
32009   */
32010 Roo.Resizable = function(el, config)
32011 {
32012     this.el = Roo.get(el);
32013
32014     if(config && config.wrap){
32015         config.resizeChild = this.el;
32016         this.el = this.el.wrap(typeof config.wrap == "object" ? config.wrap : {cls:"xresizable-wrap"});
32017         this.el.id = this.el.dom.id = config.resizeChild.id + "-rzwrap";
32018         this.el.setStyle("overflow", "hidden");
32019         this.el.setPositioning(config.resizeChild.getPositioning());
32020         config.resizeChild.clearPositioning();
32021         if(!config.width || !config.height){
32022             var csize = config.resizeChild.getSize();
32023             this.el.setSize(csize.width, csize.height);
32024         }
32025         if(config.pinned && !config.adjustments){
32026             config.adjustments = "auto";
32027         }
32028     }
32029
32030     this.proxy = this.el.createProxy({tag: "div", cls: "x-resizable-proxy", id: this.el.id + "-rzproxy"});
32031     this.proxy.unselectable();
32032     this.proxy.enableDisplayMode('block');
32033
32034     Roo.apply(this, config);
32035
32036     if(this.pinned){
32037         this.disableTrackOver = true;
32038         this.el.addClass("x-resizable-pinned");
32039     }
32040     // if the element isn't positioned, make it relative
32041     var position = this.el.getStyle("position");
32042     if(position != "absolute" && position != "fixed"){
32043         this.el.setStyle("position", "relative");
32044     }
32045     if(!this.handles){ // no handles passed, must be legacy style
32046         this.handles = 's,e,se';
32047         if(this.multiDirectional){
32048             this.handles += ',n,w';
32049         }
32050     }
32051     if(this.handles == "all"){
32052         this.handles = "n s e w ne nw se sw";
32053     }
32054     var hs = this.handles.split(/\s*?[,;]\s*?| /);
32055     var ps = Roo.Resizable.positions;
32056     for(var i = 0, len = hs.length; i < len; i++){
32057         if(hs[i] && ps[hs[i]]){
32058             var pos = ps[hs[i]];
32059             this[pos] = new Roo.Resizable.Handle(this, pos, this.disableTrackOver, this.transparent);
32060         }
32061     }
32062     // legacy
32063     this.corner = this.southeast;
32064     
32065     // updateBox = the box can move..
32066     if(this.handles.indexOf("n") != -1 || this.handles.indexOf("w") != -1 || this.handles.indexOf("hd") != -1) {
32067         this.updateBox = true;
32068     }
32069
32070     this.activeHandle = null;
32071
32072     if(this.resizeChild){
32073         if(typeof this.resizeChild == "boolean"){
32074             this.resizeChild = Roo.get(this.el.dom.firstChild, true);
32075         }else{
32076             this.resizeChild = Roo.get(this.resizeChild, true);
32077         }
32078     }
32079     
32080     if(this.adjustments == "auto"){
32081         var rc = this.resizeChild;
32082         var hw = this.west, he = this.east, hn = this.north, hs = this.south;
32083         if(rc && (hw || hn)){
32084             rc.position("relative");
32085             rc.setLeft(hw ? hw.el.getWidth() : 0);
32086             rc.setTop(hn ? hn.el.getHeight() : 0);
32087         }
32088         this.adjustments = [
32089             (he ? -he.el.getWidth() : 0) + (hw ? -hw.el.getWidth() : 0),
32090             (hn ? -hn.el.getHeight() : 0) + (hs ? -hs.el.getHeight() : 0) -1
32091         ];
32092     }
32093
32094     if(this.draggable){
32095         this.dd = this.dynamic ?
32096             this.el.initDD(null) : this.el.initDDProxy(null, {dragElId: this.proxy.id});
32097         this.dd.setHandleElId(this.resizeChild ? this.resizeChild.id : this.el.id);
32098     }
32099
32100     // public events
32101     this.addEvents({
32102         /**
32103          * @event beforeresize
32104          * Fired before resize is allowed. Set enabled to false to cancel resize.
32105          * @param {Roo.Resizable} this
32106          * @param {Roo.EventObject} e The mousedown event
32107          */
32108         "beforeresize" : true,
32109         /**
32110          * @event resizing
32111          * Fired a resizing.
32112          * @param {Roo.Resizable} this
32113          * @param {Number} x The new x position
32114          * @param {Number} y The new y position
32115          * @param {Number} w The new w width
32116          * @param {Number} h The new h hight
32117          * @param {Roo.EventObject} e The mouseup event
32118          */
32119         "resizing" : true,
32120         /**
32121          * @event resize
32122          * Fired after a resize.
32123          * @param {Roo.Resizable} this
32124          * @param {Number} width The new width
32125          * @param {Number} height The new height
32126          * @param {Roo.EventObject} e The mouseup event
32127          */
32128         "resize" : true
32129     });
32130
32131     if(this.width !== null && this.height !== null){
32132         this.resizeTo(this.width, this.height);
32133     }else{
32134         this.updateChildSize();
32135     }
32136     if(Roo.isIE){
32137         this.el.dom.style.zoom = 1;
32138     }
32139     Roo.Resizable.superclass.constructor.call(this);
32140 };
32141
32142 Roo.extend(Roo.Resizable, Roo.util.Observable, {
32143         resizeChild : false,
32144         adjustments : [0, 0],
32145         minWidth : 5,
32146         minHeight : 5,
32147         maxWidth : 10000,
32148         maxHeight : 10000,
32149         enabled : true,
32150         animate : false,
32151         duration : .35,
32152         dynamic : false,
32153         handles : false,
32154         multiDirectional : false,
32155         disableTrackOver : false,
32156         easing : 'easeOutStrong',
32157         widthIncrement : 0,
32158         heightIncrement : 0,
32159         pinned : false,
32160         width : null,
32161         height : null,
32162         preserveRatio : false,
32163         transparent: false,
32164         minX: 0,
32165         minY: 0,
32166         draggable: false,
32167
32168         /**
32169          * @cfg {String/HTMLElement/Element} constrainTo Constrain the resize to a particular element
32170          */
32171         constrainTo: undefined,
32172         /**
32173          * @cfg {Roo.lib.Region} resizeRegion Constrain the resize to a particular region
32174          */
32175         resizeRegion: undefined,
32176
32177
32178     /**
32179      * Perform a manual resize
32180      * @param {Number} width
32181      * @param {Number} height
32182      */
32183     resizeTo : function(width, height){
32184         this.el.setSize(width, height);
32185         this.updateChildSize();
32186         this.fireEvent("resize", this, width, height, null);
32187     },
32188
32189     // private
32190     startSizing : function(e, handle){
32191         this.fireEvent("beforeresize", this, e);
32192         if(this.enabled){ // 2nd enabled check in case disabled before beforeresize handler
32193
32194             if(!this.overlay){
32195                 this.overlay = this.el.createProxy({tag: "div", cls: "x-resizable-overlay", html: "&#160;"});
32196                 this.overlay.unselectable();
32197                 this.overlay.enableDisplayMode("block");
32198                 this.overlay.on("mousemove", this.onMouseMove, this);
32199                 this.overlay.on("mouseup", this.onMouseUp, this);
32200             }
32201             this.overlay.setStyle("cursor", handle.el.getStyle("cursor"));
32202
32203             this.resizing = true;
32204             this.startBox = this.el.getBox();
32205             this.startPoint = e.getXY();
32206             this.offsets = [(this.startBox.x + this.startBox.width) - this.startPoint[0],
32207                             (this.startBox.y + this.startBox.height) - this.startPoint[1]];
32208
32209             this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
32210             this.overlay.show();
32211
32212             if(this.constrainTo) {
32213                 var ct = Roo.get(this.constrainTo);
32214                 this.resizeRegion = ct.getRegion().adjust(
32215                     ct.getFrameWidth('t'),
32216                     ct.getFrameWidth('l'),
32217                     -ct.getFrameWidth('b'),
32218                     -ct.getFrameWidth('r')
32219                 );
32220             }
32221
32222             this.proxy.setStyle('visibility', 'hidden'); // workaround display none
32223             this.proxy.show();
32224             this.proxy.setBox(this.startBox);
32225             if(!this.dynamic){
32226                 this.proxy.setStyle('visibility', 'visible');
32227             }
32228         }
32229     },
32230
32231     // private
32232     onMouseDown : function(handle, e){
32233         if(this.enabled){
32234             e.stopEvent();
32235             this.activeHandle = handle;
32236             this.startSizing(e, handle);
32237         }
32238     },
32239
32240     // private
32241     onMouseUp : function(e){
32242         var size = this.resizeElement();
32243         this.resizing = false;
32244         this.handleOut();
32245         this.overlay.hide();
32246         this.proxy.hide();
32247         this.fireEvent("resize", this, size.width, size.height, e);
32248     },
32249
32250     // private
32251     updateChildSize : function(){
32252         
32253         if(this.resizeChild){
32254             var el = this.el;
32255             var child = this.resizeChild;
32256             var adj = this.adjustments;
32257             if(el.dom.offsetWidth){
32258                 var b = el.getSize(true);
32259                 child.setSize(b.width+adj[0], b.height+adj[1]);
32260             }
32261             // Second call here for IE
32262             // The first call enables instant resizing and
32263             // the second call corrects scroll bars if they
32264             // exist
32265             if(Roo.isIE){
32266                 setTimeout(function(){
32267                     if(el.dom.offsetWidth){
32268                         var b = el.getSize(true);
32269                         child.setSize(b.width+adj[0], b.height+adj[1]);
32270                     }
32271                 }, 10);
32272             }
32273         }
32274     },
32275
32276     // private
32277     snap : function(value, inc, min){
32278         if(!inc || !value) {
32279             return value;
32280         }
32281         var newValue = value;
32282         var m = value % inc;
32283         if(m > 0){
32284             if(m > (inc/2)){
32285                 newValue = value + (inc-m);
32286             }else{
32287                 newValue = value - m;
32288             }
32289         }
32290         return Math.max(min, newValue);
32291     },
32292
32293     // private
32294     resizeElement : function(){
32295         var box = this.proxy.getBox();
32296         if(this.updateBox){
32297             this.el.setBox(box, false, this.animate, this.duration, null, this.easing);
32298         }else{
32299             this.el.setSize(box.width, box.height, this.animate, this.duration, null, this.easing);
32300         }
32301         this.updateChildSize();
32302         if(!this.dynamic){
32303             this.proxy.hide();
32304         }
32305         return box;
32306     },
32307
32308     // private
32309     constrain : function(v, diff, m, mx){
32310         if(v - diff < m){
32311             diff = v - m;
32312         }else if(v - diff > mx){
32313             diff = mx - v;
32314         }
32315         return diff;
32316     },
32317
32318     // private
32319     onMouseMove : function(e){
32320         
32321         if(this.enabled){
32322             try{// try catch so if something goes wrong the user doesn't get hung
32323
32324             if(this.resizeRegion && !this.resizeRegion.contains(e.getPoint())) {
32325                 return;
32326             }
32327
32328             //var curXY = this.startPoint;
32329             var curSize = this.curSize || this.startBox;
32330             var x = this.startBox.x, y = this.startBox.y;
32331             var ox = x, oy = y;
32332             var w = curSize.width, h = curSize.height;
32333             var ow = w, oh = h;
32334             var mw = this.minWidth, mh = this.minHeight;
32335             var mxw = this.maxWidth, mxh = this.maxHeight;
32336             var wi = this.widthIncrement;
32337             var hi = this.heightIncrement;
32338
32339             var eventXY = e.getXY();
32340             var diffX = -(this.startPoint[0] - Math.max(this.minX, eventXY[0]));
32341             var diffY = -(this.startPoint[1] - Math.max(this.minY, eventXY[1]));
32342
32343             var pos = this.activeHandle.position;
32344
32345             switch(pos){
32346                 case "east":
32347                     w += diffX;
32348                     w = Math.min(Math.max(mw, w), mxw);
32349                     break;
32350              
32351                 case "south":
32352                     h += diffY;
32353                     h = Math.min(Math.max(mh, h), mxh);
32354                     break;
32355                 case "southeast":
32356                     w += diffX;
32357                     h += diffY;
32358                     w = Math.min(Math.max(mw, w), mxw);
32359                     h = Math.min(Math.max(mh, h), mxh);
32360                     break;
32361                 case "north":
32362                     diffY = this.constrain(h, diffY, mh, mxh);
32363                     y += diffY;
32364                     h -= diffY;
32365                     break;
32366                 case "hdrag":
32367                     
32368                     if (wi) {
32369                         var adiffX = Math.abs(diffX);
32370                         var sub = (adiffX % wi); // how much 
32371                         if (sub > (wi/2)) { // far enough to snap
32372                             diffX = (diffX > 0) ? diffX-sub + wi : diffX+sub - wi;
32373                         } else {
32374                             // remove difference.. 
32375                             diffX = (diffX > 0) ? diffX-sub : diffX+sub;
32376                         }
32377                     }
32378                     x += diffX;
32379                     x = Math.max(this.minX, x);
32380                     break;
32381                 case "west":
32382                     diffX = this.constrain(w, diffX, mw, mxw);
32383                     x += diffX;
32384                     w -= diffX;
32385                     break;
32386                 case "northeast":
32387                     w += diffX;
32388                     w = Math.min(Math.max(mw, w), mxw);
32389                     diffY = this.constrain(h, diffY, mh, mxh);
32390                     y += diffY;
32391                     h -= diffY;
32392                     break;
32393                 case "northwest":
32394                     diffX = this.constrain(w, diffX, mw, mxw);
32395                     diffY = this.constrain(h, diffY, mh, mxh);
32396                     y += diffY;
32397                     h -= diffY;
32398                     x += diffX;
32399                     w -= diffX;
32400                     break;
32401                case "southwest":
32402                     diffX = this.constrain(w, diffX, mw, mxw);
32403                     h += diffY;
32404                     h = Math.min(Math.max(mh, h), mxh);
32405                     x += diffX;
32406                     w -= diffX;
32407                     break;
32408             }
32409
32410             var sw = this.snap(w, wi, mw);
32411             var sh = this.snap(h, hi, mh);
32412             if(sw != w || sh != h){
32413                 switch(pos){
32414                     case "northeast":
32415                         y -= sh - h;
32416                     break;
32417                     case "north":
32418                         y -= sh - h;
32419                         break;
32420                     case "southwest":
32421                         x -= sw - w;
32422                     break;
32423                     case "west":
32424                         x -= sw - w;
32425                         break;
32426                     case "northwest":
32427                         x -= sw - w;
32428                         y -= sh - h;
32429                     break;
32430                 }
32431                 w = sw;
32432                 h = sh;
32433             }
32434
32435             if(this.preserveRatio){
32436                 switch(pos){
32437                     case "southeast":
32438                     case "east":
32439                         h = oh * (w/ow);
32440                         h = Math.min(Math.max(mh, h), mxh);
32441                         w = ow * (h/oh);
32442                        break;
32443                     case "south":
32444                         w = ow * (h/oh);
32445                         w = Math.min(Math.max(mw, w), mxw);
32446                         h = oh * (w/ow);
32447                         break;
32448                     case "northeast":
32449                         w = ow * (h/oh);
32450                         w = Math.min(Math.max(mw, w), mxw);
32451                         h = oh * (w/ow);
32452                     break;
32453                     case "north":
32454                         var tw = w;
32455                         w = ow * (h/oh);
32456                         w = Math.min(Math.max(mw, w), mxw);
32457                         h = oh * (w/ow);
32458                         x += (tw - w) / 2;
32459                         break;
32460                     case "southwest":
32461                         h = oh * (w/ow);
32462                         h = Math.min(Math.max(mh, h), mxh);
32463                         var tw = w;
32464                         w = ow * (h/oh);
32465                         x += tw - w;
32466                         break;
32467                     case "west":
32468                         var th = h;
32469                         h = oh * (w/ow);
32470                         h = Math.min(Math.max(mh, h), mxh);
32471                         y += (th - h) / 2;
32472                         var tw = w;
32473                         w = ow * (h/oh);
32474                         x += tw - w;
32475                        break;
32476                     case "northwest":
32477                         var tw = w;
32478                         var th = h;
32479                         h = oh * (w/ow);
32480                         h = Math.min(Math.max(mh, h), mxh);
32481                         w = ow * (h/oh);
32482                         y += th - h;
32483                         x += tw - w;
32484                        break;
32485
32486                 }
32487             }
32488             if (pos == 'hdrag') {
32489                 w = ow;
32490             }
32491             this.proxy.setBounds(x, y, w, h);
32492             if(this.dynamic){
32493                 this.resizeElement();
32494             }
32495             }catch(e){}
32496         }
32497         this.fireEvent("resizing", this, x, y, w, h, e);
32498     },
32499
32500     // private
32501     handleOver : function(){
32502         if(this.enabled){
32503             this.el.addClass("x-resizable-over");
32504         }
32505     },
32506
32507     // private
32508     handleOut : function(){
32509         if(!this.resizing){
32510             this.el.removeClass("x-resizable-over");
32511         }
32512     },
32513
32514     /**
32515      * Returns the element this component is bound to.
32516      * @return {Roo.Element}
32517      */
32518     getEl : function(){
32519         return this.el;
32520     },
32521
32522     /**
32523      * Returns the resizeChild element (or null).
32524      * @return {Roo.Element}
32525      */
32526     getResizeChild : function(){
32527         return this.resizeChild;
32528     },
32529     groupHandler : function()
32530     {
32531         
32532     },
32533     /**
32534      * Destroys this resizable. If the element was wrapped and
32535      * removeEl is not true then the element remains.
32536      * @param {Boolean} removeEl (optional) true to remove the element from the DOM
32537      */
32538     destroy : function(removeEl){
32539         this.proxy.remove();
32540         if(this.overlay){
32541             this.overlay.removeAllListeners();
32542             this.overlay.remove();
32543         }
32544         var ps = Roo.Resizable.positions;
32545         for(var k in ps){
32546             if(typeof ps[k] != "function" && this[ps[k]]){
32547                 var h = this[ps[k]];
32548                 h.el.removeAllListeners();
32549                 h.el.remove();
32550             }
32551         }
32552         if(removeEl){
32553             this.el.update("");
32554             this.el.remove();
32555         }
32556     }
32557 });
32558
32559 // private
32560 // hash to map config positions to true positions
32561 Roo.Resizable.positions = {
32562     n: "north", s: "south", e: "east", w: "west", se: "southeast", sw: "southwest", nw: "northwest", ne: "northeast", 
32563     hd: "hdrag"
32564 };
32565
32566 // private
32567 Roo.Resizable.Handle = function(rz, pos, disableTrackOver, transparent){
32568     if(!this.tpl){
32569         // only initialize the template if resizable is used
32570         var tpl = Roo.DomHelper.createTemplate(
32571             {tag: "div", cls: "x-resizable-handle x-resizable-handle-{0}"}
32572         );
32573         tpl.compile();
32574         Roo.Resizable.Handle.prototype.tpl = tpl;
32575     }
32576     this.position = pos;
32577     this.rz = rz;
32578     // show north drag fro topdra
32579     var handlepos = pos == 'hdrag' ? 'north' : pos;
32580     
32581     this.el = this.tpl.append(rz.el.dom, [handlepos], true);
32582     if (pos == 'hdrag') {
32583         this.el.setStyle('cursor', 'pointer');
32584     }
32585     this.el.unselectable();
32586     if(transparent){
32587         this.el.setOpacity(0);
32588     }
32589     this.el.on("mousedown", this.onMouseDown, this);
32590     if(!disableTrackOver){
32591         this.el.on("mouseover", this.onMouseOver, this);
32592         this.el.on("mouseout", this.onMouseOut, this);
32593     }
32594 };
32595
32596 // private
32597 Roo.Resizable.Handle.prototype = {
32598     afterResize : function(rz){
32599         Roo.log('after?');
32600         // do nothing
32601     },
32602     // private
32603     onMouseDown : function(e){
32604         this.rz.onMouseDown(this, e);
32605     },
32606     // private
32607     onMouseOver : function(e){
32608         this.rz.handleOver(this, e);
32609     },
32610     // private
32611     onMouseOut : function(e){
32612         this.rz.handleOut(this, e);
32613     }
32614 };/*
32615  * Based on:
32616  * Ext JS Library 1.1.1
32617  * Copyright(c) 2006-2007, Ext JS, LLC.
32618  *
32619  * Originally Released Under LGPL - original licence link has changed is not relivant.
32620  *
32621  * Fork - LGPL
32622  * <script type="text/javascript">
32623  */
32624
32625 /**
32626  * @class Roo.Editor
32627  * @extends Roo.Component
32628  * A base editor field that handles displaying/hiding on demand and has some built-in sizing and event handling logic.
32629  * @constructor
32630  * Create a new Editor
32631  * @param {Roo.form.Field} field The Field object (or descendant)
32632  * @param {Object} config The config object
32633  */
32634 Roo.Editor = function(field, config){
32635     Roo.Editor.superclass.constructor.call(this, config);
32636     this.field = field;
32637     this.addEvents({
32638         /**
32639              * @event beforestartedit
32640              * Fires when editing is initiated, but before the value changes.  Editing can be canceled by returning
32641              * false from the handler of this event.
32642              * @param {Editor} this
32643              * @param {Roo.Element} boundEl The underlying element bound to this editor
32644              * @param {Mixed} value The field value being set
32645              */
32646         "beforestartedit" : true,
32647         /**
32648              * @event startedit
32649              * Fires when this editor is displayed
32650              * @param {Roo.Element} boundEl The underlying element bound to this editor
32651              * @param {Mixed} value The starting field value
32652              */
32653         "startedit" : true,
32654         /**
32655              * @event beforecomplete
32656              * Fires after a change has been made to the field, but before the change is reflected in the underlying
32657              * field.  Saving the change to the field can be canceled by returning false from the handler of this event.
32658              * Note that if the value has not changed and ignoreNoChange = true, the editing will still end but this
32659              * event will not fire since no edit actually occurred.
32660              * @param {Editor} this
32661              * @param {Mixed} value The current field value
32662              * @param {Mixed} startValue The original field value
32663              */
32664         "beforecomplete" : true,
32665         /**
32666              * @event complete
32667              * Fires after editing is complete and any changed value has been written to the underlying field.
32668              * @param {Editor} this
32669              * @param {Mixed} value The current field value
32670              * @param {Mixed} startValue The original field value
32671              */
32672         "complete" : true,
32673         /**
32674          * @event specialkey
32675          * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
32676          * {@link Roo.EventObject#getKey} to determine which key was pressed.
32677          * @param {Roo.form.Field} this
32678          * @param {Roo.EventObject} e The event object
32679          */
32680         "specialkey" : true
32681     });
32682 };
32683
32684 Roo.extend(Roo.Editor, Roo.Component, {
32685     /**
32686      * @cfg {Boolean/String} autosize
32687      * True for the editor to automatically adopt the size of the underlying field, "width" to adopt the width only,
32688      * or "height" to adopt the height only (defaults to false)
32689      */
32690     /**
32691      * @cfg {Boolean} revertInvalid
32692      * True to automatically revert the field value and cancel the edit when the user completes an edit and the field
32693      * validation fails (defaults to true)
32694      */
32695     /**
32696      * @cfg {Boolean} ignoreNoChange
32697      * True to skip the the edit completion process (no save, no events fired) if the user completes an edit and
32698      * the value has not changed (defaults to false).  Applies only to string values - edits for other data types
32699      * will never be ignored.
32700      */
32701     /**
32702      * @cfg {Boolean} hideEl
32703      * False to keep the bound element visible while the editor is displayed (defaults to true)
32704      */
32705     /**
32706      * @cfg {Mixed} value
32707      * The data value of the underlying field (defaults to "")
32708      */
32709     value : "",
32710     /**
32711      * @cfg {String} alignment
32712      * The position to align to (see {@link Roo.Element#alignTo} for more details, defaults to "c-c?").
32713      */
32714     alignment: "c-c?",
32715     /**
32716      * @cfg {Boolean/String} shadow "sides" for sides/bottom only, "frame" for 4-way shadow, and "drop"
32717      * for bottom-right shadow (defaults to "frame")
32718      */
32719     shadow : "frame",
32720     /**
32721      * @cfg {Boolean} constrain True to constrain the editor to the viewport
32722      */
32723     constrain : false,
32724     /**
32725      * @cfg {Boolean} completeOnEnter True to complete the edit when the enter key is pressed (defaults to false)
32726      */
32727     completeOnEnter : false,
32728     /**
32729      * @cfg {Boolean} cancelOnEsc True to cancel the edit when the escape key is pressed (defaults to false)
32730      */
32731     cancelOnEsc : false,
32732     /**
32733      * @cfg {Boolean} updateEl True to update the innerHTML of the bound element when the update completes (defaults to false)
32734      */
32735     updateEl : false,
32736
32737     // private
32738     onRender : function(ct, position){
32739         this.el = new Roo.Layer({
32740             shadow: this.shadow,
32741             cls: "x-editor",
32742             parentEl : ct,
32743             shim : this.shim,
32744             shadowOffset:4,
32745             id: this.id,
32746             constrain: this.constrain
32747         });
32748         this.el.setStyle("overflow", Roo.isGecko ? "auto" : "hidden");
32749         if(this.field.msgTarget != 'title'){
32750             this.field.msgTarget = 'qtip';
32751         }
32752         this.field.render(this.el);
32753         if(Roo.isGecko){
32754             this.field.el.dom.setAttribute('autocomplete', 'off');
32755         }
32756         this.field.on("specialkey", this.onSpecialKey, this);
32757         if(this.swallowKeys){
32758             this.field.el.swallowEvent(['keydown','keypress']);
32759         }
32760         this.field.show();
32761         this.field.on("blur", this.onBlur, this);
32762         if(this.field.grow){
32763             this.field.on("autosize", this.el.sync,  this.el, {delay:1});
32764         }
32765     },
32766
32767     onSpecialKey : function(field, e)
32768     {
32769         //Roo.log('editor onSpecialKey');
32770         if(this.completeOnEnter && e.getKey() == e.ENTER){
32771             e.stopEvent();
32772             this.completeEdit();
32773             return;
32774         }
32775         // do not fire special key otherwise it might hide close the editor...
32776         if(e.getKey() == e.ENTER){    
32777             return;
32778         }
32779         if(this.cancelOnEsc && e.getKey() == e.ESC){
32780             this.cancelEdit();
32781             return;
32782         } 
32783         this.fireEvent('specialkey', field, e);
32784     
32785     },
32786
32787     /**
32788      * Starts the editing process and shows the editor.
32789      * @param {String/HTMLElement/Element} el The element to edit
32790      * @param {String} value (optional) A value to initialize the editor with. If a value is not provided, it defaults
32791       * to the innerHTML of el.
32792      */
32793     startEdit : function(el, value){
32794         if(this.editing){
32795             this.completeEdit();
32796         }
32797         this.boundEl = Roo.get(el);
32798         var v = value !== undefined ? value : this.boundEl.dom.innerHTML;
32799         if(!this.rendered){
32800             this.render(this.parentEl || document.body);
32801         }
32802         if(this.fireEvent("beforestartedit", this, this.boundEl, v) === false){
32803             return;
32804         }
32805         this.startValue = v;
32806         this.field.setValue(v);
32807         if(this.autoSize){
32808             var sz = this.boundEl.getSize();
32809             switch(this.autoSize){
32810                 case "width":
32811                 this.setSize(sz.width,  "");
32812                 break;
32813                 case "height":
32814                 this.setSize("",  sz.height);
32815                 break;
32816                 default:
32817                 this.setSize(sz.width,  sz.height);
32818             }
32819         }
32820         this.el.alignTo(this.boundEl, this.alignment);
32821         this.editing = true;
32822         if(Roo.QuickTips){
32823             Roo.QuickTips.disable();
32824         }
32825         this.show();
32826     },
32827
32828     /**
32829      * Sets the height and width of this editor.
32830      * @param {Number} width The new width
32831      * @param {Number} height The new height
32832      */
32833     setSize : function(w, h){
32834         this.field.setSize(w, h);
32835         if(this.el){
32836             this.el.sync();
32837         }
32838     },
32839
32840     /**
32841      * Realigns the editor to the bound field based on the current alignment config value.
32842      */
32843     realign : function(){
32844         this.el.alignTo(this.boundEl, this.alignment);
32845     },
32846
32847     /**
32848      * Ends the editing process, persists the changed value to the underlying field, and hides the editor.
32849      * @param {Boolean} remainVisible Override the default behavior and keep the editor visible after edit (defaults to false)
32850      */
32851     completeEdit : function(remainVisible){
32852         if(!this.editing){
32853             return;
32854         }
32855         var v = this.getValue();
32856         if(this.revertInvalid !== false && !this.field.isValid()){
32857             v = this.startValue;
32858             this.cancelEdit(true);
32859         }
32860         if(String(v) === String(this.startValue) && this.ignoreNoChange){
32861             this.editing = false;
32862             this.hide();
32863             return;
32864         }
32865         if(this.fireEvent("beforecomplete", this, v, this.startValue) !== false){
32866             this.editing = false;
32867             if(this.updateEl && this.boundEl){
32868                 this.boundEl.update(v);
32869             }
32870             if(remainVisible !== true){
32871                 this.hide();
32872             }
32873             this.fireEvent("complete", this, v, this.startValue);
32874         }
32875     },
32876
32877     // private
32878     onShow : function(){
32879         this.el.show();
32880         if(this.hideEl !== false){
32881             this.boundEl.hide();
32882         }
32883         this.field.show();
32884         if(Roo.isIE && !this.fixIEFocus){ // IE has problems with focusing the first time
32885             this.fixIEFocus = true;
32886             this.deferredFocus.defer(50, this);
32887         }else{
32888             this.field.focus();
32889         }
32890         this.fireEvent("startedit", this.boundEl, this.startValue);
32891     },
32892
32893     deferredFocus : function(){
32894         if(this.editing){
32895             this.field.focus();
32896         }
32897     },
32898
32899     /**
32900      * Cancels the editing process and hides the editor without persisting any changes.  The field value will be
32901      * reverted to the original starting value.
32902      * @param {Boolean} remainVisible Override the default behavior and keep the editor visible after
32903      * cancel (defaults to false)
32904      */
32905     cancelEdit : function(remainVisible){
32906         if(this.editing){
32907             this.setValue(this.startValue);
32908             if(remainVisible !== true){
32909                 this.hide();
32910             }
32911         }
32912     },
32913
32914     // private
32915     onBlur : function(){
32916         if(this.allowBlur !== true && this.editing){
32917             this.completeEdit();
32918         }
32919     },
32920
32921     // private
32922     onHide : function(){
32923         if(this.editing){
32924             this.completeEdit();
32925             return;
32926         }
32927         this.field.blur();
32928         if(this.field.collapse){
32929             this.field.collapse();
32930         }
32931         this.el.hide();
32932         if(this.hideEl !== false){
32933             this.boundEl.show();
32934         }
32935         if(Roo.QuickTips){
32936             Roo.QuickTips.enable();
32937         }
32938     },
32939
32940     /**
32941      * Sets the data value of the editor
32942      * @param {Mixed} value Any valid value supported by the underlying field
32943      */
32944     setValue : function(v){
32945         this.field.setValue(v);
32946     },
32947
32948     /**
32949      * Gets the data value of the editor
32950      * @return {Mixed} The data value
32951      */
32952     getValue : function(){
32953         return this.field.getValue();
32954     }
32955 });/*
32956  * Based on:
32957  * Ext JS Library 1.1.1
32958  * Copyright(c) 2006-2007, Ext JS, LLC.
32959  *
32960  * Originally Released Under LGPL - original licence link has changed is not relivant.
32961  *
32962  * Fork - LGPL
32963  * <script type="text/javascript">
32964  */
32965  
32966 /**
32967  * @class Roo.BasicDialog
32968  * @extends Roo.util.Observable
32969  * Lightweight Dialog Class.  The code below shows the creation of a typical dialog using existing HTML markup:
32970  * <pre><code>
32971 var dlg = new Roo.BasicDialog("my-dlg", {
32972     height: 200,
32973     width: 300,
32974     minHeight: 100,
32975     minWidth: 150,
32976     modal: true,
32977     proxyDrag: true,
32978     shadow: true
32979 });
32980 dlg.addKeyListener(27, dlg.hide, dlg); // ESC can also close the dialog
32981 dlg.addButton('OK', dlg.hide, dlg);    // Could call a save function instead of hiding
32982 dlg.addButton('Cancel', dlg.hide, dlg);
32983 dlg.show();
32984 </code></pre>
32985   <b>A Dialog should always be a direct child of the body element.</b>
32986  * @cfg {Boolean/DomHelper} autoCreate True to auto create from scratch, or using a DomHelper Object (defaults to false)
32987  * @cfg {String} title Default text to display in the title bar (defaults to null)
32988  * @cfg {Number} width Width of the dialog in pixels (can also be set via CSS).  Determined by browser if unspecified.
32989  * @cfg {Number} height Height of the dialog in pixels (can also be set via CSS).  Determined by browser if unspecified.
32990  * @cfg {Number} x The default left page coordinate of the dialog (defaults to center screen)
32991  * @cfg {Number} y The default top page coordinate of the dialog (defaults to center screen)
32992  * @cfg {String/Element} animateTarget Id or element from which the dialog should animate while opening
32993  * (defaults to null with no animation)
32994  * @cfg {Boolean} resizable False to disable manual dialog resizing (defaults to true)
32995  * @cfg {String} resizeHandles Which resize handles to display - see the {@link Roo.Resizable} handles config
32996  * property for valid values (defaults to 'all')
32997  * @cfg {Number} minHeight The minimum allowable height for a resizable dialog (defaults to 80)
32998  * @cfg {Number} minWidth The minimum allowable width for a resizable dialog (defaults to 200)
32999  * @cfg {Boolean} modal True to show the dialog modally, preventing user interaction with the rest of the page (defaults to false)
33000  * @cfg {Boolean} autoScroll True to allow the dialog body contents to overflow and display scrollbars (defaults to false)
33001  * @cfg {Boolean} closable False to remove the built-in top-right corner close button (defaults to true)
33002  * @cfg {Boolean} collapsible False to remove the built-in top-right corner collapse button (defaults to true)
33003  * @cfg {Boolean} constraintoviewport True to keep the dialog constrained within the visible viewport boundaries (defaults to true)
33004  * @cfg {Boolean} syncHeightBeforeShow True to cause the dimensions to be recalculated before the dialog is shown (defaults to false)
33005  * @cfg {Boolean} draggable False to disable dragging of the dialog within the viewport (defaults to true)
33006  * @cfg {Boolean} autoTabs If true, all elements with class 'x-dlg-tab' will get automatically converted to tabs (defaults to false)
33007  * @cfg {String} tabTag The tag name of tab elements, used when autoTabs = true (defaults to 'div')
33008  * @cfg {Boolean} proxyDrag True to drag a lightweight proxy element rather than the dialog itself, used when
33009  * draggable = true (defaults to false)
33010  * @cfg {Boolean} fixedcenter True to ensure that anytime the dialog is shown or resized it gets centered (defaults to false)
33011  * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
33012  * shadow (defaults to false)
33013  * @cfg {Number} shadowOffset The number of pixels to offset the shadow if displayed (defaults to 5)
33014  * @cfg {String} buttonAlign Valid values are "left," "center" and "right" (defaults to "right")
33015  * @cfg {Number} minButtonWidth Minimum width of all dialog buttons (defaults to 75)
33016  * @cfg {Array} buttons Array of buttons
33017  * @cfg {Boolean} shim True to create an iframe shim that prevents selects from showing through (defaults to false)
33018  * @constructor
33019  * Create a new BasicDialog.
33020  * @param {String/HTMLElement/Roo.Element} el The container element or DOM node, or its id
33021  * @param {Object} config Configuration options
33022  */
33023 Roo.BasicDialog = function(el, config){
33024     this.el = Roo.get(el);
33025     var dh = Roo.DomHelper;
33026     if(!this.el && config && config.autoCreate){
33027         if(typeof config.autoCreate == "object"){
33028             if(!config.autoCreate.id){
33029                 config.autoCreate.id = el;
33030             }
33031             this.el = dh.append(document.body,
33032                         config.autoCreate, true);
33033         }else{
33034             this.el = dh.append(document.body,
33035                         {tag: "div", id: el, style:'visibility:hidden;'}, true);
33036         }
33037     }
33038     el = this.el;
33039     el.setDisplayed(true);
33040     el.hide = this.hideAction;
33041     this.id = el.id;
33042     el.addClass("x-dlg");
33043
33044     Roo.apply(this, config);
33045
33046     this.proxy = el.createProxy("x-dlg-proxy");
33047     this.proxy.hide = this.hideAction;
33048     this.proxy.setOpacity(.5);
33049     this.proxy.hide();
33050
33051     if(config.width){
33052         el.setWidth(config.width);
33053     }
33054     if(config.height){
33055         el.setHeight(config.height);
33056     }
33057     this.size = el.getSize();
33058     if(typeof config.x != "undefined" && typeof config.y != "undefined"){
33059         this.xy = [config.x,config.y];
33060     }else{
33061         this.xy = el.getCenterXY(true);
33062     }
33063     /** The header element @type Roo.Element */
33064     this.header = el.child("> .x-dlg-hd");
33065     /** The body element @type Roo.Element */
33066     this.body = el.child("> .x-dlg-bd");
33067     /** The footer element @type Roo.Element */
33068     this.footer = el.child("> .x-dlg-ft");
33069
33070     if(!this.header){
33071         this.header = el.createChild({tag: "div", cls:"x-dlg-hd", html: "&#160;"}, this.body ? this.body.dom : null);
33072     }
33073     if(!this.body){
33074         this.body = el.createChild({tag: "div", cls:"x-dlg-bd"});
33075     }
33076
33077     this.header.unselectable();
33078     if(this.title){
33079         this.header.update(this.title);
33080     }
33081     // this element allows the dialog to be focused for keyboard event
33082     this.focusEl = el.createChild({tag: "a", href:"#", cls:"x-dlg-focus", tabIndex:"-1"});
33083     this.focusEl.swallowEvent("click", true);
33084
33085     this.header.wrap({cls:"x-dlg-hd-right"}).wrap({cls:"x-dlg-hd-left"}, true);
33086
33087     // wrap the body and footer for special rendering
33088     this.bwrap = this.body.wrap({tag: "div", cls:"x-dlg-dlg-body"});
33089     if(this.footer){
33090         this.bwrap.dom.appendChild(this.footer.dom);
33091     }
33092
33093     this.bg = this.el.createChild({
33094         tag: "div", cls:"x-dlg-bg",
33095         html: '<div class="x-dlg-bg-left"><div class="x-dlg-bg-right"><div class="x-dlg-bg-center">&#160;</div></div></div>'
33096     });
33097     this.centerBg = this.bg.child("div.x-dlg-bg-center");
33098
33099
33100     if(this.autoScroll !== false && !this.autoTabs){
33101         this.body.setStyle("overflow", "auto");
33102     }
33103
33104     this.toolbox = this.el.createChild({cls: "x-dlg-toolbox"});
33105
33106     if(this.closable !== false){
33107         this.el.addClass("x-dlg-closable");
33108         this.close = this.toolbox.createChild({cls:"x-dlg-close"});
33109         this.close.on("click", this.closeClick, this);
33110         this.close.addClassOnOver("x-dlg-close-over");
33111     }
33112     if(this.collapsible !== false){
33113         this.collapseBtn = this.toolbox.createChild({cls:"x-dlg-collapse"});
33114         this.collapseBtn.on("click", this.collapseClick, this);
33115         this.collapseBtn.addClassOnOver("x-dlg-collapse-over");
33116         this.header.on("dblclick", this.collapseClick, this);
33117     }
33118     if(this.resizable !== false){
33119         this.el.addClass("x-dlg-resizable");
33120         this.resizer = new Roo.Resizable(el, {
33121             minWidth: this.minWidth || 80,
33122             minHeight:this.minHeight || 80,
33123             handles: this.resizeHandles || "all",
33124             pinned: true
33125         });
33126         this.resizer.on("beforeresize", this.beforeResize, this);
33127         this.resizer.on("resize", this.onResize, this);
33128     }
33129     if(this.draggable !== false){
33130         el.addClass("x-dlg-draggable");
33131         if (!this.proxyDrag) {
33132             var dd = new Roo.dd.DD(el.dom.id, "WindowDrag");
33133         }
33134         else {
33135             var dd = new Roo.dd.DDProxy(el.dom.id, "WindowDrag", {dragElId: this.proxy.id});
33136         }
33137         dd.setHandleElId(this.header.id);
33138         dd.endDrag = this.endMove.createDelegate(this);
33139         dd.startDrag = this.startMove.createDelegate(this);
33140         dd.onDrag = this.onDrag.createDelegate(this);
33141         dd.scroll = false;
33142         this.dd = dd;
33143     }
33144     if(this.modal){
33145         this.mask = dh.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
33146         this.mask.enableDisplayMode("block");
33147         this.mask.hide();
33148         this.el.addClass("x-dlg-modal");
33149     }
33150     if(this.shadow){
33151         this.shadow = new Roo.Shadow({
33152             mode : typeof this.shadow == "string" ? this.shadow : "sides",
33153             offset : this.shadowOffset
33154         });
33155     }else{
33156         this.shadowOffset = 0;
33157     }
33158     if(Roo.useShims && this.shim !== false){
33159         this.shim = this.el.createShim();
33160         this.shim.hide = this.hideAction;
33161         this.shim.hide();
33162     }else{
33163         this.shim = false;
33164     }
33165     if(this.autoTabs){
33166         this.initTabs();
33167     }
33168     if (this.buttons) { 
33169         var bts= this.buttons;
33170         this.buttons = [];
33171         Roo.each(bts, function(b) {
33172             this.addButton(b);
33173         }, this);
33174     }
33175     
33176     
33177     this.addEvents({
33178         /**
33179          * @event keydown
33180          * Fires when a key is pressed
33181          * @param {Roo.BasicDialog} this
33182          * @param {Roo.EventObject} e
33183          */
33184         "keydown" : true,
33185         /**
33186          * @event move
33187          * Fires when this dialog is moved by the user.
33188          * @param {Roo.BasicDialog} this
33189          * @param {Number} x The new page X
33190          * @param {Number} y The new page Y
33191          */
33192         "move" : true,
33193         /**
33194          * @event resize
33195          * Fires when this dialog is resized by the user.
33196          * @param {Roo.BasicDialog} this
33197          * @param {Number} width The new width
33198          * @param {Number} height The new height
33199          */
33200         "resize" : true,
33201         /**
33202          * @event beforehide
33203          * Fires before this dialog is hidden.
33204          * @param {Roo.BasicDialog} this
33205          */
33206         "beforehide" : true,
33207         /**
33208          * @event hide
33209          * Fires when this dialog is hidden.
33210          * @param {Roo.BasicDialog} this
33211          */
33212         "hide" : true,
33213         /**
33214          * @event beforeshow
33215          * Fires before this dialog is shown.
33216          * @param {Roo.BasicDialog} this
33217          */
33218         "beforeshow" : true,
33219         /**
33220          * @event show
33221          * Fires when this dialog is shown.
33222          * @param {Roo.BasicDialog} this
33223          */
33224         "show" : true
33225     });
33226     el.on("keydown", this.onKeyDown, this);
33227     el.on("mousedown", this.toFront, this);
33228     Roo.EventManager.onWindowResize(this.adjustViewport, this, true);
33229     this.el.hide();
33230     Roo.DialogManager.register(this);
33231     Roo.BasicDialog.superclass.constructor.call(this);
33232 };
33233
33234 Roo.extend(Roo.BasicDialog, Roo.util.Observable, {
33235     shadowOffset: Roo.isIE ? 6 : 5,
33236     minHeight: 80,
33237     minWidth: 200,
33238     minButtonWidth: 75,
33239     defaultButton: null,
33240     buttonAlign: "right",
33241     tabTag: 'div',
33242     firstShow: true,
33243
33244     /**
33245      * Sets the dialog title text
33246      * @param {String} text The title text to display
33247      * @return {Roo.BasicDialog} this
33248      */
33249     setTitle : function(text){
33250         this.header.update(text);
33251         return this;
33252     },
33253
33254     // private
33255     closeClick : function(){
33256         this.hide();
33257     },
33258
33259     // private
33260     collapseClick : function(){
33261         this[this.collapsed ? "expand" : "collapse"]();
33262     },
33263
33264     /**
33265      * Collapses the dialog to its minimized state (only the title bar is visible).
33266      * Equivalent to the user clicking the collapse dialog button.
33267      */
33268     collapse : function(){
33269         if(!this.collapsed){
33270             this.collapsed = true;
33271             this.el.addClass("x-dlg-collapsed");
33272             this.restoreHeight = this.el.getHeight();
33273             this.resizeTo(this.el.getWidth(), this.header.getHeight());
33274         }
33275     },
33276
33277     /**
33278      * Expands a collapsed dialog back to its normal state.  Equivalent to the user
33279      * clicking the expand dialog button.
33280      */
33281     expand : function(){
33282         if(this.collapsed){
33283             this.collapsed = false;
33284             this.el.removeClass("x-dlg-collapsed");
33285             this.resizeTo(this.el.getWidth(), this.restoreHeight);
33286         }
33287     },
33288
33289     /**
33290      * Reinitializes the tabs component, clearing out old tabs and finding new ones.
33291      * @return {Roo.TabPanel} The tabs component
33292      */
33293     initTabs : function(){
33294         var tabs = this.getTabs();
33295         while(tabs.getTab(0)){
33296             tabs.removeTab(0);
33297         }
33298         this.el.select(this.tabTag+'.x-dlg-tab').each(function(el){
33299             var dom = el.dom;
33300             tabs.addTab(Roo.id(dom), dom.title);
33301             dom.title = "";
33302         });
33303         tabs.activate(0);
33304         return tabs;
33305     },
33306
33307     // private
33308     beforeResize : function(){
33309         this.resizer.minHeight = Math.max(this.minHeight, this.getHeaderFooterHeight(true)+40);
33310     },
33311
33312     // private
33313     onResize : function(){
33314         this.refreshSize();
33315         this.syncBodyHeight();
33316         this.adjustAssets();
33317         this.focus();
33318         this.fireEvent("resize", this, this.size.width, this.size.height);
33319     },
33320
33321     // private
33322     onKeyDown : function(e){
33323         if(this.isVisible()){
33324             this.fireEvent("keydown", this, e);
33325         }
33326     },
33327
33328     /**
33329      * Resizes the dialog.
33330      * @param {Number} width
33331      * @param {Number} height
33332      * @return {Roo.BasicDialog} this
33333      */
33334     resizeTo : function(width, height){
33335         this.el.setSize(width, height);
33336         this.size = {width: width, height: height};
33337         this.syncBodyHeight();
33338         if(this.fixedcenter){
33339             this.center();
33340         }
33341         if(this.isVisible()){
33342             this.constrainXY();
33343             this.adjustAssets();
33344         }
33345         this.fireEvent("resize", this, width, height);
33346         return this;
33347     },
33348
33349
33350     /**
33351      * Resizes the dialog to fit the specified content size.
33352      * @param {Number} width
33353      * @param {Number} height
33354      * @return {Roo.BasicDialog} this
33355      */
33356     setContentSize : function(w, h){
33357         h += this.getHeaderFooterHeight() + this.body.getMargins("tb");
33358         w += this.body.getMargins("lr") + this.bwrap.getMargins("lr") + this.centerBg.getPadding("lr");
33359         //if(!this.el.isBorderBox()){
33360             h +=  this.body.getPadding("tb") + this.bwrap.getBorderWidth("tb") + this.body.getBorderWidth("tb") + this.el.getBorderWidth("tb");
33361             w += this.body.getPadding("lr") + this.bwrap.getBorderWidth("lr") + this.body.getBorderWidth("lr") + this.bwrap.getPadding("lr") + this.el.getBorderWidth("lr");
33362         //}
33363         if(this.tabs){
33364             h += this.tabs.stripWrap.getHeight() + this.tabs.bodyEl.getMargins("tb") + this.tabs.bodyEl.getPadding("tb");
33365             w += this.tabs.bodyEl.getMargins("lr") + this.tabs.bodyEl.getPadding("lr");
33366         }
33367         this.resizeTo(w, h);
33368         return this;
33369     },
33370
33371     /**
33372      * Adds a key listener for when this dialog is displayed.  This allows you to hook in a function that will be
33373      * executed in response to a particular key being pressed while the dialog is active.
33374      * @param {Number/Array/Object} key Either the numeric key code, array of key codes or an object with the following options:
33375      *                                  {key: (number or array), shift: (true/false), ctrl: (true/false), alt: (true/false)}
33376      * @param {Function} fn The function to call
33377      * @param {Object} scope (optional) The scope of the function
33378      * @return {Roo.BasicDialog} this
33379      */
33380     addKeyListener : function(key, fn, scope){
33381         var keyCode, shift, ctrl, alt;
33382         if(typeof key == "object" && !(key instanceof Array)){
33383             keyCode = key["key"];
33384             shift = key["shift"];
33385             ctrl = key["ctrl"];
33386             alt = key["alt"];
33387         }else{
33388             keyCode = key;
33389         }
33390         var handler = function(dlg, e){
33391             if((!shift || e.shiftKey) && (!ctrl || e.ctrlKey) &&  (!alt || e.altKey)){
33392                 var k = e.getKey();
33393                 if(keyCode instanceof Array){
33394                     for(var i = 0, len = keyCode.length; i < len; i++){
33395                         if(keyCode[i] == k){
33396                           fn.call(scope || window, dlg, k, e);
33397                           return;
33398                         }
33399                     }
33400                 }else{
33401                     if(k == keyCode){
33402                         fn.call(scope || window, dlg, k, e);
33403                     }
33404                 }
33405             }
33406         };
33407         this.on("keydown", handler);
33408         return this;
33409     },
33410
33411     /**
33412      * Returns the TabPanel component (creates it if it doesn't exist).
33413      * Note: If you wish to simply check for the existence of tabs without creating them,
33414      * check for a null 'tabs' property.
33415      * @return {Roo.TabPanel} The tabs component
33416      */
33417     getTabs : function(){
33418         if(!this.tabs){
33419             this.el.addClass("x-dlg-auto-tabs");
33420             this.body.addClass(this.tabPosition == "bottom" ? "x-tabs-bottom" : "x-tabs-top");
33421             this.tabs = new Roo.TabPanel(this.body.dom, this.tabPosition == "bottom");
33422         }
33423         return this.tabs;
33424     },
33425
33426     /**
33427      * Adds a button to the footer section of the dialog.
33428      * @param {String/Object} config A string becomes the button text, an object can either be a Button config
33429      * object or a valid Roo.DomHelper element config
33430      * @param {Function} handler The function called when the button is clicked
33431      * @param {Object} scope (optional) The scope of the handler function (accepts position as a property)
33432      * @return {Roo.Button} The new button
33433      */
33434     addButton : function(config, handler, scope){
33435         var dh = Roo.DomHelper;
33436         if(!this.footer){
33437             this.footer = dh.append(this.bwrap, {tag: "div", cls:"x-dlg-ft"}, true);
33438         }
33439         if(!this.btnContainer){
33440             var tb = this.footer.createChild({
33441
33442                 cls:"x-dlg-btns x-dlg-btns-"+this.buttonAlign,
33443                 html:'<table cellspacing="0"><tbody><tr></tr></tbody></table><div class="x-clear"></div>'
33444             }, null, true);
33445             this.btnContainer = tb.firstChild.firstChild.firstChild;
33446         }
33447         var bconfig = {
33448             handler: handler,
33449             scope: scope,
33450             minWidth: this.minButtonWidth,
33451             hideParent:true
33452         };
33453         if(typeof config == "string"){
33454             bconfig.text = config;
33455         }else{
33456             if(config.tag){
33457                 bconfig.dhconfig = config;
33458             }else{
33459                 Roo.apply(bconfig, config);
33460             }
33461         }
33462         var fc = false;
33463         if ((typeof(bconfig.position) != 'undefined') && bconfig.position < this.btnContainer.childNodes.length-1) {
33464             bconfig.position = Math.max(0, bconfig.position);
33465             fc = this.btnContainer.childNodes[bconfig.position];
33466         }
33467          
33468         var btn = new Roo.Button(
33469             fc ? 
33470                 this.btnContainer.insertBefore(document.createElement("td"),fc)
33471                 : this.btnContainer.appendChild(document.createElement("td")),
33472             //Roo.get(this.btnContainer).createChild( { tag: 'td'},  fc ),
33473             bconfig
33474         );
33475         this.syncBodyHeight();
33476         if(!this.buttons){
33477             /**
33478              * Array of all the buttons that have been added to this dialog via addButton
33479              * @type Array
33480              */
33481             this.buttons = [];
33482         }
33483         this.buttons.push(btn);
33484         return btn;
33485     },
33486
33487     /**
33488      * Sets the default button to be focused when the dialog is displayed.
33489      * @param {Roo.BasicDialog.Button} btn The button object returned by {@link #addButton}
33490      * @return {Roo.BasicDialog} this
33491      */
33492     setDefaultButton : function(btn){
33493         this.defaultButton = btn;
33494         return this;
33495     },
33496
33497     // private
33498     getHeaderFooterHeight : function(safe){
33499         var height = 0;
33500         if(this.header){
33501            height += this.header.getHeight();
33502         }
33503         if(this.footer){
33504            var fm = this.footer.getMargins();
33505             height += (this.footer.getHeight()+fm.top+fm.bottom);
33506         }
33507         height += this.bwrap.getPadding("tb")+this.bwrap.getBorderWidth("tb");
33508         height += this.centerBg.getPadding("tb");
33509         return height;
33510     },
33511
33512     // private
33513     syncBodyHeight : function()
33514     {
33515         var bd = this.body, // the text
33516             cb = this.centerBg, // wrapper around bottom.. but does not seem to be used..
33517             bw = this.bwrap;
33518         var height = this.size.height - this.getHeaderFooterHeight(false);
33519         bd.setHeight(height-bd.getMargins("tb"));
33520         var hh = this.header.getHeight();
33521         var h = this.size.height-hh;
33522         cb.setHeight(h);
33523         
33524         bw.setLeftTop(cb.getPadding("l"), hh+cb.getPadding("t"));
33525         bw.setHeight(h-cb.getPadding("tb"));
33526         
33527         bw.setWidth(this.el.getWidth(true)-cb.getPadding("lr"));
33528         bd.setWidth(bw.getWidth(true));
33529         if(this.tabs){
33530             this.tabs.syncHeight();
33531             if(Roo.isIE){
33532                 this.tabs.el.repaint();
33533             }
33534         }
33535     },
33536
33537     /**
33538      * Restores the previous state of the dialog if Roo.state is configured.
33539      * @return {Roo.BasicDialog} this
33540      */
33541     restoreState : function(){
33542         var box = Roo.state.Manager.get(this.stateId || (this.el.id + "-state"));
33543         if(box && box.width){
33544             this.xy = [box.x, box.y];
33545             this.resizeTo(box.width, box.height);
33546         }
33547         return this;
33548     },
33549
33550     // private
33551     beforeShow : function(){
33552         this.expand();
33553         if(this.fixedcenter){
33554             this.xy = this.el.getCenterXY(true);
33555         }
33556         if(this.modal){
33557             Roo.get(document.body).addClass("x-body-masked");
33558             this.mask.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
33559             this.mask.show();
33560         }
33561         this.constrainXY();
33562     },
33563
33564     // private
33565     animShow : function(){
33566         var b = Roo.get(this.animateTarget).getBox();
33567         this.proxy.setSize(b.width, b.height);
33568         this.proxy.setLocation(b.x, b.y);
33569         this.proxy.show();
33570         this.proxy.setBounds(this.xy[0], this.xy[1], this.size.width, this.size.height,
33571                     true, .35, this.showEl.createDelegate(this));
33572     },
33573
33574     /**
33575      * Shows the dialog.
33576      * @param {String/HTMLElement/Roo.Element} animateTarget (optional) Reset the animation target
33577      * @return {Roo.BasicDialog} this
33578      */
33579     show : function(animateTarget){
33580         if (this.fireEvent("beforeshow", this) === false){
33581             return;
33582         }
33583         if(this.syncHeightBeforeShow){
33584             this.syncBodyHeight();
33585         }else if(this.firstShow){
33586             this.firstShow = false;
33587             this.syncBodyHeight(); // sync the height on the first show instead of in the constructor
33588         }
33589         this.animateTarget = animateTarget || this.animateTarget;
33590         if(!this.el.isVisible()){
33591             this.beforeShow();
33592             if(this.animateTarget && Roo.get(this.animateTarget)){
33593                 this.animShow();
33594             }else{
33595                 this.showEl();
33596             }
33597         }
33598         return this;
33599     },
33600
33601     // private
33602     showEl : function(){
33603         this.proxy.hide();
33604         this.el.setXY(this.xy);
33605         this.el.show();
33606         this.adjustAssets(true);
33607         this.toFront();
33608         this.focus();
33609         // IE peekaboo bug - fix found by Dave Fenwick
33610         if(Roo.isIE){
33611             this.el.repaint();
33612         }
33613         this.fireEvent("show", this);
33614     },
33615
33616     /**
33617      * Focuses the dialog.  If a defaultButton is set, it will receive focus, otherwise the
33618      * dialog itself will receive focus.
33619      */
33620     focus : function(){
33621         if(this.defaultButton){
33622             this.defaultButton.focus();
33623         }else{
33624             this.focusEl.focus();
33625         }
33626     },
33627
33628     // private
33629     constrainXY : function(){
33630         if(this.constraintoviewport !== false){
33631             if(!this.viewSize){
33632                 if(this.container){
33633                     var s = this.container.getSize();
33634                     this.viewSize = [s.width, s.height];
33635                 }else{
33636                     this.viewSize = [Roo.lib.Dom.getViewWidth(),Roo.lib.Dom.getViewHeight()];
33637                 }
33638             }
33639             var s = Roo.get(this.container||document).getScroll();
33640
33641             var x = this.xy[0], y = this.xy[1];
33642             var w = this.size.width, h = this.size.height;
33643             var vw = this.viewSize[0], vh = this.viewSize[1];
33644             // only move it if it needs it
33645             var moved = false;
33646             // first validate right/bottom
33647             if(x + w > vw+s.left){
33648                 x = vw - w;
33649                 moved = true;
33650             }
33651             if(y + h > vh+s.top){
33652                 y = vh - h;
33653                 moved = true;
33654             }
33655             // then make sure top/left isn't negative
33656             if(x < s.left){
33657                 x = s.left;
33658                 moved = true;
33659             }
33660             if(y < s.top){
33661                 y = s.top;
33662                 moved = true;
33663             }
33664             if(moved){
33665                 // cache xy
33666                 this.xy = [x, y];
33667                 if(this.isVisible()){
33668                     this.el.setLocation(x, y);
33669                     this.adjustAssets();
33670                 }
33671             }
33672         }
33673     },
33674
33675     // private
33676     onDrag : function(){
33677         if(!this.proxyDrag){
33678             this.xy = this.el.getXY();
33679             this.adjustAssets();
33680         }
33681     },
33682
33683     // private
33684     adjustAssets : function(doShow){
33685         var x = this.xy[0], y = this.xy[1];
33686         var w = this.size.width, h = this.size.height;
33687         if(doShow === true){
33688             if(this.shadow){
33689                 this.shadow.show(this.el);
33690             }
33691             if(this.shim){
33692                 this.shim.show();
33693             }
33694         }
33695         if(this.shadow && this.shadow.isVisible()){
33696             this.shadow.show(this.el);
33697         }
33698         if(this.shim && this.shim.isVisible()){
33699             this.shim.setBounds(x, y, w, h);
33700         }
33701     },
33702
33703     // private
33704     adjustViewport : function(w, h){
33705         if(!w || !h){
33706             w = Roo.lib.Dom.getViewWidth();
33707             h = Roo.lib.Dom.getViewHeight();
33708         }
33709         // cache the size
33710         this.viewSize = [w, h];
33711         if(this.modal && this.mask.isVisible()){
33712             this.mask.setSize(w, h); // first make sure the mask isn't causing overflow
33713             this.mask.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
33714         }
33715         if(this.isVisible()){
33716             this.constrainXY();
33717         }
33718     },
33719
33720     /**
33721      * Destroys this dialog and all its supporting elements (including any tabs, shim,
33722      * shadow, proxy, mask, etc.)  Also removes all event listeners.
33723      * @param {Boolean} removeEl (optional) true to remove the element from the DOM
33724      */
33725     destroy : function(removeEl){
33726         if(this.isVisible()){
33727             this.animateTarget = null;
33728             this.hide();
33729         }
33730         Roo.EventManager.removeResizeListener(this.adjustViewport, this);
33731         if(this.tabs){
33732             this.tabs.destroy(removeEl);
33733         }
33734         Roo.destroy(
33735              this.shim,
33736              this.proxy,
33737              this.resizer,
33738              this.close,
33739              this.mask
33740         );
33741         if(this.dd){
33742             this.dd.unreg();
33743         }
33744         if(this.buttons){
33745            for(var i = 0, len = this.buttons.length; i < len; i++){
33746                this.buttons[i].destroy();
33747            }
33748         }
33749         this.el.removeAllListeners();
33750         if(removeEl === true){
33751             this.el.update("");
33752             this.el.remove();
33753         }
33754         Roo.DialogManager.unregister(this);
33755     },
33756
33757     // private
33758     startMove : function(){
33759         if(this.proxyDrag){
33760             this.proxy.show();
33761         }
33762         if(this.constraintoviewport !== false){
33763             this.dd.constrainTo(document.body, {right: this.shadowOffset, bottom: this.shadowOffset});
33764         }
33765     },
33766
33767     // private
33768     endMove : function(){
33769         if(!this.proxyDrag){
33770             Roo.dd.DD.prototype.endDrag.apply(this.dd, arguments);
33771         }else{
33772             Roo.dd.DDProxy.prototype.endDrag.apply(this.dd, arguments);
33773             this.proxy.hide();
33774         }
33775         this.refreshSize();
33776         this.adjustAssets();
33777         this.focus();
33778         this.fireEvent("move", this, this.xy[0], this.xy[1]);
33779     },
33780
33781     /**
33782      * Brings this dialog to the front of any other visible dialogs
33783      * @return {Roo.BasicDialog} this
33784      */
33785     toFront : function(){
33786         Roo.DialogManager.bringToFront(this);
33787         return this;
33788     },
33789
33790     /**
33791      * Sends this dialog to the back (under) of any other visible dialogs
33792      * @return {Roo.BasicDialog} this
33793      */
33794     toBack : function(){
33795         Roo.DialogManager.sendToBack(this);
33796         return this;
33797     },
33798
33799     /**
33800      * Centers this dialog in the viewport
33801      * @return {Roo.BasicDialog} this
33802      */
33803     center : function(){
33804         var xy = this.el.getCenterXY(true);
33805         this.moveTo(xy[0], xy[1]);
33806         return this;
33807     },
33808
33809     /**
33810      * Moves the dialog's top-left corner to the specified point
33811      * @param {Number} x
33812      * @param {Number} y
33813      * @return {Roo.BasicDialog} this
33814      */
33815     moveTo : function(x, y){
33816         this.xy = [x,y];
33817         if(this.isVisible()){
33818             this.el.setXY(this.xy);
33819             this.adjustAssets();
33820         }
33821         return this;
33822     },
33823
33824     /**
33825      * Aligns the dialog to the specified element
33826      * @param {String/HTMLElement/Roo.Element} element The element to align to.
33827      * @param {String} position The position to align to (see {@link Roo.Element#alignTo} for more details).
33828      * @param {Array} offsets (optional) Offset the positioning by [x, y]
33829      * @return {Roo.BasicDialog} this
33830      */
33831     alignTo : function(element, position, offsets){
33832         this.xy = this.el.getAlignToXY(element, position, offsets);
33833         if(this.isVisible()){
33834             this.el.setXY(this.xy);
33835             this.adjustAssets();
33836         }
33837         return this;
33838     },
33839
33840     /**
33841      * Anchors an element to another element and realigns it when the window is resized.
33842      * @param {String/HTMLElement/Roo.Element} element The element to align to.
33843      * @param {String} position The position to align to (see {@link Roo.Element#alignTo} for more details)
33844      * @param {Array} offsets (optional) Offset the positioning by [x, y]
33845      * @param {Boolean/Number} monitorScroll (optional) true to monitor body scroll and reposition. If this parameter
33846      * is a number, it is used as the buffer delay (defaults to 50ms).
33847      * @return {Roo.BasicDialog} this
33848      */
33849     anchorTo : function(el, alignment, offsets, monitorScroll){
33850         var action = function(){
33851             this.alignTo(el, alignment, offsets);
33852         };
33853         Roo.EventManager.onWindowResize(action, this);
33854         var tm = typeof monitorScroll;
33855         if(tm != 'undefined'){
33856             Roo.EventManager.on(window, 'scroll', action, this,
33857                 {buffer: tm == 'number' ? monitorScroll : 50});
33858         }
33859         action.call(this);
33860         return this;
33861     },
33862
33863     /**
33864      * Returns true if the dialog is visible
33865      * @return {Boolean}
33866      */
33867     isVisible : function(){
33868         return this.el.isVisible();
33869     },
33870
33871     // private
33872     animHide : function(callback){
33873         var b = Roo.get(this.animateTarget).getBox();
33874         this.proxy.show();
33875         this.proxy.setBounds(this.xy[0], this.xy[1], this.size.width, this.size.height);
33876         this.el.hide();
33877         this.proxy.setBounds(b.x, b.y, b.width, b.height, true, .35,
33878                     this.hideEl.createDelegate(this, [callback]));
33879     },
33880
33881     /**
33882      * Hides the dialog.
33883      * @param {Function} callback (optional) Function to call when the dialog is hidden
33884      * @return {Roo.BasicDialog} this
33885      */
33886     hide : function(callback){
33887         if (this.fireEvent("beforehide", this) === false){
33888             return;
33889         }
33890         if(this.shadow){
33891             this.shadow.hide();
33892         }
33893         if(this.shim) {
33894           this.shim.hide();
33895         }
33896         // sometimes animateTarget seems to get set.. causing problems...
33897         // this just double checks..
33898         if(this.animateTarget && Roo.get(this.animateTarget)) {
33899            this.animHide(callback);
33900         }else{
33901             this.el.hide();
33902             this.hideEl(callback);
33903         }
33904         return this;
33905     },
33906
33907     // private
33908     hideEl : function(callback){
33909         this.proxy.hide();
33910         if(this.modal){
33911             this.mask.hide();
33912             Roo.get(document.body).removeClass("x-body-masked");
33913         }
33914         this.fireEvent("hide", this);
33915         if(typeof callback == "function"){
33916             callback();
33917         }
33918     },
33919
33920     // private
33921     hideAction : function(){
33922         this.setLeft("-10000px");
33923         this.setTop("-10000px");
33924         this.setStyle("visibility", "hidden");
33925     },
33926
33927     // private
33928     refreshSize : function(){
33929         this.size = this.el.getSize();
33930         this.xy = this.el.getXY();
33931         Roo.state.Manager.set(this.stateId || this.el.id + "-state", this.el.getBox());
33932     },
33933
33934     // private
33935     // z-index is managed by the DialogManager and may be overwritten at any time
33936     setZIndex : function(index){
33937         if(this.modal){
33938             this.mask.setStyle("z-index", index);
33939         }
33940         if(this.shim){
33941             this.shim.setStyle("z-index", ++index);
33942         }
33943         if(this.shadow){
33944             this.shadow.setZIndex(++index);
33945         }
33946         this.el.setStyle("z-index", ++index);
33947         if(this.proxy){
33948             this.proxy.setStyle("z-index", ++index);
33949         }
33950         if(this.resizer){
33951             this.resizer.proxy.setStyle("z-index", ++index);
33952         }
33953
33954         this.lastZIndex = index;
33955     },
33956
33957     /**
33958      * Returns the element for this dialog
33959      * @return {Roo.Element} The underlying dialog Element
33960      */
33961     getEl : function(){
33962         return this.el;
33963     }
33964 });
33965
33966 /**
33967  * @class Roo.DialogManager
33968  * Provides global access to BasicDialogs that have been created and
33969  * support for z-indexing (layering) multiple open dialogs.
33970  */
33971 Roo.DialogManager = function(){
33972     var list = {};
33973     var accessList = [];
33974     var front = null;
33975
33976     // private
33977     var sortDialogs = function(d1, d2){
33978         return (!d1._lastAccess || d1._lastAccess < d2._lastAccess) ? -1 : 1;
33979     };
33980
33981     // private
33982     var orderDialogs = function(){
33983         accessList.sort(sortDialogs);
33984         var seed = Roo.DialogManager.zseed;
33985         for(var i = 0, len = accessList.length; i < len; i++){
33986             var dlg = accessList[i];
33987             if(dlg){
33988                 dlg.setZIndex(seed + (i*10));
33989             }
33990         }
33991     };
33992
33993     return {
33994         /**
33995          * The starting z-index for BasicDialogs (defaults to 9000)
33996          * @type Number The z-index value
33997          */
33998         zseed : 9000,
33999
34000         // private
34001         register : function(dlg){
34002             list[dlg.id] = dlg;
34003             accessList.push(dlg);
34004         },
34005
34006         // private
34007         unregister : function(dlg){
34008             delete list[dlg.id];
34009             var i=0;
34010             var len=0;
34011             if(!accessList.indexOf){
34012                 for(  i = 0, len = accessList.length; i < len; i++){
34013                     if(accessList[i] == dlg){
34014                         accessList.splice(i, 1);
34015                         return;
34016                     }
34017                 }
34018             }else{
34019                  i = accessList.indexOf(dlg);
34020                 if(i != -1){
34021                     accessList.splice(i, 1);
34022                 }
34023             }
34024         },
34025
34026         /**
34027          * Gets a registered dialog by id
34028          * @param {String/Object} id The id of the dialog or a dialog
34029          * @return {Roo.BasicDialog} this
34030          */
34031         get : function(id){
34032             return typeof id == "object" ? id : list[id];
34033         },
34034
34035         /**
34036          * Brings the specified dialog to the front
34037          * @param {String/Object} dlg The id of the dialog or a dialog
34038          * @return {Roo.BasicDialog} this
34039          */
34040         bringToFront : function(dlg){
34041             dlg = this.get(dlg);
34042             if(dlg != front){
34043                 front = dlg;
34044                 dlg._lastAccess = new Date().getTime();
34045                 orderDialogs();
34046             }
34047             return dlg;
34048         },
34049
34050         /**
34051          * Sends the specified dialog to the back
34052          * @param {String/Object} dlg The id of the dialog or a dialog
34053          * @return {Roo.BasicDialog} this
34054          */
34055         sendToBack : function(dlg){
34056             dlg = this.get(dlg);
34057             dlg._lastAccess = -(new Date().getTime());
34058             orderDialogs();
34059             return dlg;
34060         },
34061
34062         /**
34063          * Hides all dialogs
34064          */
34065         hideAll : function(){
34066             for(var id in list){
34067                 if(list[id] && typeof list[id] != "function" && list[id].isVisible()){
34068                     list[id].hide();
34069                 }
34070             }
34071         }
34072     };
34073 }();
34074
34075 /**
34076  * @class Roo.LayoutDialog
34077  * @extends Roo.BasicDialog
34078  * @children Roo.ContentPanel
34079  * @parent builder none
34080  * Dialog which provides adjustments for working with a layout in a Dialog.
34081  * Add your necessary layout config options to the dialog's config.<br>
34082  * Example usage (including a nested layout):
34083  * <pre><code>
34084 if(!dialog){
34085     dialog = new Roo.LayoutDialog("download-dlg", {
34086         modal: true,
34087         width:600,
34088         height:450,
34089         shadow:true,
34090         minWidth:500,
34091         minHeight:350,
34092         autoTabs:true,
34093         proxyDrag:true,
34094         // layout config merges with the dialog config
34095         center:{
34096             tabPosition: "top",
34097             alwaysShowTabs: true
34098         }
34099     });
34100     dialog.addKeyListener(27, dialog.hide, dialog);
34101     dialog.setDefaultButton(dialog.addButton("Close", dialog.hide, dialog));
34102     dialog.addButton("Build It!", this.getDownload, this);
34103
34104     // we can even add nested layouts
34105     var innerLayout = new Roo.BorderLayout("dl-inner", {
34106         east: {
34107             initialSize: 200,
34108             autoScroll:true,
34109             split:true
34110         },
34111         center: {
34112             autoScroll:true
34113         }
34114     });
34115     innerLayout.beginUpdate();
34116     innerLayout.add("east", new Roo.ContentPanel("dl-details"));
34117     innerLayout.add("center", new Roo.ContentPanel("selection-panel"));
34118     innerLayout.endUpdate(true);
34119
34120     var layout = dialog.getLayout();
34121     layout.beginUpdate();
34122     layout.add("center", new Roo.ContentPanel("standard-panel",
34123                         {title: "Download the Source", fitToFrame:true}));
34124     layout.add("center", new Roo.NestedLayoutPanel(innerLayout,
34125                {title: "Build your own roo.js"}));
34126     layout.getRegion("center").showPanel(sp);
34127     layout.endUpdate();
34128 }
34129 </code></pre>
34130     * @constructor
34131     * @param {String/HTMLElement/Roo.Element} el The id of or container element, or config
34132     * @param {Object} config configuration options
34133   */
34134 Roo.LayoutDialog = function(el, cfg){
34135     
34136     var config=  cfg;
34137     if (typeof(cfg) == 'undefined') {
34138         config = Roo.apply({}, el);
34139         // not sure why we use documentElement here.. - it should always be body.
34140         // IE7 borks horribly if we use documentElement.
34141         // webkit also does not like documentElement - it creates a body element...
34142         el = Roo.get( document.body || document.documentElement ).createChild();
34143         //config.autoCreate = true;
34144     }
34145     
34146     
34147     config.autoTabs = false;
34148     Roo.LayoutDialog.superclass.constructor.call(this, el, config);
34149     this.body.setStyle({overflow:"hidden", position:"relative"});
34150     this.layout = new Roo.BorderLayout(this.body.dom, config);
34151     this.layout.monitorWindowResize = false;
34152     this.el.addClass("x-dlg-auto-layout");
34153     // fix case when center region overwrites center function
34154     this.center = Roo.BasicDialog.prototype.center;
34155     this.on("show", this.layout.layout, this.layout, true);
34156     if (config.items) {
34157         var xitems = config.items;
34158         delete config.items;
34159         Roo.each(xitems, this.addxtype, this);
34160     }
34161     
34162     
34163 };
34164 Roo.extend(Roo.LayoutDialog, Roo.BasicDialog, {
34165     
34166     
34167     /**
34168      * @cfg {Roo.LayoutRegion} east  
34169      */
34170     /**
34171      * @cfg {Roo.LayoutRegion} west
34172      */
34173     /**
34174      * @cfg {Roo.LayoutRegion} south
34175      */
34176     /**
34177      * @cfg {Roo.LayoutRegion} north
34178      */
34179     /**
34180      * @cfg {Roo.LayoutRegion} center
34181      */
34182     /**
34183      * @cfg {Roo.Button} buttons[]  Bottom buttons..
34184      */
34185     
34186     
34187     /**
34188      * Ends update of the layout <strike>and resets display to none</strike>. Use standard beginUpdate/endUpdate on the layout.
34189      * @deprecated
34190      */
34191     endUpdate : function(){
34192         this.layout.endUpdate();
34193     },
34194
34195     /**
34196      * Begins an update of the layout <strike>and sets display to block and visibility to hidden</strike>. Use standard beginUpdate/endUpdate on the layout.
34197      *  @deprecated
34198      */
34199     beginUpdate : function(){
34200         this.layout.beginUpdate();
34201     },
34202
34203     /**
34204      * Get the BorderLayout for this dialog
34205      * @return {Roo.BorderLayout}
34206      */
34207     getLayout : function(){
34208         return this.layout;
34209     },
34210
34211     showEl : function(){
34212         Roo.LayoutDialog.superclass.showEl.apply(this, arguments);
34213         if(Roo.isIE7){
34214             this.layout.layout();
34215         }
34216     },
34217
34218     // private
34219     // Use the syncHeightBeforeShow config option to control this automatically
34220     syncBodyHeight : function(){
34221         Roo.LayoutDialog.superclass.syncBodyHeight.call(this);
34222         if(this.layout){this.layout.layout();}
34223     },
34224     
34225       /**
34226      * Add an xtype element (actually adds to the layout.)
34227      * @return {Object} xdata xtype object data.
34228      */
34229     
34230     addxtype : function(c) {
34231         return this.layout.addxtype(c);
34232     }
34233 });/*
34234  * Based on:
34235  * Ext JS Library 1.1.1
34236  * Copyright(c) 2006-2007, Ext JS, LLC.
34237  *
34238  * Originally Released Under LGPL - original licence link has changed is not relivant.
34239  *
34240  * Fork - LGPL
34241  * <script type="text/javascript">
34242  */
34243  
34244 /**
34245  * @class Roo.MessageBox
34246  * Utility class for generating different styles of message boxes.  The alias Roo.Msg can also be used.
34247  * Example usage:
34248  *<pre><code>
34249 // Basic alert:
34250 Roo.Msg.alert('Status', 'Changes saved successfully.');
34251
34252 // Prompt for user data:
34253 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
34254     if (btn == 'ok'){
34255         // process text value...
34256     }
34257 });
34258
34259 // Show a dialog using config options:
34260 Roo.Msg.show({
34261    title:'Save Changes?',
34262    msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
34263    buttons: Roo.Msg.YESNOCANCEL,
34264    fn: processResult,
34265    animEl: 'elId'
34266 });
34267 </code></pre>
34268  * @static
34269  */
34270 Roo.MessageBox = function(){
34271     var dlg, opt, mask, waitTimer;
34272     var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
34273     var buttons, activeTextEl, bwidth;
34274
34275     // private
34276     var handleButton = function(button){
34277         dlg.hide();
34278         Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
34279     };
34280
34281     // private
34282     var handleHide = function(){
34283         if(opt && opt.cls){
34284             dlg.el.removeClass(opt.cls);
34285         }
34286         if(waitTimer){
34287             Roo.TaskMgr.stop(waitTimer);
34288             waitTimer = null;
34289         }
34290     };
34291
34292     // private
34293     var updateButtons = function(b){
34294         var width = 0;
34295         if(!b){
34296             buttons["ok"].hide();
34297             buttons["cancel"].hide();
34298             buttons["yes"].hide();
34299             buttons["no"].hide();
34300             dlg.footer.dom.style.display = 'none';
34301             return width;
34302         }
34303         dlg.footer.dom.style.display = '';
34304         for(var k in buttons){
34305             if(typeof buttons[k] != "function"){
34306                 if(b[k]){
34307                     buttons[k].show();
34308                     buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.MessageBox.buttonText[k]);
34309                     width += buttons[k].el.getWidth()+15;
34310                 }else{
34311                     buttons[k].hide();
34312                 }
34313             }
34314         }
34315         return width;
34316     };
34317
34318     // private
34319     var handleEsc = function(d, k, e){
34320         if(opt && opt.closable !== false){
34321             dlg.hide();
34322         }
34323         if(e){
34324             e.stopEvent();
34325         }
34326     };
34327
34328     return {
34329         /**
34330          * Returns a reference to the underlying {@link Roo.BasicDialog} element
34331          * @return {Roo.BasicDialog} The BasicDialog element
34332          */
34333         getDialog : function(){
34334            if(!dlg){
34335                 dlg = new Roo.BasicDialog("x-msg-box", {
34336                     autoCreate : true,
34337                     shadow: true,
34338                     draggable: true,
34339                     resizable:false,
34340                     constraintoviewport:false,
34341                     fixedcenter:true,
34342                     collapsible : false,
34343                     shim:true,
34344                     modal: true,
34345                     width:400, height:100,
34346                     buttonAlign:"center",
34347                     closeClick : function(){
34348                         if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
34349                             handleButton("no");
34350                         }else{
34351                             handleButton("cancel");
34352                         }
34353                     }
34354                 });
34355                 dlg.on("hide", handleHide);
34356                 mask = dlg.mask;
34357                 dlg.addKeyListener(27, handleEsc);
34358                 buttons = {};
34359                 var bt = this.buttonText;
34360                 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
34361                 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
34362                 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
34363                 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
34364                 bodyEl = dlg.body.createChild({
34365
34366                     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>'
34367                 });
34368                 msgEl = bodyEl.dom.firstChild;
34369                 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
34370                 textboxEl.enableDisplayMode();
34371                 textboxEl.addKeyListener([10,13], function(){
34372                     if(dlg.isVisible() && opt && opt.buttons){
34373                         if(opt.buttons.ok){
34374                             handleButton("ok");
34375                         }else if(opt.buttons.yes){
34376                             handleButton("yes");
34377                         }
34378                     }
34379                 });
34380                 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
34381                 textareaEl.enableDisplayMode();
34382                 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
34383                 progressEl.enableDisplayMode();
34384                 var pf = progressEl.dom.firstChild;
34385                 if (pf) {
34386                     pp = Roo.get(pf.firstChild);
34387                     pp.setHeight(pf.offsetHeight);
34388                 }
34389                 
34390             }
34391             return dlg;
34392         },
34393
34394         /**
34395          * Updates the message box body text
34396          * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
34397          * the XHTML-compliant non-breaking space character '&amp;#160;')
34398          * @return {Roo.MessageBox} This message box
34399          */
34400         updateText : function(text){
34401             if(!dlg.isVisible() && !opt.width){
34402                 dlg.resizeTo(this.maxWidth, 100); // resize first so content is never clipped from previous shows
34403             }
34404             msgEl.innerHTML = text || '&#160;';
34405       
34406             var cw =  Math.max(msgEl.offsetWidth, msgEl.parentNode.scrollWidth);
34407             //Roo.log("guesed size: " + JSON.stringify([cw,msgEl.offsetWidth, msgEl.parentNode.scrollWidth]));
34408             var w = Math.max(
34409                     Math.min(opt.width || cw , this.maxWidth), 
34410                     Math.max(opt.minWidth || this.minWidth, bwidth)
34411             );
34412             if(opt.prompt){
34413                 activeTextEl.setWidth(w);
34414             }
34415             if(dlg.isVisible()){
34416                 dlg.fixedcenter = false;
34417             }
34418             // to big, make it scroll. = But as usual stupid IE does not support
34419             // !important..
34420             
34421             if ( bodyEl.getHeight() > (Roo.lib.Dom.getViewHeight() - 100)) {
34422                 bodyEl.setHeight ( Roo.lib.Dom.getViewHeight() - 100 );
34423                 bodyEl.dom.style.overflowY = 'auto' + ( Roo.isIE ? '' : ' !important');
34424             } else {
34425                 bodyEl.dom.style.height = '';
34426                 bodyEl.dom.style.overflowY = '';
34427             }
34428             if (cw > w) {
34429                 bodyEl.dom.style.get = 'auto' + ( Roo.isIE ? '' : ' !important');
34430             } else {
34431                 bodyEl.dom.style.overflowX = '';
34432             }
34433             
34434             dlg.setContentSize(w, bodyEl.getHeight());
34435             if(dlg.isVisible()){
34436                 dlg.fixedcenter = true;
34437             }
34438             return this;
34439         },
34440
34441         /**
34442          * Updates a progress-style message box's text and progress bar.  Only relevant on message boxes
34443          * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
34444          * @param {Number} value Any number between 0 and 1 (e.g., .5)
34445          * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
34446          * @return {Roo.MessageBox} This message box
34447          */
34448         updateProgress : function(value, text){
34449             if(text){
34450                 this.updateText(text);
34451             }
34452             if (pp) { // weird bug on my firefox - for some reason this is not defined
34453                 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
34454             }
34455             return this;
34456         },        
34457
34458         /**
34459          * Returns true if the message box is currently displayed
34460          * @return {Boolean} True if the message box is visible, else false
34461          */
34462         isVisible : function(){
34463             return dlg && dlg.isVisible();  
34464         },
34465
34466         /**
34467          * Hides the message box if it is displayed
34468          */
34469         hide : function(){
34470             if(this.isVisible()){
34471                 dlg.hide();
34472             }  
34473         },
34474
34475         /**
34476          * Displays a new message box, or reinitializes an existing message box, based on the config options
34477          * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
34478          * The following config object properties are supported:
34479          * <pre>
34480 Property    Type             Description
34481 ----------  ---------------  ------------------------------------------------------------------------------------
34482 animEl            String/Element   An id or Element from which the message box should animate as it opens and
34483                                    closes (defaults to undefined)
34484 buttons           Object/Boolean   A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
34485                                    cancel:'Bar'}), or false to not show any buttons (defaults to false)
34486 closable          Boolean          False to hide the top-right close button (defaults to true).  Note that
34487                                    progress and wait dialogs will ignore this property and always hide the
34488                                    close button as they can only be closed programmatically.
34489 cls               String           A custom CSS class to apply to the message box element
34490 defaultTextHeight Number           The default height in pixels of the message box's multiline textarea if
34491                                    displayed (defaults to 75)
34492 fn                Function         A callback function to execute after closing the dialog.  The arguments to the
34493                                    function will be btn (the name of the button that was clicked, if applicable,
34494                                    e.g. "ok"), and text (the value of the active text field, if applicable).
34495                                    Progress and wait dialogs will ignore this option since they do not respond to
34496                                    user actions and can only be closed programmatically, so any required function
34497                                    should be called by the same code after it closes the dialog.
34498 icon              String           A CSS class that provides a background image to be used as an icon for
34499                                    the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
34500 maxWidth          Number           The maximum width in pixels of the message box (defaults to 600)
34501 minWidth          Number           The minimum width in pixels of the message box (defaults to 100)
34502 modal             Boolean          False to allow user interaction with the page while the message box is
34503                                    displayed (defaults to true)
34504 msg               String           A string that will replace the existing message box body text (defaults
34505                                    to the XHTML-compliant non-breaking space character '&#160;')
34506 multiline         Boolean          True to prompt the user to enter multi-line text (defaults to false)
34507 progress          Boolean          True to display a progress bar (defaults to false)
34508 progressText      String           The text to display inside the progress bar if progress = true (defaults to '')
34509 prompt            Boolean          True to prompt the user to enter single-line text (defaults to false)
34510 proxyDrag         Boolean          True to display a lightweight proxy while dragging (defaults to false)
34511 title             String           The title text
34512 value             String           The string value to set into the active textbox element if displayed
34513 wait              Boolean          True to display a progress bar (defaults to false)
34514 width             Number           The width of the dialog in pixels
34515 </pre>
34516          *
34517          * Example usage:
34518          * <pre><code>
34519 Roo.Msg.show({
34520    title: 'Address',
34521    msg: 'Please enter your address:',
34522    width: 300,
34523    buttons: Roo.MessageBox.OKCANCEL,
34524    multiline: true,
34525    fn: saveAddress,
34526    animEl: 'addAddressBtn'
34527 });
34528 </code></pre>
34529          * @param {Object} config Configuration options
34530          * @return {Roo.MessageBox} This message box
34531          */
34532         show : function(options)
34533         {
34534             
34535             // this causes nightmares if you show one dialog after another
34536             // especially on callbacks..
34537              
34538             if(this.isVisible()){
34539                 
34540                 this.hide();
34541                 Roo.log("[Roo.Messagebox] Show called while message displayed:" );
34542                 Roo.log("Old Dialog Message:" +  msgEl.innerHTML );
34543                 Roo.log("New Dialog Message:" +  options.msg )
34544                 //this.alert("ERROR", "Multiple dialogs where displayed at the same time");
34545                 //throw "Roo.MessageBox ERROR : Multiple dialogs where displayed at the same time";
34546                 
34547             }
34548             var d = this.getDialog();
34549             opt = options;
34550             d.setTitle(opt.title || "&#160;");
34551             d.close.setDisplayed(opt.closable !== false);
34552             activeTextEl = textboxEl;
34553             opt.prompt = opt.prompt || (opt.multiline ? true : false);
34554             if(opt.prompt){
34555                 if(opt.multiline){
34556                     textboxEl.hide();
34557                     textareaEl.show();
34558                     textareaEl.setHeight(typeof opt.multiline == "number" ?
34559                         opt.multiline : this.defaultTextHeight);
34560                     activeTextEl = textareaEl;
34561                 }else{
34562                     textboxEl.show();
34563                     textareaEl.hide();
34564                 }
34565             }else{
34566                 textboxEl.hide();
34567                 textareaEl.hide();
34568             }
34569             progressEl.setDisplayed(opt.progress === true);
34570             this.updateProgress(0);
34571             activeTextEl.dom.value = opt.value || "";
34572             if(opt.prompt){
34573                 dlg.setDefaultButton(activeTextEl);
34574             }else{
34575                 var bs = opt.buttons;
34576                 var db = null;
34577                 if(bs && bs.ok){
34578                     db = buttons["ok"];
34579                 }else if(bs && bs.yes){
34580                     db = buttons["yes"];
34581                 }
34582                 dlg.setDefaultButton(db);
34583             }
34584             bwidth = updateButtons(opt.buttons);
34585             this.updateText(opt.msg);
34586             if(opt.cls){
34587                 d.el.addClass(opt.cls);
34588             }
34589             d.proxyDrag = opt.proxyDrag === true;
34590             d.modal = opt.modal !== false;
34591             d.mask = opt.modal !== false ? mask : false;
34592             if(!d.isVisible()){
34593                 // force it to the end of the z-index stack so it gets a cursor in FF
34594                 document.body.appendChild(dlg.el.dom);
34595                 d.animateTarget = null;
34596                 d.show(options.animEl);
34597             }
34598             return this;
34599         },
34600
34601         /**
34602          * Displays a message box with a progress bar.  This message box has no buttons and is not closeable by
34603          * the user.  You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
34604          * and closing the message box when the process is complete.
34605          * @param {String} title The title bar text
34606          * @param {String} msg The message box body text
34607          * @return {Roo.MessageBox} This message box
34608          */
34609         progress : function(title, msg){
34610             this.show({
34611                 title : title,
34612                 msg : msg,
34613                 buttons: false,
34614                 progress:true,
34615                 closable:false,
34616                 minWidth: this.minProgressWidth,
34617                 modal : true
34618             });
34619             return this;
34620         },
34621
34622         /**
34623          * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
34624          * If a callback function is passed it will be called after the user clicks the button, and the
34625          * id of the button that was clicked will be passed as the only parameter to the callback
34626          * (could also be the top-right close button).
34627          * @param {String} title The title bar text
34628          * @param {String} msg The message box body text
34629          * @param {Function} fn (optional) The callback function invoked after the message box is closed
34630          * @param {Object} scope (optional) The scope of the callback function
34631          * @return {Roo.MessageBox} This message box
34632          */
34633         alert : function(title, msg, fn, scope){
34634             this.show({
34635                 title : title,
34636                 msg : msg,
34637                 buttons: this.OK,
34638                 fn: fn,
34639                 scope : scope,
34640                 modal : true
34641             });
34642             return this;
34643         },
34644
34645         /**
34646          * Displays a message box with an infinitely auto-updating progress bar.  This can be used to block user
34647          * interaction while waiting for a long-running process to complete that does not have defined intervals.
34648          * You are responsible for closing the message box when the process is complete.
34649          * @param {String} msg The message box body text
34650          * @param {String} title (optional) The title bar text
34651          * @return {Roo.MessageBox} This message box
34652          */
34653         wait : function(msg, title){
34654             this.show({
34655                 title : title,
34656                 msg : msg,
34657                 buttons: false,
34658                 closable:false,
34659                 progress:true,
34660                 modal:true,
34661                 width:300,
34662                 wait:true
34663             });
34664             waitTimer = Roo.TaskMgr.start({
34665                 run: function(i){
34666                     Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
34667                 },
34668                 interval: 1000
34669             });
34670             return this;
34671         },
34672
34673         /**
34674          * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
34675          * If a callback function is passed it will be called after the user clicks either button, and the id of the
34676          * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
34677          * @param {String} title The title bar text
34678          * @param {String} msg The message box body text
34679          * @param {Function} fn (optional) The callback function invoked after the message box is closed
34680          * @param {Object} scope (optional) The scope of the callback function
34681          * @return {Roo.MessageBox} This message box
34682          */
34683         confirm : function(title, msg, fn, scope){
34684             this.show({
34685                 title : title,
34686                 msg : msg,
34687                 buttons: this.YESNO,
34688                 fn: fn,
34689                 scope : scope,
34690                 modal : true
34691             });
34692             return this;
34693         },
34694
34695         /**
34696          * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
34697          * JavaScript's Window.prompt).  The prompt can be a single-line or multi-line textbox.  If a callback function
34698          * is passed it will be called after the user clicks either button, and the id of the button that was clicked
34699          * (could also be the top-right close button) and the text that was entered will be passed as the two
34700          * parameters to the callback.
34701          * @param {String} title The title bar text
34702          * @param {String} msg The message box body text
34703          * @param {Function} fn (optional) The callback function invoked after the message box is closed
34704          * @param {Object} scope (optional) The scope of the callback function
34705          * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
34706          * property, or the height in pixels to create the textbox (defaults to false / single-line)
34707          * @return {Roo.MessageBox} This message box
34708          */
34709         prompt : function(title, msg, fn, scope, multiline){
34710             this.show({
34711                 title : title,
34712                 msg : msg,
34713                 buttons: this.OKCANCEL,
34714                 fn: fn,
34715                 minWidth:250,
34716                 scope : scope,
34717                 prompt:true,
34718                 multiline: multiline,
34719                 modal : true
34720             });
34721             return this;
34722         },
34723
34724         /**
34725          * Button config that displays a single OK button
34726          * @type Object
34727          */
34728         OK : {ok:true},
34729         /**
34730          * Button config that displays Yes and No buttons
34731          * @type Object
34732          */
34733         YESNO : {yes:true, no:true},
34734         /**
34735          * Button config that displays OK and Cancel buttons
34736          * @type Object
34737          */
34738         OKCANCEL : {ok:true, cancel:true},
34739         /**
34740          * Button config that displays Yes, No and Cancel buttons
34741          * @type Object
34742          */
34743         YESNOCANCEL : {yes:true, no:true, cancel:true},
34744
34745         /**
34746          * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
34747          * @type Number
34748          */
34749         defaultTextHeight : 75,
34750         /**
34751          * The maximum width in pixels of the message box (defaults to 600)
34752          * @type Number
34753          */
34754         maxWidth : 600,
34755         /**
34756          * The minimum width in pixels of the message box (defaults to 100)
34757          * @type Number
34758          */
34759         minWidth : 100,
34760         /**
34761          * The minimum width in pixels of the message box if it is a progress-style dialog.  This is useful
34762          * for setting a different minimum width than text-only dialogs may need (defaults to 250)
34763          * @type Number
34764          */
34765         minProgressWidth : 250,
34766         /**
34767          * An object containing the default button text strings that can be overriden for localized language support.
34768          * Supported properties are: ok, cancel, yes and no.
34769          * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
34770          * @type Object
34771          */
34772         buttonText : {
34773             ok : "OK",
34774             cancel : "Cancel",
34775             yes : "Yes",
34776             no : "No"
34777         }
34778     };
34779 }();
34780
34781 /**
34782  * Shorthand for {@link Roo.MessageBox}
34783  */
34784 Roo.Msg = Roo.MessageBox;/*
34785  * Based on:
34786  * Ext JS Library 1.1.1
34787  * Copyright(c) 2006-2007, Ext JS, LLC.
34788  *
34789  * Originally Released Under LGPL - original licence link has changed is not relivant.
34790  *
34791  * Fork - LGPL
34792  * <script type="text/javascript">
34793  */
34794 /**
34795  * @class Roo.QuickTips
34796  * Provides attractive and customizable tooltips for any element.
34797  * @static
34798  */
34799 Roo.QuickTips = function(){
34800     var el, tipBody, tipBodyText, tipTitle, tm, cfg, close, tagEls = {}, esc, removeCls = null, bdLeft, bdRight;
34801     var ce, bd, xy, dd;
34802     var visible = false, disabled = true, inited = false;
34803     var showProc = 1, hideProc = 1, dismissProc = 1, locks = [];
34804     
34805     var onOver = function(e){
34806         if(disabled){
34807             return;
34808         }
34809         var t = e.getTarget();
34810         if(!t || t.nodeType !== 1 || t == document || t == document.body){
34811             return;
34812         }
34813         if(ce && t == ce.el){
34814             clearTimeout(hideProc);
34815             return;
34816         }
34817         if(t && tagEls[t.id]){
34818             tagEls[t.id].el = t;
34819             showProc = show.defer(tm.showDelay, tm, [tagEls[t.id]]);
34820             return;
34821         }
34822         var ttp, et = Roo.fly(t);
34823         var ns = cfg.namespace;
34824         if(tm.interceptTitles && t.title){
34825             ttp = t.title;
34826             t.qtip = ttp;
34827             t.removeAttribute("title");
34828             e.preventDefault();
34829         }else{
34830             ttp = t.qtip || et.getAttributeNS(ns, cfg.attribute) || et.getAttributeNS(cfg.alt_namespace, cfg.attribute) ;
34831         }
34832         if(ttp){
34833             showProc = show.defer(tm.showDelay, tm, [{
34834                 el: t, 
34835                 text: ttp.replace(/\\n/g,'<br/>'),
34836                 width: et.getAttributeNS(ns, cfg.width),
34837                 autoHide: et.getAttributeNS(ns, cfg.hide) != "user",
34838                 title: et.getAttributeNS(ns, cfg.title),
34839                     cls: et.getAttributeNS(ns, cfg.cls)
34840             }]);
34841         }
34842     };
34843     
34844     var onOut = function(e){
34845         clearTimeout(showProc);
34846         var t = e.getTarget();
34847         if(t && ce && ce.el == t && (tm.autoHide && ce.autoHide !== false)){
34848             hideProc = setTimeout(hide, tm.hideDelay);
34849         }
34850     };
34851     
34852     var onMove = function(e){
34853         if(disabled){
34854             return;
34855         }
34856         xy = e.getXY();
34857         xy[1] += 18;
34858         if(tm.trackMouse && ce){
34859             el.setXY(xy);
34860         }
34861     };
34862     
34863     var onDown = function(e){
34864         clearTimeout(showProc);
34865         clearTimeout(hideProc);
34866         if(!e.within(el)){
34867             if(tm.hideOnClick){
34868                 hide();
34869                 tm.disable();
34870                 tm.enable.defer(100, tm);
34871             }
34872         }
34873     };
34874     
34875     var getPad = function(){
34876         return 2;//bdLeft.getPadding('l')+bdRight.getPadding('r');
34877     };
34878
34879     var show = function(o){
34880         if(disabled){
34881             return;
34882         }
34883         clearTimeout(dismissProc);
34884         ce = o;
34885         if(removeCls){ // in case manually hidden
34886             el.removeClass(removeCls);
34887             removeCls = null;
34888         }
34889         if(ce.cls){
34890             el.addClass(ce.cls);
34891             removeCls = ce.cls;
34892         }
34893         if(ce.title){
34894             tipTitle.update(ce.title);
34895             tipTitle.show();
34896         }else{
34897             tipTitle.update('');
34898             tipTitle.hide();
34899         }
34900         el.dom.style.width  = tm.maxWidth+'px';
34901         //tipBody.dom.style.width = '';
34902         tipBodyText.update(o.text);
34903         var p = getPad(), w = ce.width;
34904         if(!w){
34905             var td = tipBodyText.dom;
34906             var aw = Math.max(td.offsetWidth, td.clientWidth, td.scrollWidth);
34907             if(aw > tm.maxWidth){
34908                 w = tm.maxWidth;
34909             }else if(aw < tm.minWidth){
34910                 w = tm.minWidth;
34911             }else{
34912                 w = aw;
34913             }
34914         }
34915         //tipBody.setWidth(w);
34916         el.setWidth(parseInt(w, 10) + p);
34917         if(ce.autoHide === false){
34918             close.setDisplayed(true);
34919             if(dd){
34920                 dd.unlock();
34921             }
34922         }else{
34923             close.setDisplayed(false);
34924             if(dd){
34925                 dd.lock();
34926             }
34927         }
34928         if(xy){
34929             el.avoidY = xy[1]-18;
34930             el.setXY(xy);
34931         }
34932         if(tm.animate){
34933             el.setOpacity(.1);
34934             el.setStyle("visibility", "visible");
34935             el.fadeIn({callback: afterShow});
34936         }else{
34937             afterShow();
34938         }
34939     };
34940     
34941     var afterShow = function(){
34942         if(ce){
34943             el.show();
34944             esc.enable();
34945             if(tm.autoDismiss && ce.autoHide !== false){
34946                 dismissProc = setTimeout(hide, tm.autoDismissDelay);
34947             }
34948         }
34949     };
34950     
34951     var hide = function(noanim){
34952         clearTimeout(dismissProc);
34953         clearTimeout(hideProc);
34954         ce = null;
34955         if(el.isVisible()){
34956             esc.disable();
34957             if(noanim !== true && tm.animate){
34958                 el.fadeOut({callback: afterHide});
34959             }else{
34960                 afterHide();
34961             } 
34962         }
34963     };
34964     
34965     var afterHide = function(){
34966         el.hide();
34967         if(removeCls){
34968             el.removeClass(removeCls);
34969             removeCls = null;
34970         }
34971     };
34972     
34973     return {
34974         /**
34975         * @cfg {Number} minWidth
34976         * The minimum width of the quick tip (defaults to 40)
34977         */
34978        minWidth : 40,
34979         /**
34980         * @cfg {Number} maxWidth
34981         * The maximum width of the quick tip (defaults to 300)
34982         */
34983        maxWidth : 300,
34984         /**
34985         * @cfg {Boolean} interceptTitles
34986         * True to automatically use the element's DOM title value if available (defaults to false)
34987         */
34988        interceptTitles : false,
34989         /**
34990         * @cfg {Boolean} trackMouse
34991         * True to have the quick tip follow the mouse as it moves over the target element (defaults to false)
34992         */
34993        trackMouse : false,
34994         /**
34995         * @cfg {Boolean} hideOnClick
34996         * True to hide the quick tip if the user clicks anywhere in the document (defaults to true)
34997         */
34998        hideOnClick : true,
34999         /**
35000         * @cfg {Number} showDelay
35001         * Delay in milliseconds before the quick tip displays after the mouse enters the target element (defaults to 500)
35002         */
35003        showDelay : 500,
35004         /**
35005         * @cfg {Number} hideDelay
35006         * Delay in milliseconds before the quick tip hides when autoHide = true (defaults to 200)
35007         */
35008        hideDelay : 200,
35009         /**
35010         * @cfg {Boolean} autoHide
35011         * True to automatically hide the quick tip after the mouse exits the target element (defaults to true).
35012         * Used in conjunction with hideDelay.
35013         */
35014        autoHide : true,
35015         /**
35016         * @cfg {Boolean}
35017         * True to automatically hide the quick tip after a set period of time, regardless of the user's actions
35018         * (defaults to true).  Used in conjunction with autoDismissDelay.
35019         */
35020        autoDismiss : true,
35021         /**
35022         * @cfg {Number}
35023         * Delay in milliseconds before the quick tip hides when autoDismiss = true (defaults to 5000)
35024         */
35025        autoDismissDelay : 5000,
35026        /**
35027         * @cfg {Boolean} animate
35028         * True to turn on fade animation. Defaults to false (ClearType/scrollbar flicker issues in IE7).
35029         */
35030        animate : false,
35031
35032        /**
35033         * @cfg {String} title
35034         * Title text to display (defaults to '').  This can be any valid HTML markup.
35035         */
35036         title: '',
35037        /**
35038         * @cfg {String} text
35039         * Body text to display (defaults to '').  This can be any valid HTML markup.
35040         */
35041         text : '',
35042        /**
35043         * @cfg {String} cls
35044         * A CSS class to apply to the base quick tip element (defaults to '').
35045         */
35046         cls : '',
35047        /**
35048         * @cfg {Number} width
35049         * Width in pixels of the quick tip (defaults to auto).  Width will be ignored if it exceeds the bounds of
35050         * minWidth or maxWidth.
35051         */
35052         width : null,
35053
35054     /**
35055      * Initialize and enable QuickTips for first use.  This should be called once before the first attempt to access
35056      * or display QuickTips in a page.
35057      */
35058        init : function(){
35059           tm = Roo.QuickTips;
35060           cfg = tm.tagConfig;
35061           if(!inited){
35062               if(!Roo.isReady){ // allow calling of init() before onReady
35063                   Roo.onReady(Roo.QuickTips.init, Roo.QuickTips);
35064                   return;
35065               }
35066               el = new Roo.Layer({cls:"x-tip", shadow:"drop", shim: true, constrain:true, shadowOffset:4});
35067               el.fxDefaults = {stopFx: true};
35068               // maximum custom styling
35069               //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>');
35070               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>');              
35071               tipTitle = el.child('h3');
35072               tipTitle.enableDisplayMode("block");
35073               tipBody = el.child('div.x-tip-bd');
35074               tipBodyText = el.child('div.x-tip-bd-inner');
35075               //bdLeft = el.child('div.x-tip-bd-left');
35076               //bdRight = el.child('div.x-tip-bd-right');
35077               close = el.child('div.x-tip-close');
35078               close.enableDisplayMode("block");
35079               close.on("click", hide);
35080               var d = Roo.get(document);
35081               d.on("mousedown", onDown);
35082               d.on("mouseover", onOver);
35083               d.on("mouseout", onOut);
35084               d.on("mousemove", onMove);
35085               esc = d.addKeyListener(27, hide);
35086               esc.disable();
35087               if(Roo.dd.DD){
35088                   dd = el.initDD("default", null, {
35089                       onDrag : function(){
35090                           el.sync();  
35091                       }
35092                   });
35093                   dd.setHandleElId(tipTitle.id);
35094                   dd.lock();
35095               }
35096               inited = true;
35097           }
35098           this.enable(); 
35099        },
35100
35101     /**
35102      * Configures a new quick tip instance and assigns it to a target element.  The following config options
35103      * are supported:
35104      * <pre>
35105 Property    Type                   Description
35106 ----------  ---------------------  ------------------------------------------------------------------------
35107 target      Element/String/Array   An Element, id or array of ids that this quick tip should be tied to
35108      * </ul>
35109      * @param {Object} config The config object
35110      */
35111        register : function(config){
35112            var cs = config instanceof Array ? config : arguments;
35113            for(var i = 0, len = cs.length; i < len; i++) {
35114                var c = cs[i];
35115                var target = c.target;
35116                if(target){
35117                    if(target instanceof Array){
35118                        for(var j = 0, jlen = target.length; j < jlen; j++){
35119                            tagEls[target[j]] = c;
35120                        }
35121                    }else{
35122                        tagEls[typeof target == 'string' ? target : Roo.id(target)] = c;
35123                    }
35124                }
35125            }
35126        },
35127
35128     /**
35129      * Removes this quick tip from its element and destroys it.
35130      * @param {String/HTMLElement/Element} el The element from which the quick tip is to be removed.
35131      */
35132        unregister : function(el){
35133            delete tagEls[Roo.id(el)];
35134        },
35135
35136     /**
35137      * Enable this quick tip.
35138      */
35139        enable : function(){
35140            if(inited && disabled){
35141                locks.pop();
35142                if(locks.length < 1){
35143                    disabled = false;
35144                }
35145            }
35146        },
35147
35148     /**
35149      * Disable this quick tip.
35150      */
35151        disable : function(){
35152           disabled = true;
35153           clearTimeout(showProc);
35154           clearTimeout(hideProc);
35155           clearTimeout(dismissProc);
35156           if(ce){
35157               hide(true);
35158           }
35159           locks.push(1);
35160        },
35161
35162     /**
35163      * Returns true if the quick tip is enabled, else false.
35164      */
35165        isEnabled : function(){
35166             return !disabled;
35167        },
35168
35169         // private
35170        tagConfig : {
35171            namespace : "roo", // was ext?? this may break..
35172            alt_namespace : "ext",
35173            attribute : "qtip",
35174            width : "width",
35175            target : "target",
35176            title : "qtitle",
35177            hide : "hide",
35178            cls : "qclass"
35179        }
35180    };
35181 }();
35182
35183 // backwards compat
35184 Roo.QuickTips.tips = Roo.QuickTips.register;/*
35185  * Based on:
35186  * Ext JS Library 1.1.1
35187  * Copyright(c) 2006-2007, Ext JS, LLC.
35188  *
35189  * Originally Released Under LGPL - original licence link has changed is not relivant.
35190  *
35191  * Fork - LGPL
35192  * <script type="text/javascript">
35193  */
35194  
35195
35196 /**
35197  * @class Roo.tree.TreePanel
35198  * @extends Roo.data.Tree
35199  * @cfg {Roo.tree.TreeNode} root The root node
35200  * @cfg {Boolean} rootVisible false to hide the root node (defaults to true)
35201  * @cfg {Boolean} lines false to disable tree lines (defaults to true)
35202  * @cfg {Boolean} enableDD true to enable drag and drop
35203  * @cfg {Boolean} enableDrag true to enable just drag
35204  * @cfg {Boolean} enableDrop true to enable just drop
35205  * @cfg {Object} dragConfig Custom config to pass to the {@link Roo.tree.TreeDragZone} instance
35206  * @cfg {Object} dropConfig Custom config to pass to the {@link Roo.tree.TreeDropZone} instance
35207  * @cfg {String} ddGroup The DD group this TreePanel belongs to
35208  * @cfg {String} ddAppendOnly True if the tree should only allow append drops (use for trees which are sorted)
35209  * @cfg {Boolean} ddScroll true to enable YUI body scrolling
35210  * @cfg {Boolean} containerScroll true to register this container with ScrollManager
35211  * @cfg {Boolean} hlDrop false to disable node highlight on drop (defaults to the value of Roo.enableFx)
35212  * @cfg {String} hlColor The color of the node highlight (defaults to C3DAF9)
35213  * @cfg {Boolean} animate true to enable animated expand/collapse (defaults to the value of Roo.enableFx)
35214  * @cfg {Boolean} singleExpand true if only 1 node per branch may be expanded
35215  * @cfg {Boolean} selModel A tree selection model to use with this TreePanel (defaults to a {@link Roo.tree.DefaultSelectionModel})
35216  * @cfg {Roo.tree.TreeLoader} loader A TreeLoader for use with this TreePanel
35217  * @cfg {Roo.tree.TreeEditor} editor The TreeEditor to display when clicked.
35218  * @cfg {String} pathSeparator The token used to separate sub-paths in path strings (defaults to '/')
35219  * @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>
35220  * @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>
35221  * 
35222  * @constructor
35223  * @param {String/HTMLElement/Element} el The container element
35224  * @param {Object} config
35225  */
35226 Roo.tree.TreePanel = function(el, config){
35227     var root = false;
35228     var loader = false;
35229     if (config.root) {
35230         root = config.root;
35231         delete config.root;
35232     }
35233     if (config.loader) {
35234         loader = config.loader;
35235         delete config.loader;
35236     }
35237     
35238     Roo.apply(this, config);
35239     Roo.tree.TreePanel.superclass.constructor.call(this);
35240     this.el = Roo.get(el);
35241     this.el.addClass('x-tree');
35242     //console.log(root);
35243     if (root) {
35244         this.setRootNode( Roo.factory(root, Roo.tree));
35245     }
35246     if (loader) {
35247         this.loader = Roo.factory(loader, Roo.tree);
35248     }
35249    /**
35250     * Read-only. The id of the container element becomes this TreePanel's id.
35251     */
35252     this.id = this.el.id;
35253     this.addEvents({
35254         /**
35255         * @event beforeload
35256         * Fires before a node is loaded, return false to cancel
35257         * @param {Node} node The node being loaded
35258         */
35259         "beforeload" : true,
35260         /**
35261         * @event load
35262         * Fires when a node is loaded
35263         * @param {Node} node The node that was loaded
35264         */
35265         "load" : true,
35266         /**
35267         * @event textchange
35268         * Fires when the text for a node is changed
35269         * @param {Node} node The node
35270         * @param {String} text The new text
35271         * @param {String} oldText The old text
35272         */
35273         "textchange" : true,
35274         /**
35275         * @event beforeexpand
35276         * Fires before a node is expanded, return false to cancel.
35277         * @param {Node} node The node
35278         * @param {Boolean} deep
35279         * @param {Boolean} anim
35280         */
35281         "beforeexpand" : true,
35282         /**
35283         * @event beforecollapse
35284         * Fires before a node is collapsed, return false to cancel.
35285         * @param {Node} node The node
35286         * @param {Boolean} deep
35287         * @param {Boolean} anim
35288         */
35289         "beforecollapse" : true,
35290         /**
35291         * @event expand
35292         * Fires when a node is expanded
35293         * @param {Node} node The node
35294         */
35295         "expand" : true,
35296         /**
35297         * @event disabledchange
35298         * Fires when the disabled status of a node changes
35299         * @param {Node} node The node
35300         * @param {Boolean} disabled
35301         */
35302         "disabledchange" : true,
35303         /**
35304         * @event collapse
35305         * Fires when a node is collapsed
35306         * @param {Node} node The node
35307         */
35308         "collapse" : true,
35309         /**
35310         * @event beforeclick
35311         * Fires before click processing on a node. Return false to cancel the default action.
35312         * @param {Node} node The node
35313         * @param {Roo.EventObject} e The event object
35314         */
35315         "beforeclick":true,
35316         /**
35317         * @event checkchange
35318         * Fires when a node with a checkbox's checked property changes
35319         * @param {Node} this This node
35320         * @param {Boolean} checked
35321         */
35322         "checkchange":true,
35323         /**
35324         * @event click
35325         * Fires when a node is clicked
35326         * @param {Node} node The node
35327         * @param {Roo.EventObject} e The event object
35328         */
35329         "click":true,
35330         /**
35331         * @event dblclick
35332         * Fires when a node is double clicked
35333         * @param {Node} node The node
35334         * @param {Roo.EventObject} e The event object
35335         */
35336         "dblclick":true,
35337         /**
35338         * @event contextmenu
35339         * Fires when a node is right clicked
35340         * @param {Node} node The node
35341         * @param {Roo.EventObject} e The event object
35342         */
35343         "contextmenu":true,
35344         /**
35345         * @event beforechildrenrendered
35346         * Fires right before the child nodes for a node are rendered
35347         * @param {Node} node The node
35348         */
35349         "beforechildrenrendered":true,
35350         /**
35351         * @event startdrag
35352         * Fires when a node starts being dragged
35353         * @param {Roo.tree.TreePanel} this
35354         * @param {Roo.tree.TreeNode} node
35355         * @param {event} e The raw browser event
35356         */ 
35357        "startdrag" : true,
35358        /**
35359         * @event enddrag
35360         * Fires when a drag operation is complete
35361         * @param {Roo.tree.TreePanel} this
35362         * @param {Roo.tree.TreeNode} node
35363         * @param {event} e The raw browser event
35364         */
35365        "enddrag" : true,
35366        /**
35367         * @event dragdrop
35368         * Fires when a dragged node is dropped on a valid DD target
35369         * @param {Roo.tree.TreePanel} this
35370         * @param {Roo.tree.TreeNode} node
35371         * @param {DD} dd The dd it was dropped on
35372         * @param {event} e The raw browser event
35373         */
35374        "dragdrop" : true,
35375        /**
35376         * @event beforenodedrop
35377         * Fires when a DD object is dropped on a node in this tree for preprocessing. Return false to cancel the drop. The dropEvent
35378         * passed to handlers has the following properties:<br />
35379         * <ul style="padding:5px;padding-left:16px;">
35380         * <li>tree - The TreePanel</li>
35381         * <li>target - The node being targeted for the drop</li>
35382         * <li>data - The drag data from the drag source</li>
35383         * <li>point - The point of the drop - append, above or below</li>
35384         * <li>source - The drag source</li>
35385         * <li>rawEvent - Raw mouse event</li>
35386         * <li>dropNode - Drop node(s) provided by the source <b>OR</b> you can supply node(s)
35387         * to be inserted by setting them on this object.</li>
35388         * <li>cancel - Set this to true to cancel the drop.</li>
35389         * </ul>
35390         * @param {Object} dropEvent
35391         */
35392        "beforenodedrop" : true,
35393        /**
35394         * @event nodedrop
35395         * Fires after a DD object is dropped on a node in this tree. The dropEvent
35396         * passed to handlers has the following properties:<br />
35397         * <ul style="padding:5px;padding-left:16px;">
35398         * <li>tree - The TreePanel</li>
35399         * <li>target - The node being targeted for the drop</li>
35400         * <li>data - The drag data from the drag source</li>
35401         * <li>point - The point of the drop - append, above or below</li>
35402         * <li>source - The drag source</li>
35403         * <li>rawEvent - Raw mouse event</li>
35404         * <li>dropNode - Dropped node(s).</li>
35405         * </ul>
35406         * @param {Object} dropEvent
35407         */
35408        "nodedrop" : true,
35409         /**
35410         * @event nodedragover
35411         * Fires when a tree node is being targeted for a drag drop, return false to signal drop not allowed. The dragOverEvent
35412         * passed to handlers has the following properties:<br />
35413         * <ul style="padding:5px;padding-left:16px;">
35414         * <li>tree - The TreePanel</li>
35415         * <li>target - The node being targeted for the drop</li>
35416         * <li>data - The drag data from the drag source</li>
35417         * <li>point - The point of the drop - append, above or below</li>
35418         * <li>source - The drag source</li>
35419         * <li>rawEvent - Raw mouse event</li>
35420         * <li>dropNode - Drop node(s) provided by the source.</li>
35421         * <li>cancel - Set this to true to signal drop not allowed.</li>
35422         * </ul>
35423         * @param {Object} dragOverEvent
35424         */
35425        "nodedragover" : true,
35426        /**
35427         * @event appendnode
35428         * Fires when append node to the tree
35429         * @param {Roo.tree.TreePanel} this
35430         * @param {Roo.tree.TreeNode} node
35431         * @param {Number} index The index of the newly appended node
35432         */
35433        "appendnode" : true
35434         
35435     });
35436     if(this.singleExpand){
35437        this.on("beforeexpand", this.restrictExpand, this);
35438     }
35439     if (this.editor) {
35440         this.editor.tree = this;
35441         this.editor = Roo.factory(this.editor, Roo.tree);
35442     }
35443     
35444     if (this.selModel) {
35445         this.selModel = Roo.factory(this.selModel, Roo.tree);
35446     }
35447    
35448 };
35449 Roo.extend(Roo.tree.TreePanel, Roo.data.Tree, {
35450     rootVisible : true,
35451     animate: Roo.enableFx,
35452     lines : true,
35453     enableDD : false,
35454     hlDrop : Roo.enableFx,
35455   
35456     renderer: false,
35457     
35458     rendererTip: false,
35459     // private
35460     restrictExpand : function(node){
35461         var p = node.parentNode;
35462         if(p){
35463             if(p.expandedChild && p.expandedChild.parentNode == p){
35464                 p.expandedChild.collapse();
35465             }
35466             p.expandedChild = node;
35467         }
35468     },
35469
35470     // private override
35471     setRootNode : function(node){
35472         Roo.tree.TreePanel.superclass.setRootNode.call(this, node);
35473         if(!this.rootVisible){
35474             node.ui = new Roo.tree.RootTreeNodeUI(node);
35475         }
35476         return node;
35477     },
35478
35479     /**
35480      * Returns the container element for this TreePanel
35481      */
35482     getEl : function(){
35483         return this.el;
35484     },
35485
35486     /**
35487      * Returns the default TreeLoader for this TreePanel
35488      */
35489     getLoader : function(){
35490         return this.loader;
35491     },
35492
35493     /**
35494      * Expand all nodes
35495      */
35496     expandAll : function(){
35497         this.root.expand(true);
35498     },
35499
35500     /**
35501      * Collapse all nodes
35502      */
35503     collapseAll : function(){
35504         this.root.collapse(true);
35505     },
35506
35507     /**
35508      * Returns the selection model used by this TreePanel
35509      */
35510     getSelectionModel : function(){
35511         if(!this.selModel){
35512             this.selModel = new Roo.tree.DefaultSelectionModel();
35513         }
35514         return this.selModel;
35515     },
35516
35517     /**
35518      * Retrieve an array of checked nodes, or an array of a specific attribute of checked nodes (e.g. "id")
35519      * @param {String} attribute (optional) Defaults to null (return the actual nodes)
35520      * @param {TreeNode} startNode (optional) The node to start from, defaults to the root
35521      * @return {Array}
35522      */
35523     getChecked : function(a, startNode){
35524         startNode = startNode || this.root;
35525         var r = [];
35526         var f = function(){
35527             if(this.attributes.checked){
35528                 r.push(!a ? this : (a == 'id' ? this.id : this.attributes[a]));
35529             }
35530         }
35531         startNode.cascade(f);
35532         return r;
35533     },
35534
35535     /**
35536      * Expands a specified path in this TreePanel. A path can be retrieved from a node with {@link Roo.data.Node#getPath}
35537      * @param {String} path
35538      * @param {String} attr (optional) The attribute used in the path (see {@link Roo.data.Node#getPath} for more info)
35539      * @param {Function} callback (optional) The callback to call when the expand is complete. The callback will be called with
35540      * (bSuccess, oLastNode) where bSuccess is if the expand was successful and oLastNode is the last node that was expanded.
35541      */
35542     expandPath : function(path, attr, callback){
35543         attr = attr || "id";
35544         var keys = path.split(this.pathSeparator);
35545         var curNode = this.root;
35546         if(curNode.attributes[attr] != keys[1]){ // invalid root
35547             if(callback){
35548                 callback(false, null);
35549             }
35550             return;
35551         }
35552         var index = 1;
35553         var f = function(){
35554             if(++index == keys.length){
35555                 if(callback){
35556                     callback(true, curNode);
35557                 }
35558                 return;
35559             }
35560             var c = curNode.findChild(attr, keys[index]);
35561             if(!c){
35562                 if(callback){
35563                     callback(false, curNode);
35564                 }
35565                 return;
35566             }
35567             curNode = c;
35568             c.expand(false, false, f);
35569         };
35570         curNode.expand(false, false, f);
35571     },
35572
35573     /**
35574      * Selects the node in this tree at the specified path. A path can be retrieved from a node with {@link Roo.data.Node#getPath}
35575      * @param {String} path
35576      * @param {String} attr (optional) The attribute used in the path (see {@link Roo.data.Node#getPath} for more info)
35577      * @param {Function} callback (optional) The callback to call when the selection is complete. The callback will be called with
35578      * (bSuccess, oSelNode) where bSuccess is if the selection was successful and oSelNode is the selected node.
35579      */
35580     selectPath : function(path, attr, callback){
35581         attr = attr || "id";
35582         var keys = path.split(this.pathSeparator);
35583         var v = keys.pop();
35584         if(keys.length > 0){
35585             var f = function(success, node){
35586                 if(success && node){
35587                     var n = node.findChild(attr, v);
35588                     if(n){
35589                         n.select();
35590                         if(callback){
35591                             callback(true, n);
35592                         }
35593                     }else if(callback){
35594                         callback(false, n);
35595                     }
35596                 }else{
35597                     if(callback){
35598                         callback(false, n);
35599                     }
35600                 }
35601             };
35602             this.expandPath(keys.join(this.pathSeparator), attr, f);
35603         }else{
35604             this.root.select();
35605             if(callback){
35606                 callback(true, this.root);
35607             }
35608         }
35609     },
35610
35611     getTreeEl : function(){
35612         return this.el;
35613     },
35614
35615     /**
35616      * Trigger rendering of this TreePanel
35617      */
35618     render : function(){
35619         if (this.innerCt) {
35620             return this; // stop it rendering more than once!!
35621         }
35622         
35623         this.innerCt = this.el.createChild({tag:"ul",
35624                cls:"x-tree-root-ct " +
35625                (this.lines ? "x-tree-lines" : "x-tree-no-lines")});
35626
35627         if(this.containerScroll){
35628             Roo.dd.ScrollManager.register(this.el);
35629         }
35630         if((this.enableDD || this.enableDrop) && !this.dropZone){
35631            /**
35632             * The dropZone used by this tree if drop is enabled
35633             * @type Roo.tree.TreeDropZone
35634             */
35635              this.dropZone = new Roo.tree.TreeDropZone(this, this.dropConfig || {
35636                ddGroup: this.ddGroup || "TreeDD", appendOnly: this.ddAppendOnly === true
35637            });
35638         }
35639         if((this.enableDD || this.enableDrag) && !this.dragZone){
35640            /**
35641             * The dragZone used by this tree if drag is enabled
35642             * @type Roo.tree.TreeDragZone
35643             */
35644             this.dragZone = new Roo.tree.TreeDragZone(this, this.dragConfig || {
35645                ddGroup: this.ddGroup || "TreeDD",
35646                scroll: this.ddScroll
35647            });
35648         }
35649         this.getSelectionModel().init(this);
35650         if (!this.root) {
35651             Roo.log("ROOT not set in tree");
35652             return this;
35653         }
35654         this.root.render();
35655         if(!this.rootVisible){
35656             this.root.renderChildren();
35657         }
35658         return this;
35659     }
35660 });/*
35661  * Based on:
35662  * Ext JS Library 1.1.1
35663  * Copyright(c) 2006-2007, Ext JS, LLC.
35664  *
35665  * Originally Released Under LGPL - original licence link has changed is not relivant.
35666  *
35667  * Fork - LGPL
35668  * <script type="text/javascript">
35669  */
35670  
35671
35672 /**
35673  * @class Roo.tree.DefaultSelectionModel
35674  * @extends Roo.util.Observable
35675  * The default single selection for a TreePanel.
35676  * @param {Object} cfg Configuration
35677  */
35678 Roo.tree.DefaultSelectionModel = function(cfg){
35679    this.selNode = null;
35680    
35681    
35682    
35683    this.addEvents({
35684        /**
35685         * @event selectionchange
35686         * Fires when the selected node changes
35687         * @param {DefaultSelectionModel} this
35688         * @param {TreeNode} node the new selection
35689         */
35690        "selectionchange" : true,
35691
35692        /**
35693         * @event beforeselect
35694         * Fires before the selected node changes, return false to cancel the change
35695         * @param {DefaultSelectionModel} this
35696         * @param {TreeNode} node the new selection
35697         * @param {TreeNode} node the old selection
35698         */
35699        "beforeselect" : true
35700    });
35701    
35702     Roo.tree.DefaultSelectionModel.superclass.constructor.call(this,cfg);
35703 };
35704
35705 Roo.extend(Roo.tree.DefaultSelectionModel, Roo.util.Observable, {
35706     init : function(tree){
35707         this.tree = tree;
35708         tree.getTreeEl().on("keydown", this.onKeyDown, this);
35709         tree.on("click", this.onNodeClick, this);
35710     },
35711     
35712     onNodeClick : function(node, e){
35713         if (e.ctrlKey && this.selNode == node)  {
35714             this.unselect(node);
35715             return;
35716         }
35717         this.select(node);
35718     },
35719     
35720     /**
35721      * Select a node.
35722      * @param {TreeNode} node The node to select
35723      * @return {TreeNode} The selected node
35724      */
35725     select : function(node){
35726         var last = this.selNode;
35727         if(last != node && this.fireEvent('beforeselect', this, node, last) !== false){
35728             if(last){
35729                 last.ui.onSelectedChange(false);
35730             }
35731             this.selNode = node;
35732             node.ui.onSelectedChange(true);
35733             this.fireEvent("selectionchange", this, node, last);
35734         }
35735         return node;
35736     },
35737     
35738     /**
35739      * Deselect a node.
35740      * @param {TreeNode} node The node to unselect
35741      */
35742     unselect : function(node){
35743         if(this.selNode == node){
35744             this.clearSelections();
35745         }    
35746     },
35747     
35748     /**
35749      * Clear all selections
35750      */
35751     clearSelections : function(){
35752         var n = this.selNode;
35753         if(n){
35754             n.ui.onSelectedChange(false);
35755             this.selNode = null;
35756             this.fireEvent("selectionchange", this, null);
35757         }
35758         return n;
35759     },
35760     
35761     /**
35762      * Get the selected node
35763      * @return {TreeNode} The selected node
35764      */
35765     getSelectedNode : function(){
35766         return this.selNode;    
35767     },
35768     
35769     /**
35770      * Returns true if the node is selected
35771      * @param {TreeNode} node The node to check
35772      * @return {Boolean}
35773      */
35774     isSelected : function(node){
35775         return this.selNode == node;  
35776     },
35777
35778     /**
35779      * Selects the node above the selected node in the tree, intelligently walking the nodes
35780      * @return TreeNode The new selection
35781      */
35782     selectPrevious : function(){
35783         var s = this.selNode || this.lastSelNode;
35784         if(!s){
35785             return null;
35786         }
35787         var ps = s.previousSibling;
35788         if(ps){
35789             if(!ps.isExpanded() || ps.childNodes.length < 1){
35790                 return this.select(ps);
35791             } else{
35792                 var lc = ps.lastChild;
35793                 while(lc && lc.isExpanded() && lc.childNodes.length > 0){
35794                     lc = lc.lastChild;
35795                 }
35796                 return this.select(lc);
35797             }
35798         } else if(s.parentNode && (this.tree.rootVisible || !s.parentNode.isRoot)){
35799             return this.select(s.parentNode);
35800         }
35801         return null;
35802     },
35803
35804     /**
35805      * Selects the node above the selected node in the tree, intelligently walking the nodes
35806      * @return TreeNode The new selection
35807      */
35808     selectNext : function(){
35809         var s = this.selNode || this.lastSelNode;
35810         if(!s){
35811             return null;
35812         }
35813         if(s.firstChild && s.isExpanded()){
35814              return this.select(s.firstChild);
35815          }else if(s.nextSibling){
35816              return this.select(s.nextSibling);
35817          }else if(s.parentNode){
35818             var newS = null;
35819             s.parentNode.bubble(function(){
35820                 if(this.nextSibling){
35821                     newS = this.getOwnerTree().selModel.select(this.nextSibling);
35822                     return false;
35823                 }
35824             });
35825             return newS;
35826          }
35827         return null;
35828     },
35829
35830     onKeyDown : function(e){
35831         var s = this.selNode || this.lastSelNode;
35832         // undesirable, but required
35833         var sm = this;
35834         if(!s){
35835             return;
35836         }
35837         var k = e.getKey();
35838         switch(k){
35839              case e.DOWN:
35840                  e.stopEvent();
35841                  this.selectNext();
35842              break;
35843              case e.UP:
35844                  e.stopEvent();
35845                  this.selectPrevious();
35846              break;
35847              case e.RIGHT:
35848                  e.preventDefault();
35849                  if(s.hasChildNodes()){
35850                      if(!s.isExpanded()){
35851                          s.expand();
35852                      }else if(s.firstChild){
35853                          this.select(s.firstChild, e);
35854                      }
35855                  }
35856              break;
35857              case e.LEFT:
35858                  e.preventDefault();
35859                  if(s.hasChildNodes() && s.isExpanded()){
35860                      s.collapse();
35861                  }else if(s.parentNode && (this.tree.rootVisible || s.parentNode != this.tree.getRootNode())){
35862                      this.select(s.parentNode, e);
35863                  }
35864              break;
35865         };
35866     }
35867 });
35868
35869 /**
35870  * @class Roo.tree.MultiSelectionModel
35871  * @extends Roo.util.Observable
35872  * Multi selection for a TreePanel.
35873  * @param {Object} cfg Configuration
35874  */
35875 Roo.tree.MultiSelectionModel = function(){
35876    this.selNodes = [];
35877    this.selMap = {};
35878    this.addEvents({
35879        /**
35880         * @event selectionchange
35881         * Fires when the selected nodes change
35882         * @param {MultiSelectionModel} this
35883         * @param {Array} nodes Array of the selected nodes
35884         */
35885        "selectionchange" : true
35886    });
35887    Roo.tree.MultiSelectionModel.superclass.constructor.call(this,cfg);
35888    
35889 };
35890
35891 Roo.extend(Roo.tree.MultiSelectionModel, Roo.util.Observable, {
35892     init : function(tree){
35893         this.tree = tree;
35894         tree.getTreeEl().on("keydown", this.onKeyDown, this);
35895         tree.on("click", this.onNodeClick, this);
35896     },
35897     
35898     onNodeClick : function(node, e){
35899         this.select(node, e, e.ctrlKey);
35900     },
35901     
35902     /**
35903      * Select a node.
35904      * @param {TreeNode} node The node to select
35905      * @param {EventObject} e (optional) An event associated with the selection
35906      * @param {Boolean} keepExisting True to retain existing selections
35907      * @return {TreeNode} The selected node
35908      */
35909     select : function(node, e, keepExisting){
35910         if(keepExisting !== true){
35911             this.clearSelections(true);
35912         }
35913         if(this.isSelected(node)){
35914             this.lastSelNode = node;
35915             return node;
35916         }
35917         this.selNodes.push(node);
35918         this.selMap[node.id] = node;
35919         this.lastSelNode = node;
35920         node.ui.onSelectedChange(true);
35921         this.fireEvent("selectionchange", this, this.selNodes);
35922         return node;
35923     },
35924     
35925     /**
35926      * Deselect a node.
35927      * @param {TreeNode} node The node to unselect
35928      */
35929     unselect : function(node){
35930         if(this.selMap[node.id]){
35931             node.ui.onSelectedChange(false);
35932             var sn = this.selNodes;
35933             var index = -1;
35934             if(sn.indexOf){
35935                 index = sn.indexOf(node);
35936             }else{
35937                 for(var i = 0, len = sn.length; i < len; i++){
35938                     if(sn[i] == node){
35939                         index = i;
35940                         break;
35941                     }
35942                 }
35943             }
35944             if(index != -1){
35945                 this.selNodes.splice(index, 1);
35946             }
35947             delete this.selMap[node.id];
35948             this.fireEvent("selectionchange", this, this.selNodes);
35949         }
35950     },
35951     
35952     /**
35953      * Clear all selections
35954      */
35955     clearSelections : function(suppressEvent){
35956         var sn = this.selNodes;
35957         if(sn.length > 0){
35958             for(var i = 0, len = sn.length; i < len; i++){
35959                 sn[i].ui.onSelectedChange(false);
35960             }
35961             this.selNodes = [];
35962             this.selMap = {};
35963             if(suppressEvent !== true){
35964                 this.fireEvent("selectionchange", this, this.selNodes);
35965             }
35966         }
35967     },
35968     
35969     /**
35970      * Returns true if the node is selected
35971      * @param {TreeNode} node The node to check
35972      * @return {Boolean}
35973      */
35974     isSelected : function(node){
35975         return this.selMap[node.id] ? true : false;  
35976     },
35977     
35978     /**
35979      * Returns an array of the selected nodes
35980      * @return {Array}
35981      */
35982     getSelectedNodes : function(){
35983         return this.selNodes;    
35984     },
35985
35986     onKeyDown : Roo.tree.DefaultSelectionModel.prototype.onKeyDown,
35987
35988     selectNext : Roo.tree.DefaultSelectionModel.prototype.selectNext,
35989
35990     selectPrevious : Roo.tree.DefaultSelectionModel.prototype.selectPrevious
35991 });/*
35992  * Based on:
35993  * Ext JS Library 1.1.1
35994  * Copyright(c) 2006-2007, Ext JS, LLC.
35995  *
35996  * Originally Released Under LGPL - original licence link has changed is not relivant.
35997  *
35998  * Fork - LGPL
35999  * <script type="text/javascript">
36000  */
36001  
36002 /**
36003  * @class Roo.tree.TreeNode
36004  * @extends Roo.data.Node
36005  * @cfg {String} text The text for this node
36006  * @cfg {Boolean} expanded true to start the node expanded
36007  * @cfg {Boolean} allowDrag false to make this node undraggable if DD is on (defaults to true)
36008  * @cfg {Boolean} allowDrop false if this node cannot be drop on
36009  * @cfg {Boolean} disabled true to start the node disabled
36010  * @cfg {String} icon The path to an icon for the node. The preferred way to do this
36011  *    is to use the cls or iconCls attributes and add the icon via a CSS background image.
36012  * @cfg {String} cls A css class to be added to the node
36013  * @cfg {String} iconCls A css class to be added to the nodes icon element for applying css background images
36014  * @cfg {String} href URL of the link used for the node (defaults to #)
36015  * @cfg {String} hrefTarget target frame for the link
36016  * @cfg {String} qtip An Ext QuickTip for the node
36017  * @cfg {String} qtipCfg An Ext QuickTip config for the node (used instead of qtip)
36018  * @cfg {Boolean} singleClickExpand True for single click expand on this node
36019  * @cfg {Function} uiProvider A UI <b>class</b> to use for this node (defaults to Roo.tree.TreeNodeUI)
36020  * @cfg {Boolean} checked True to render a checked checkbox for this node, false to render an unchecked checkbox
36021  * (defaults to undefined with no checkbox rendered)
36022  * @constructor
36023  * @param {Object/String} attributes The attributes/config for the node or just a string with the text for the node
36024  */
36025 Roo.tree.TreeNode = function(attributes){
36026     attributes = attributes || {};
36027     if(typeof attributes == "string"){
36028         attributes = {text: attributes};
36029     }
36030     this.childrenRendered = false;
36031     this.rendered = false;
36032     Roo.tree.TreeNode.superclass.constructor.call(this, attributes);
36033     this.expanded = attributes.expanded === true;
36034     this.isTarget = attributes.isTarget !== false;
36035     this.draggable = attributes.draggable !== false && attributes.allowDrag !== false;
36036     this.allowChildren = attributes.allowChildren !== false && attributes.allowDrop !== false;
36037
36038     /**
36039      * Read-only. The text for this node. To change it use setText().
36040      * @type String
36041      */
36042     this.text = attributes.text;
36043     /**
36044      * True if this node is disabled.
36045      * @type Boolean
36046      */
36047     this.disabled = attributes.disabled === true;
36048
36049     this.addEvents({
36050         /**
36051         * @event textchange
36052         * Fires when the text for this node is changed
36053         * @param {Node} this This node
36054         * @param {String} text The new text
36055         * @param {String} oldText The old text
36056         */
36057         "textchange" : true,
36058         /**
36059         * @event beforeexpand
36060         * Fires before this node is expanded, return false to cancel.
36061         * @param {Node} this This node
36062         * @param {Boolean} deep
36063         * @param {Boolean} anim
36064         */
36065         "beforeexpand" : true,
36066         /**
36067         * @event beforecollapse
36068         * Fires before this node is collapsed, return false to cancel.
36069         * @param {Node} this This node
36070         * @param {Boolean} deep
36071         * @param {Boolean} anim
36072         */
36073         "beforecollapse" : true,
36074         /**
36075         * @event expand
36076         * Fires when this node is expanded
36077         * @param {Node} this This node
36078         */
36079         "expand" : true,
36080         /**
36081         * @event disabledchange
36082         * Fires when the disabled status of this node changes
36083         * @param {Node} this This node
36084         * @param {Boolean} disabled
36085         */
36086         "disabledchange" : true,
36087         /**
36088         * @event collapse
36089         * Fires when this node is collapsed
36090         * @param {Node} this This node
36091         */
36092         "collapse" : true,
36093         /**
36094         * @event beforeclick
36095         * Fires before click processing. Return false to cancel the default action.
36096         * @param {Node} this This node
36097         * @param {Roo.EventObject} e The event object
36098         */
36099         "beforeclick":true,
36100         /**
36101         * @event checkchange
36102         * Fires when a node with a checkbox's checked property changes
36103         * @param {Node} this This node
36104         * @param {Boolean} checked
36105         */
36106         "checkchange":true,
36107         /**
36108         * @event click
36109         * Fires when this node is clicked
36110         * @param {Node} this This node
36111         * @param {Roo.EventObject} e The event object
36112         */
36113         "click":true,
36114         /**
36115         * @event dblclick
36116         * Fires when this node is double clicked
36117         * @param {Node} this This node
36118         * @param {Roo.EventObject} e The event object
36119         */
36120         "dblclick":true,
36121         /**
36122         * @event contextmenu
36123         * Fires when this node is right clicked
36124         * @param {Node} this This node
36125         * @param {Roo.EventObject} e The event object
36126         */
36127         "contextmenu":true,
36128         /**
36129         * @event beforechildrenrendered
36130         * Fires right before the child nodes for this node are rendered
36131         * @param {Node} this This node
36132         */
36133         "beforechildrenrendered":true
36134     });
36135
36136     var uiClass = this.attributes.uiProvider || Roo.tree.TreeNodeUI;
36137
36138     /**
36139      * Read-only. The UI for this node
36140      * @type TreeNodeUI
36141      */
36142     this.ui = new uiClass(this);
36143     
36144     // finally support items[]
36145     if (typeof(this.attributes.items) == 'undefined' || !this.attributes.items) {
36146         return;
36147     }
36148     
36149     
36150     Roo.each(this.attributes.items, function(c) {
36151         this.appendChild(Roo.factory(c,Roo.Tree));
36152     }, this);
36153     delete this.attributes.items;
36154     
36155     
36156     
36157 };
36158 Roo.extend(Roo.tree.TreeNode, Roo.data.Node, {
36159     preventHScroll: true,
36160     /**
36161      * Returns true if this node is expanded
36162      * @return {Boolean}
36163      */
36164     isExpanded : function(){
36165         return this.expanded;
36166     },
36167
36168     /**
36169      * Returns the UI object for this node
36170      * @return {TreeNodeUI}
36171      */
36172     getUI : function(){
36173         return this.ui;
36174     },
36175
36176     // private override
36177     setFirstChild : function(node){
36178         var of = this.firstChild;
36179         Roo.tree.TreeNode.superclass.setFirstChild.call(this, node);
36180         if(this.childrenRendered && of && node != of){
36181             of.renderIndent(true, true);
36182         }
36183         if(this.rendered){
36184             this.renderIndent(true, true);
36185         }
36186     },
36187
36188     // private override
36189     setLastChild : function(node){
36190         var ol = this.lastChild;
36191         Roo.tree.TreeNode.superclass.setLastChild.call(this, node);
36192         if(this.childrenRendered && ol && node != ol){
36193             ol.renderIndent(true, true);
36194         }
36195         if(this.rendered){
36196             this.renderIndent(true, true);
36197         }
36198     },
36199
36200     // these methods are overridden to provide lazy rendering support
36201     // private override
36202     appendChild : function()
36203     {
36204         var node = Roo.tree.TreeNode.superclass.appendChild.apply(this, arguments);
36205         if(node && this.childrenRendered){
36206             node.render();
36207         }
36208         this.ui.updateExpandIcon();
36209         return node;
36210     },
36211
36212     // private override
36213     removeChild : function(node){
36214         this.ownerTree.getSelectionModel().unselect(node);
36215         Roo.tree.TreeNode.superclass.removeChild.apply(this, arguments);
36216         // if it's been rendered remove dom node
36217         if(this.childrenRendered){
36218             node.ui.remove();
36219         }
36220         if(this.childNodes.length < 1){
36221             this.collapse(false, false);
36222         }else{
36223             this.ui.updateExpandIcon();
36224         }
36225         if(!this.firstChild) {
36226             this.childrenRendered = false;
36227         }
36228         return node;
36229     },
36230
36231     // private override
36232     insertBefore : function(node, refNode){
36233         var newNode = Roo.tree.TreeNode.superclass.insertBefore.apply(this, arguments);
36234         if(newNode && refNode && this.childrenRendered){
36235             node.render();
36236         }
36237         this.ui.updateExpandIcon();
36238         return newNode;
36239     },
36240
36241     /**
36242      * Sets the text for this node
36243      * @param {String} text
36244      */
36245     setText : function(text){
36246         var oldText = this.text;
36247         this.text = text;
36248         this.attributes.text = text;
36249         if(this.rendered){ // event without subscribing
36250             this.ui.onTextChange(this, text, oldText);
36251         }
36252         this.fireEvent("textchange", this, text, oldText);
36253     },
36254
36255     /**
36256      * Triggers selection of this node
36257      */
36258     select : function(){
36259         this.getOwnerTree().getSelectionModel().select(this);
36260     },
36261
36262     /**
36263      * Triggers deselection of this node
36264      */
36265     unselect : function(){
36266         this.getOwnerTree().getSelectionModel().unselect(this);
36267     },
36268
36269     /**
36270      * Returns true if this node is selected
36271      * @return {Boolean}
36272      */
36273     isSelected : function(){
36274         return this.getOwnerTree().getSelectionModel().isSelected(this);
36275     },
36276
36277     /**
36278      * Expand this node.
36279      * @param {Boolean} deep (optional) True to expand all children as well
36280      * @param {Boolean} anim (optional) false to cancel the default animation
36281      * @param {Function} callback (optional) A callback to be called when
36282      * expanding this node completes (does not wait for deep expand to complete).
36283      * Called with 1 parameter, this node.
36284      */
36285     expand : function(deep, anim, callback){
36286         if(!this.expanded){
36287             if(this.fireEvent("beforeexpand", this, deep, anim) === false){
36288                 return;
36289             }
36290             if(!this.childrenRendered){
36291                 this.renderChildren();
36292             }
36293             this.expanded = true;
36294             
36295             if(!this.isHiddenRoot() && (this.getOwnerTree() && this.getOwnerTree().animate && anim !== false) || anim){
36296                 this.ui.animExpand(function(){
36297                     this.fireEvent("expand", this);
36298                     if(typeof callback == "function"){
36299                         callback(this);
36300                     }
36301                     if(deep === true){
36302                         this.expandChildNodes(true);
36303                     }
36304                 }.createDelegate(this));
36305                 return;
36306             }else{
36307                 this.ui.expand();
36308                 this.fireEvent("expand", this);
36309                 if(typeof callback == "function"){
36310                     callback(this);
36311                 }
36312             }
36313         }else{
36314            if(typeof callback == "function"){
36315                callback(this);
36316            }
36317         }
36318         if(deep === true){
36319             this.expandChildNodes(true);
36320         }
36321     },
36322
36323     isHiddenRoot : function(){
36324         return this.isRoot && !this.getOwnerTree().rootVisible;
36325     },
36326
36327     /**
36328      * Collapse this node.
36329      * @param {Boolean} deep (optional) True to collapse all children as well
36330      * @param {Boolean} anim (optional) false to cancel the default animation
36331      */
36332     collapse : function(deep, anim){
36333         if(this.expanded && !this.isHiddenRoot()){
36334             if(this.fireEvent("beforecollapse", this, deep, anim) === false){
36335                 return;
36336             }
36337             this.expanded = false;
36338             if((this.getOwnerTree().animate && anim !== false) || anim){
36339                 this.ui.animCollapse(function(){
36340                     this.fireEvent("collapse", this);
36341                     if(deep === true){
36342                         this.collapseChildNodes(true);
36343                     }
36344                 }.createDelegate(this));
36345                 return;
36346             }else{
36347                 this.ui.collapse();
36348                 this.fireEvent("collapse", this);
36349             }
36350         }
36351         if(deep === true){
36352             var cs = this.childNodes;
36353             for(var i = 0, len = cs.length; i < len; i++) {
36354                 cs[i].collapse(true, false);
36355             }
36356         }
36357     },
36358
36359     // private
36360     delayedExpand : function(delay){
36361         if(!this.expandProcId){
36362             this.expandProcId = this.expand.defer(delay, this);
36363         }
36364     },
36365
36366     // private
36367     cancelExpand : function(){
36368         if(this.expandProcId){
36369             clearTimeout(this.expandProcId);
36370         }
36371         this.expandProcId = false;
36372     },
36373
36374     /**
36375      * Toggles expanded/collapsed state of the node
36376      */
36377     toggle : function(){
36378         if(this.expanded){
36379             this.collapse();
36380         }else{
36381             this.expand();
36382         }
36383     },
36384
36385     /**
36386      * Ensures all parent nodes are expanded
36387      */
36388     ensureVisible : function(callback){
36389         var tree = this.getOwnerTree();
36390         tree.expandPath(this.parentNode.getPath(), false, function(){
36391             tree.getTreeEl().scrollChildIntoView(this.ui.anchor);
36392             Roo.callback(callback);
36393         }.createDelegate(this));
36394     },
36395
36396     /**
36397      * Expand all child nodes
36398      * @param {Boolean} deep (optional) true if the child nodes should also expand their child nodes
36399      */
36400     expandChildNodes : function(deep){
36401         var cs = this.childNodes;
36402         for(var i = 0, len = cs.length; i < len; i++) {
36403                 cs[i].expand(deep);
36404         }
36405     },
36406
36407     /**
36408      * Collapse all child nodes
36409      * @param {Boolean} deep (optional) true if the child nodes should also collapse their child nodes
36410      */
36411     collapseChildNodes : function(deep){
36412         var cs = this.childNodes;
36413         for(var i = 0, len = cs.length; i < len; i++) {
36414                 cs[i].collapse(deep);
36415         }
36416     },
36417
36418     /**
36419      * Disables this node
36420      */
36421     disable : function(){
36422         this.disabled = true;
36423         this.unselect();
36424         if(this.rendered && this.ui.onDisableChange){ // event without subscribing
36425             this.ui.onDisableChange(this, true);
36426         }
36427         this.fireEvent("disabledchange", this, true);
36428     },
36429
36430     /**
36431      * Enables this node
36432      */
36433     enable : function(){
36434         this.disabled = false;
36435         if(this.rendered && this.ui.onDisableChange){ // event without subscribing
36436             this.ui.onDisableChange(this, false);
36437         }
36438         this.fireEvent("disabledchange", this, false);
36439     },
36440
36441     // private
36442     renderChildren : function(suppressEvent){
36443         if(suppressEvent !== false){
36444             this.fireEvent("beforechildrenrendered", this);
36445         }
36446         var cs = this.childNodes;
36447         for(var i = 0, len = cs.length; i < len; i++){
36448             cs[i].render(true);
36449         }
36450         this.childrenRendered = true;
36451     },
36452
36453     // private
36454     sort : function(fn, scope){
36455         Roo.tree.TreeNode.superclass.sort.apply(this, arguments);
36456         if(this.childrenRendered){
36457             var cs = this.childNodes;
36458             for(var i = 0, len = cs.length; i < len; i++){
36459                 cs[i].render(true);
36460             }
36461         }
36462     },
36463
36464     // private
36465     render : function(bulkRender){
36466         this.ui.render(bulkRender);
36467         if(!this.rendered){
36468             this.rendered = true;
36469             if(this.expanded){
36470                 this.expanded = false;
36471                 this.expand(false, false);
36472             }
36473         }
36474     },
36475
36476     // private
36477     renderIndent : function(deep, refresh){
36478         if(refresh){
36479             this.ui.childIndent = null;
36480         }
36481         this.ui.renderIndent();
36482         if(deep === true && this.childrenRendered){
36483             var cs = this.childNodes;
36484             for(var i = 0, len = cs.length; i < len; i++){
36485                 cs[i].renderIndent(true, refresh);
36486             }
36487         }
36488     }
36489 });/*
36490  * Based on:
36491  * Ext JS Library 1.1.1
36492  * Copyright(c) 2006-2007, Ext JS, LLC.
36493  *
36494  * Originally Released Under LGPL - original licence link has changed is not relivant.
36495  *
36496  * Fork - LGPL
36497  * <script type="text/javascript">
36498  */
36499  
36500 /**
36501  * @class Roo.tree.AsyncTreeNode
36502  * @extends Roo.tree.TreeNode
36503  * @cfg {TreeLoader} loader A TreeLoader to be used by this node (defaults to the loader defined on the tree)
36504  * @constructor
36505  * @param {Object/String} attributes The attributes/config for the node or just a string with the text for the node 
36506  */
36507  Roo.tree.AsyncTreeNode = function(config){
36508     this.loaded = false;
36509     this.loading = false;
36510     Roo.tree.AsyncTreeNode.superclass.constructor.apply(this, arguments);
36511     /**
36512     * @event beforeload
36513     * Fires before this node is loaded, return false to cancel
36514     * @param {Node} this This node
36515     */
36516     this.addEvents({'beforeload':true, 'load': true});
36517     /**
36518     * @event load
36519     * Fires when this node is loaded
36520     * @param {Node} this This node
36521     */
36522     /**
36523      * The loader used by this node (defaults to using the tree's defined loader)
36524      * @type TreeLoader
36525      * @property loader
36526      */
36527 };
36528 Roo.extend(Roo.tree.AsyncTreeNode, Roo.tree.TreeNode, {
36529     expand : function(deep, anim, callback){
36530         if(this.loading){ // if an async load is already running, waiting til it's done
36531             var timer;
36532             var f = function(){
36533                 if(!this.loading){ // done loading
36534                     clearInterval(timer);
36535                     this.expand(deep, anim, callback);
36536                 }
36537             }.createDelegate(this);
36538             timer = setInterval(f, 200);
36539             return;
36540         }
36541         if(!this.loaded){
36542             if(this.fireEvent("beforeload", this) === false){
36543                 return;
36544             }
36545             this.loading = true;
36546             this.ui.beforeLoad(this);
36547             var loader = this.loader || this.attributes.loader || this.getOwnerTree().getLoader();
36548             if(loader){
36549                 loader.load(this, this.loadComplete.createDelegate(this, [deep, anim, callback]));
36550                 return;
36551             }
36552         }
36553         Roo.tree.AsyncTreeNode.superclass.expand.call(this, deep, anim, callback);
36554     },
36555     
36556     /**
36557      * Returns true if this node is currently loading
36558      * @return {Boolean}
36559      */
36560     isLoading : function(){
36561         return this.loading;  
36562     },
36563     
36564     loadComplete : function(deep, anim, callback){
36565         this.loading = false;
36566         this.loaded = true;
36567         this.ui.afterLoad(this);
36568         this.fireEvent("load", this);
36569         this.expand(deep, anim, callback);
36570     },
36571     
36572     /**
36573      * Returns true if this node has been loaded
36574      * @return {Boolean}
36575      */
36576     isLoaded : function(){
36577         return this.loaded;
36578     },
36579     
36580     hasChildNodes : function(){
36581         if(!this.isLeaf() && !this.loaded){
36582             return true;
36583         }else{
36584             return Roo.tree.AsyncTreeNode.superclass.hasChildNodes.call(this);
36585         }
36586     },
36587
36588     /**
36589      * Trigger a reload for this node
36590      * @param {Function} callback
36591      */
36592     reload : function(callback){
36593         this.collapse(false, false);
36594         while(this.firstChild){
36595             this.removeChild(this.firstChild);
36596         }
36597         this.childrenRendered = false;
36598         this.loaded = false;
36599         if(this.isHiddenRoot()){
36600             this.expanded = false;
36601         }
36602         this.expand(false, false, callback);
36603     }
36604 });/*
36605  * Based on:
36606  * Ext JS Library 1.1.1
36607  * Copyright(c) 2006-2007, Ext JS, LLC.
36608  *
36609  * Originally Released Under LGPL - original licence link has changed is not relivant.
36610  *
36611  * Fork - LGPL
36612  * <script type="text/javascript">
36613  */
36614  
36615 /**
36616  * @class Roo.tree.TreeNodeUI
36617  * @constructor
36618  * @param {Object} node The node to render
36619  * The TreeNode UI implementation is separate from the
36620  * tree implementation. Unless you are customizing the tree UI,
36621  * you should never have to use this directly.
36622  */
36623 Roo.tree.TreeNodeUI = function(node){
36624     this.node = node;
36625     this.rendered = false;
36626     this.animating = false;
36627     this.emptyIcon = Roo.BLANK_IMAGE_URL;
36628 };
36629
36630 Roo.tree.TreeNodeUI.prototype = {
36631     removeChild : function(node){
36632         if(this.rendered){
36633             this.ctNode.removeChild(node.ui.getEl());
36634         }
36635     },
36636
36637     beforeLoad : function(){
36638          this.addClass("x-tree-node-loading");
36639     },
36640
36641     afterLoad : function(){
36642          this.removeClass("x-tree-node-loading");
36643     },
36644
36645     onTextChange : function(node, text, oldText){
36646         if(this.rendered){
36647             this.textNode.innerHTML = text;
36648         }
36649     },
36650
36651     onDisableChange : function(node, state){
36652         this.disabled = state;
36653         if(state){
36654             this.addClass("x-tree-node-disabled");
36655         }else{
36656             this.removeClass("x-tree-node-disabled");
36657         }
36658     },
36659
36660     onSelectedChange : function(state){
36661         if(state){
36662             this.focus();
36663             this.addClass("x-tree-selected");
36664         }else{
36665             //this.blur();
36666             this.removeClass("x-tree-selected");
36667         }
36668     },
36669
36670     onMove : function(tree, node, oldParent, newParent, index, refNode){
36671         this.childIndent = null;
36672         if(this.rendered){
36673             var targetNode = newParent.ui.getContainer();
36674             if(!targetNode){//target not rendered
36675                 this.holder = document.createElement("div");
36676                 this.holder.appendChild(this.wrap);
36677                 return;
36678             }
36679             var insertBefore = refNode ? refNode.ui.getEl() : null;
36680             if(insertBefore){
36681                 targetNode.insertBefore(this.wrap, insertBefore);
36682             }else{
36683                 targetNode.appendChild(this.wrap);
36684             }
36685             this.node.renderIndent(true);
36686         }
36687     },
36688
36689     addClass : function(cls){
36690         if(this.elNode){
36691             Roo.fly(this.elNode).addClass(cls);
36692         }
36693     },
36694
36695     removeClass : function(cls){
36696         if(this.elNode){
36697             Roo.fly(this.elNode).removeClass(cls);
36698         }
36699     },
36700
36701     remove : function(){
36702         if(this.rendered){
36703             this.holder = document.createElement("div");
36704             this.holder.appendChild(this.wrap);
36705         }
36706     },
36707
36708     fireEvent : function(){
36709         return this.node.fireEvent.apply(this.node, arguments);
36710     },
36711
36712     initEvents : function(){
36713         this.node.on("move", this.onMove, this);
36714         var E = Roo.EventManager;
36715         var a = this.anchor;
36716
36717         var el = Roo.fly(a, '_treeui');
36718
36719         if(Roo.isOpera){ // opera render bug ignores the CSS
36720             el.setStyle("text-decoration", "none");
36721         }
36722
36723         el.on("click", this.onClick, this);
36724         el.on("dblclick", this.onDblClick, this);
36725
36726         if(this.checkbox){
36727             Roo.EventManager.on(this.checkbox,
36728                     Roo.isIE ? 'click' : 'change', this.onCheckChange, this);
36729         }
36730
36731         el.on("contextmenu", this.onContextMenu, this);
36732
36733         var icon = Roo.fly(this.iconNode);
36734         icon.on("click", this.onClick, this);
36735         icon.on("dblclick", this.onDblClick, this);
36736         icon.on("contextmenu", this.onContextMenu, this);
36737         E.on(this.ecNode, "click", this.ecClick, this, true);
36738
36739         if(this.node.disabled){
36740             this.addClass("x-tree-node-disabled");
36741         }
36742         if(this.node.hidden){
36743             this.addClass("x-tree-node-disabled");
36744         }
36745         var ot = this.node.getOwnerTree();
36746         var dd = ot ? (ot.enableDD || ot.enableDrag || ot.enableDrop) : false;
36747         if(dd && (!this.node.isRoot || ot.rootVisible)){
36748             Roo.dd.Registry.register(this.elNode, {
36749                 node: this.node,
36750                 handles: this.getDDHandles(),
36751                 isHandle: false
36752             });
36753         }
36754     },
36755
36756     getDDHandles : function(){
36757         return [this.iconNode, this.textNode];
36758     },
36759
36760     hide : function(){
36761         if(this.rendered){
36762             this.wrap.style.display = "none";
36763         }
36764     },
36765
36766     show : function(){
36767         if(this.rendered){
36768             this.wrap.style.display = "";
36769         }
36770     },
36771
36772     onContextMenu : function(e){
36773         if (this.node.hasListener("contextmenu") || this.node.getOwnerTree().hasListener("contextmenu")) {
36774             e.preventDefault();
36775             this.focus();
36776             this.fireEvent("contextmenu", this.node, e);
36777         }
36778     },
36779
36780     onClick : function(e){
36781         if(this.dropping){
36782             e.stopEvent();
36783             return;
36784         }
36785         if(this.fireEvent("beforeclick", this.node, e) !== false){
36786             if(!this.disabled && this.node.attributes.href){
36787                 this.fireEvent("click", this.node, e);
36788                 return;
36789             }
36790             e.preventDefault();
36791             if(this.disabled){
36792                 return;
36793             }
36794
36795             if(this.node.attributes.singleClickExpand && !this.animating && this.node.hasChildNodes()){
36796                 this.node.toggle();
36797             }
36798
36799             this.fireEvent("click", this.node, e);
36800         }else{
36801             e.stopEvent();
36802         }
36803     },
36804
36805     onDblClick : function(e){
36806         e.preventDefault();
36807         if(this.disabled){
36808             return;
36809         }
36810         if(this.checkbox){
36811             this.toggleCheck();
36812         }
36813         if(!this.animating && this.node.hasChildNodes()){
36814             this.node.toggle();
36815         }
36816         this.fireEvent("dblclick", this.node, e);
36817     },
36818
36819     onCheckChange : function(){
36820         var checked = this.checkbox.checked;
36821         this.node.attributes.checked = checked;
36822         this.fireEvent('checkchange', this.node, checked);
36823     },
36824
36825     ecClick : function(e){
36826         if(!this.animating && this.node.hasChildNodes()){
36827             this.node.toggle();
36828         }
36829     },
36830
36831     startDrop : function(){
36832         this.dropping = true;
36833     },
36834
36835     // delayed drop so the click event doesn't get fired on a drop
36836     endDrop : function(){
36837        setTimeout(function(){
36838            this.dropping = false;
36839        }.createDelegate(this), 50);
36840     },
36841
36842     expand : function(){
36843         this.updateExpandIcon();
36844         this.ctNode.style.display = "";
36845     },
36846
36847     focus : function(){
36848         if(!this.node.preventHScroll){
36849             try{this.anchor.focus();
36850             }catch(e){}
36851         }else if(!Roo.isIE){
36852             try{
36853                 var noscroll = this.node.getOwnerTree().getTreeEl().dom;
36854                 var l = noscroll.scrollLeft;
36855                 this.anchor.focus();
36856                 noscroll.scrollLeft = l;
36857             }catch(e){}
36858         }
36859     },
36860
36861     toggleCheck : function(value){
36862         var cb = this.checkbox;
36863         if(cb){
36864             cb.checked = (value === undefined ? !cb.checked : value);
36865         }
36866     },
36867
36868     blur : function(){
36869         try{
36870             this.anchor.blur();
36871         }catch(e){}
36872     },
36873
36874     animExpand : function(callback){
36875         var ct = Roo.get(this.ctNode);
36876         ct.stopFx();
36877         if(!this.node.hasChildNodes()){
36878             this.updateExpandIcon();
36879             this.ctNode.style.display = "";
36880             Roo.callback(callback);
36881             return;
36882         }
36883         this.animating = true;
36884         this.updateExpandIcon();
36885
36886         ct.slideIn('t', {
36887            callback : function(){
36888                this.animating = false;
36889                Roo.callback(callback);
36890             },
36891             scope: this,
36892             duration: this.node.ownerTree.duration || .25
36893         });
36894     },
36895
36896     highlight : function(){
36897         var tree = this.node.getOwnerTree();
36898         Roo.fly(this.wrap).highlight(
36899             tree.hlColor || "C3DAF9",
36900             {endColor: tree.hlBaseColor}
36901         );
36902     },
36903
36904     collapse : function(){
36905         this.updateExpandIcon();
36906         this.ctNode.style.display = "none";
36907     },
36908
36909     animCollapse : function(callback){
36910         var ct = Roo.get(this.ctNode);
36911         ct.enableDisplayMode('block');
36912         ct.stopFx();
36913
36914         this.animating = true;
36915         this.updateExpandIcon();
36916
36917         ct.slideOut('t', {
36918             callback : function(){
36919                this.animating = false;
36920                Roo.callback(callback);
36921             },
36922             scope: this,
36923             duration: this.node.ownerTree.duration || .25
36924         });
36925     },
36926
36927     getContainer : function(){
36928         return this.ctNode;
36929     },
36930
36931     getEl : function(){
36932         return this.wrap;
36933     },
36934
36935     appendDDGhost : function(ghostNode){
36936         ghostNode.appendChild(this.elNode.cloneNode(true));
36937     },
36938
36939     getDDRepairXY : function(){
36940         return Roo.lib.Dom.getXY(this.iconNode);
36941     },
36942
36943     onRender : function(){
36944         this.render();
36945     },
36946
36947     render : function(bulkRender){
36948         var n = this.node, a = n.attributes;
36949         var targetNode = n.parentNode ?
36950               n.parentNode.ui.getContainer() : n.ownerTree.innerCt.dom;
36951
36952         if(!this.rendered){
36953             this.rendered = true;
36954
36955             this.renderElements(n, a, targetNode, bulkRender);
36956
36957             if(a.qtip){
36958                if(this.textNode.setAttributeNS){
36959                    this.textNode.setAttributeNS("ext", "qtip", a.qtip);
36960                    if(a.qtipTitle){
36961                        this.textNode.setAttributeNS("ext", "qtitle", a.qtipTitle);
36962                    }
36963                }else{
36964                    this.textNode.setAttribute("ext:qtip", a.qtip);
36965                    if(a.qtipTitle){
36966                        this.textNode.setAttribute("ext:qtitle", a.qtipTitle);
36967                    }
36968                }
36969             }else if(a.qtipCfg){
36970                 a.qtipCfg.target = Roo.id(this.textNode);
36971                 Roo.QuickTips.register(a.qtipCfg);
36972             }
36973             this.initEvents();
36974             if(!this.node.expanded){
36975                 this.updateExpandIcon();
36976             }
36977         }else{
36978             if(bulkRender === true) {
36979                 targetNode.appendChild(this.wrap);
36980             }
36981         }
36982     },
36983
36984     renderElements : function(n, a, targetNode, bulkRender)
36985     {
36986         // add some indent caching, this helps performance when rendering a large tree
36987         this.indentMarkup = n.parentNode ? n.parentNode.ui.getChildIndent() : '';
36988         var t = n.getOwnerTree();
36989         var txt = t && t.renderer ? t.renderer(n.attributes) : Roo.util.Format.htmlEncode(n.text);
36990         if (typeof(n.attributes.html) != 'undefined') {
36991             txt = n.attributes.html;
36992         }
36993         var tip = t && t.rendererTip ? t.rendererTip(n.attributes) : txt;
36994         var cb = typeof a.checked == 'boolean';
36995         var href = a.href ? a.href : Roo.isGecko ? "" : "#";
36996         var buf = ['<li class="x-tree-node"><div class="x-tree-node-el ', a.cls,'">',
36997             '<span class="x-tree-node-indent">',this.indentMarkup,"</span>",
36998             '<img src="', this.emptyIcon, '" class="x-tree-ec-icon" />',
36999             '<img src="', a.icon || this.emptyIcon, '" class="x-tree-node-icon',(a.icon ? " x-tree-node-inline-icon" : ""),(a.iconCls ? " "+a.iconCls : ""),'" unselectable="on" />',
37000             cb ? ('<input class="x-tree-node-cb" type="checkbox" ' + (a.checked ? 'checked="checked" />' : ' />')) : '',
37001             '<a hidefocus="on" href="',href,'" tabIndex="1" ',
37002              a.hrefTarget ? ' target="'+a.hrefTarget+'"' : "", 
37003                 '><span unselectable="on" qtip="' , tip ,'">',txt,"</span></a></div>",
37004             '<ul class="x-tree-node-ct" style="display:none;"></ul>',
37005             "</li>"];
37006
37007         if(bulkRender !== true && n.nextSibling && n.nextSibling.ui.getEl()){
37008             this.wrap = Roo.DomHelper.insertHtml("beforeBegin",
37009                                 n.nextSibling.ui.getEl(), buf.join(""));
37010         }else{
37011             this.wrap = Roo.DomHelper.insertHtml("beforeEnd", targetNode, buf.join(""));
37012         }
37013
37014         this.elNode = this.wrap.childNodes[0];
37015         this.ctNode = this.wrap.childNodes[1];
37016         var cs = this.elNode.childNodes;
37017         this.indentNode = cs[0];
37018         this.ecNode = cs[1];
37019         this.iconNode = cs[2];
37020         var index = 3;
37021         if(cb){
37022             this.checkbox = cs[3];
37023             index++;
37024         }
37025         this.anchor = cs[index];
37026         this.textNode = cs[index].firstChild;
37027     },
37028
37029     getAnchor : function(){
37030         return this.anchor;
37031     },
37032
37033     getTextEl : function(){
37034         return this.textNode;
37035     },
37036
37037     getIconEl : function(){
37038         return this.iconNode;
37039     },
37040
37041     isChecked : function(){
37042         return this.checkbox ? this.checkbox.checked : false;
37043     },
37044
37045     updateExpandIcon : function(){
37046         if(this.rendered){
37047             var n = this.node, c1, c2;
37048             var cls = n.isLast() ? "x-tree-elbow-end" : "x-tree-elbow";
37049             var hasChild = n.hasChildNodes();
37050             if(hasChild){
37051                 if(n.expanded){
37052                     cls += "-minus";
37053                     c1 = "x-tree-node-collapsed";
37054                     c2 = "x-tree-node-expanded";
37055                 }else{
37056                     cls += "-plus";
37057                     c1 = "x-tree-node-expanded";
37058                     c2 = "x-tree-node-collapsed";
37059                 }
37060                 if(this.wasLeaf){
37061                     this.removeClass("x-tree-node-leaf");
37062                     this.wasLeaf = false;
37063                 }
37064                 if(this.c1 != c1 || this.c2 != c2){
37065                     Roo.fly(this.elNode).replaceClass(c1, c2);
37066                     this.c1 = c1; this.c2 = c2;
37067                 }
37068             }else{
37069                 // this changes non-leafs into leafs if they have no children.
37070                 // it's not very rational behaviour..
37071                 
37072                 if(!this.wasLeaf && this.node.leaf){
37073                     Roo.fly(this.elNode).replaceClass("x-tree-node-expanded", "x-tree-node-leaf");
37074                     delete this.c1;
37075                     delete this.c2;
37076                     this.wasLeaf = true;
37077                 }
37078             }
37079             var ecc = "x-tree-ec-icon "+cls;
37080             if(this.ecc != ecc){
37081                 this.ecNode.className = ecc;
37082                 this.ecc = ecc;
37083             }
37084         }
37085     },
37086
37087     getChildIndent : function(){
37088         if(!this.childIndent){
37089             var buf = [];
37090             var p = this.node;
37091             while(p){
37092                 if(!p.isRoot || (p.isRoot && p.ownerTree.rootVisible)){
37093                     if(!p.isLast()) {
37094                         buf.unshift('<img src="'+this.emptyIcon+'" class="x-tree-elbow-line" />');
37095                     } else {
37096                         buf.unshift('<img src="'+this.emptyIcon+'" class="x-tree-icon" />');
37097                     }
37098                 }
37099                 p = p.parentNode;
37100             }
37101             this.childIndent = buf.join("");
37102         }
37103         return this.childIndent;
37104     },
37105
37106     renderIndent : function(){
37107         if(this.rendered){
37108             var indent = "";
37109             var p = this.node.parentNode;
37110             if(p){
37111                 indent = p.ui.getChildIndent();
37112             }
37113             if(this.indentMarkup != indent){ // don't rerender if not required
37114                 this.indentNode.innerHTML = indent;
37115                 this.indentMarkup = indent;
37116             }
37117             this.updateExpandIcon();
37118         }
37119     }
37120 };
37121
37122 Roo.tree.RootTreeNodeUI = function(){
37123     Roo.tree.RootTreeNodeUI.superclass.constructor.apply(this, arguments);
37124 };
37125 Roo.extend(Roo.tree.RootTreeNodeUI, Roo.tree.TreeNodeUI, {
37126     render : function(){
37127         if(!this.rendered){
37128             var targetNode = this.node.ownerTree.innerCt.dom;
37129             this.node.expanded = true;
37130             targetNode.innerHTML = '<div class="x-tree-root-node"></div>';
37131             this.wrap = this.ctNode = targetNode.firstChild;
37132         }
37133     },
37134     collapse : function(){
37135     },
37136     expand : function(){
37137     }
37138 });/*
37139  * Based on:
37140  * Ext JS Library 1.1.1
37141  * Copyright(c) 2006-2007, Ext JS, LLC.
37142  *
37143  * Originally Released Under LGPL - original licence link has changed is not relivant.
37144  *
37145  * Fork - LGPL
37146  * <script type="text/javascript">
37147  */
37148 /**
37149  * @class Roo.tree.TreeLoader
37150  * @extends Roo.util.Observable
37151  * A TreeLoader provides for lazy loading of an {@link Roo.tree.TreeNode}'s child
37152  * nodes from a specified URL. The response must be a javascript Array definition
37153  * who's elements are node definition objects. eg:
37154  * <pre><code>
37155 {  success : true,
37156    data :      [
37157    
37158     { 'id': 1, 'text': 'A folder Node', 'leaf': false },
37159     { 'id': 2, 'text': 'A leaf Node', 'leaf': true }
37160     ]
37161 }
37162
37163
37164 </code></pre>
37165  * <br><br>
37166  * The old style respose with just an array is still supported, but not recommended.
37167  * <br><br>
37168  *
37169  * A server request is sent, and child nodes are loaded only when a node is expanded.
37170  * The loading node's id is passed to the server under the parameter name "node" to
37171  * enable the server to produce the correct child nodes.
37172  * <br><br>
37173  * To pass extra parameters, an event handler may be attached to the "beforeload"
37174  * event, and the parameters specified in the TreeLoader's baseParams property:
37175  * <pre><code>
37176     myTreeLoader.on("beforeload", function(treeLoader, node) {
37177         this.baseParams.category = node.attributes.category;
37178     }, this);
37179     
37180 </code></pre>
37181  *
37182  * This would pass an HTTP parameter called "category" to the server containing
37183  * the value of the Node's "category" attribute.
37184  * @constructor
37185  * Creates a new Treeloader.
37186  * @param {Object} config A config object containing config properties.
37187  */
37188 Roo.tree.TreeLoader = function(config){
37189     this.baseParams = {};
37190     this.requestMethod = "POST";
37191     Roo.apply(this, config);
37192
37193     this.addEvents({
37194     
37195         /**
37196          * @event beforeload
37197          * Fires before a network request is made to retrieve the Json text which specifies a node's children.
37198          * @param {Object} This TreeLoader object.
37199          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
37200          * @param {Object} callback The callback function specified in the {@link #load} call.
37201          */
37202         beforeload : true,
37203         /**
37204          * @event load
37205          * Fires when the node has been successfuly loaded.
37206          * @param {Object} This TreeLoader object.
37207          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
37208          * @param {Object} response The response object containing the data from the server.
37209          */
37210         load : true,
37211         /**
37212          * @event loadexception
37213          * Fires if the network request failed.
37214          * @param {Object} This TreeLoader object.
37215          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
37216          * @param {Object} response The response object containing the data from the server.
37217          */
37218         loadexception : true,
37219         /**
37220          * @event create
37221          * Fires before a node is created, enabling you to return custom Node types 
37222          * @param {Object} This TreeLoader object.
37223          * @param {Object} attr - the data returned from the AJAX call (modify it to suit)
37224          */
37225         create : true
37226     });
37227
37228     Roo.tree.TreeLoader.superclass.constructor.call(this);
37229 };
37230
37231 Roo.extend(Roo.tree.TreeLoader, Roo.util.Observable, {
37232     /**
37233     * @cfg {String} dataUrl The URL from which to request a Json string which
37234     * specifies an array of node definition object representing the child nodes
37235     * to be loaded.
37236     */
37237     /**
37238     * @cfg {String} requestMethod either GET or POST
37239     * defaults to POST (due to BC)
37240     * to be loaded.
37241     */
37242     /**
37243     * @cfg {Object} baseParams (optional) An object containing properties which
37244     * specify HTTP parameters to be passed to each request for child nodes.
37245     */
37246     /**
37247     * @cfg {Object} baseAttrs (optional) An object containing attributes to be added to all nodes
37248     * created by this loader. If the attributes sent by the server have an attribute in this object,
37249     * they take priority.
37250     */
37251     /**
37252     * @cfg {Object} uiProviders (optional) An object containing properties which
37253     * 
37254     * DEPRECATED - use 'create' event handler to modify attributes - which affect creation.
37255     * specify custom {@link Roo.tree.TreeNodeUI} implementations. If the optional
37256     * <i>uiProvider</i> attribute of a returned child node is a string rather
37257     * than a reference to a TreeNodeUI implementation, this that string value
37258     * is used as a property name in the uiProviders object. You can define the provider named
37259     * 'default' , and this will be used for all nodes (if no uiProvider is delivered by the node data)
37260     */
37261     uiProviders : {},
37262
37263     /**
37264     * @cfg {Boolean} clearOnLoad (optional) Default to true. Remove previously existing
37265     * child nodes before loading.
37266     */
37267     clearOnLoad : true,
37268
37269     /**
37270     * @cfg {String} root (optional) Default to false. Use this to read data from an object 
37271     * property on loading, rather than expecting an array. (eg. more compatible to a standard
37272     * Grid query { data : [ .....] }
37273     */
37274     
37275     root : false,
37276      /**
37277     * @cfg {String} queryParam (optional) 
37278     * Name of the query as it will be passed on the querystring (defaults to 'node')
37279     * eg. the request will be ?node=[id]
37280     */
37281     
37282     
37283     queryParam: false,
37284     
37285     /**
37286      * Load an {@link Roo.tree.TreeNode} from the URL specified in the constructor.
37287      * This is called automatically when a node is expanded, but may be used to reload
37288      * a node (or append new children if the {@link #clearOnLoad} option is false.)
37289      * @param {Roo.tree.TreeNode} node
37290      * @param {Function} callback
37291      */
37292     load : function(node, callback){
37293         if(this.clearOnLoad){
37294             while(node.firstChild){
37295                 node.removeChild(node.firstChild);
37296             }
37297         }
37298         if(node.attributes.children){ // preloaded json children
37299             var cs = node.attributes.children;
37300             for(var i = 0, len = cs.length; i < len; i++){
37301                 node.appendChild(this.createNode(cs[i]));
37302             }
37303             if(typeof callback == "function"){
37304                 callback();
37305             }
37306         }else if(this.dataUrl){
37307             this.requestData(node, callback);
37308         }
37309     },
37310
37311     getParams: function(node){
37312         var buf = [], bp = this.baseParams;
37313         for(var key in bp){
37314             if(typeof bp[key] != "function"){
37315                 buf.push(encodeURIComponent(key), "=", encodeURIComponent(bp[key]), "&");
37316             }
37317         }
37318         var n = this.queryParam === false ? 'node' : this.queryParam;
37319         buf.push(n + "=", encodeURIComponent(node.id));
37320         return buf.join("");
37321     },
37322
37323     requestData : function(node, callback){
37324         if(this.fireEvent("beforeload", this, node, callback) !== false){
37325             this.transId = Roo.Ajax.request({
37326                 method:this.requestMethod,
37327                 url: this.dataUrl||this.url,
37328                 success: this.handleResponse,
37329                 failure: this.handleFailure,
37330                 scope: this,
37331                 argument: {callback: callback, node: node},
37332                 params: this.getParams(node)
37333             });
37334         }else{
37335             // if the load is cancelled, make sure we notify
37336             // the node that we are done
37337             if(typeof callback == "function"){
37338                 callback();
37339             }
37340         }
37341     },
37342
37343     isLoading : function(){
37344         return this.transId ? true : false;
37345     },
37346
37347     abort : function(){
37348         if(this.isLoading()){
37349             Roo.Ajax.abort(this.transId);
37350         }
37351     },
37352
37353     // private
37354     createNode : function(attr)
37355     {
37356         // apply baseAttrs, nice idea Corey!
37357         if(this.baseAttrs){
37358             Roo.applyIf(attr, this.baseAttrs);
37359         }
37360         if(this.applyLoader !== false){
37361             attr.loader = this;
37362         }
37363         // uiProvider = depreciated..
37364         
37365         if(typeof(attr.uiProvider) == 'string'){
37366            attr.uiProvider = this.uiProviders[attr.uiProvider] || 
37367                 /**  eval:var:attr */ eval(attr.uiProvider);
37368         }
37369         if(typeof(this.uiProviders['default']) != 'undefined') {
37370             attr.uiProvider = this.uiProviders['default'];
37371         }
37372         
37373         this.fireEvent('create', this, attr);
37374         
37375         attr.leaf  = typeof(attr.leaf) == 'string' ? attr.leaf * 1 : attr.leaf;
37376         return(attr.leaf ?
37377                         new Roo.tree.TreeNode(attr) :
37378                         new Roo.tree.AsyncTreeNode(attr));
37379     },
37380
37381     processResponse : function(response, node, callback)
37382     {
37383         var json = response.responseText;
37384         try {
37385             
37386             var o = Roo.decode(json);
37387             
37388             if (this.root === false && typeof(o.success) != undefined) {
37389                 this.root = 'data'; // the default behaviour for list like data..
37390                 }
37391                 
37392             if (this.root !== false &&  !o.success) {
37393                 // it's a failure condition.
37394                 var a = response.argument;
37395                 this.fireEvent("loadexception", this, a.node, response);
37396                 Roo.log("Load failed - should have a handler really");
37397                 return;
37398             }
37399             
37400             
37401             
37402             if (this.root !== false) {
37403                  o = o[this.root];
37404             }
37405             
37406             for(var i = 0, len = o.length; i < len; i++){
37407                 var n = this.createNode(o[i]);
37408                 if(n){
37409                     node.appendChild(n);
37410                 }
37411             }
37412             if(typeof callback == "function"){
37413                 callback(this, node);
37414             }
37415         }catch(e){
37416             this.handleFailure(response);
37417         }
37418     },
37419
37420     handleResponse : function(response){
37421         this.transId = false;
37422         var a = response.argument;
37423         this.processResponse(response, a.node, a.callback);
37424         this.fireEvent("load", this, a.node, response);
37425     },
37426
37427     handleFailure : function(response)
37428     {
37429         // should handle failure better..
37430         this.transId = false;
37431         var a = response.argument;
37432         this.fireEvent("loadexception", this, a.node, response);
37433         if(typeof a.callback == "function"){
37434             a.callback(this, a.node);
37435         }
37436     }
37437 });/*
37438  * Based on:
37439  * Ext JS Library 1.1.1
37440  * Copyright(c) 2006-2007, Ext JS, LLC.
37441  *
37442  * Originally Released Under LGPL - original licence link has changed is not relivant.
37443  *
37444  * Fork - LGPL
37445  * <script type="text/javascript">
37446  */
37447
37448 /**
37449 * @class Roo.tree.TreeFilter
37450 * Note this class is experimental and doesn't update the indent (lines) or expand collapse icons of the nodes
37451 * @param {TreePanel} tree
37452 * @param {Object} config (optional)
37453  */
37454 Roo.tree.TreeFilter = function(tree, config){
37455     this.tree = tree;
37456     this.filtered = {};
37457     Roo.apply(this, config);
37458 };
37459
37460 Roo.tree.TreeFilter.prototype = {
37461     clearBlank:false,
37462     reverse:false,
37463     autoClear:false,
37464     remove:false,
37465
37466      /**
37467      * Filter the data by a specific attribute.
37468      * @param {String/RegExp} value Either string that the attribute value
37469      * should start with or a RegExp to test against the attribute
37470      * @param {String} attr (optional) The attribute passed in your node's attributes collection. Defaults to "text".
37471      * @param {TreeNode} startNode (optional) The node to start the filter at.
37472      */
37473     filter : function(value, attr, startNode){
37474         attr = attr || "text";
37475         var f;
37476         if(typeof value == "string"){
37477             var vlen = value.length;
37478             // auto clear empty filter
37479             if(vlen == 0 && this.clearBlank){
37480                 this.clear();
37481                 return;
37482             }
37483             value = value.toLowerCase();
37484             f = function(n){
37485                 return n.attributes[attr].substr(0, vlen).toLowerCase() == value;
37486             };
37487         }else if(value.exec){ // regex?
37488             f = function(n){
37489                 return value.test(n.attributes[attr]);
37490             };
37491         }else{
37492             throw 'Illegal filter type, must be string or regex';
37493         }
37494         this.filterBy(f, null, startNode);
37495         },
37496
37497     /**
37498      * Filter by a function. The passed function will be called with each
37499      * node in the tree (or from the startNode). If the function returns true, the node is kept
37500      * otherwise it is filtered. If a node is filtered, its children are also filtered.
37501      * @param {Function} fn The filter function
37502      * @param {Object} scope (optional) The scope of the function (defaults to the current node)
37503      */
37504     filterBy : function(fn, scope, startNode){
37505         startNode = startNode || this.tree.root;
37506         if(this.autoClear){
37507             this.clear();
37508         }
37509         var af = this.filtered, rv = this.reverse;
37510         var f = function(n){
37511             if(n == startNode){
37512                 return true;
37513             }
37514             if(af[n.id]){
37515                 return false;
37516             }
37517             var m = fn.call(scope || n, n);
37518             if(!m || rv){
37519                 af[n.id] = n;
37520                 n.ui.hide();
37521                 return false;
37522             }
37523             return true;
37524         };
37525         startNode.cascade(f);
37526         if(this.remove){
37527            for(var id in af){
37528                if(typeof id != "function"){
37529                    var n = af[id];
37530                    if(n && n.parentNode){
37531                        n.parentNode.removeChild(n);
37532                    }
37533                }
37534            }
37535         }
37536     },
37537
37538     /**
37539      * Clears the current filter. Note: with the "remove" option
37540      * set a filter cannot be cleared.
37541      */
37542     clear : function(){
37543         var t = this.tree;
37544         var af = this.filtered;
37545         for(var id in af){
37546             if(typeof id != "function"){
37547                 var n = af[id];
37548                 if(n){
37549                     n.ui.show();
37550                 }
37551             }
37552         }
37553         this.filtered = {};
37554     }
37555 };
37556 /*
37557  * Based on:
37558  * Ext JS Library 1.1.1
37559  * Copyright(c) 2006-2007, Ext JS, LLC.
37560  *
37561  * Originally Released Under LGPL - original licence link has changed is not relivant.
37562  *
37563  * Fork - LGPL
37564  * <script type="text/javascript">
37565  */
37566  
37567
37568 /**
37569  * @class Roo.tree.TreeSorter
37570  * Provides sorting of nodes in a TreePanel
37571  * 
37572  * @cfg {Boolean} folderSort True to sort leaf nodes under non leaf nodes
37573  * @cfg {String} property The named attribute on the node to sort by (defaults to text)
37574  * @cfg {String} dir The direction to sort (asc or desc) (defaults to asc)
37575  * @cfg {String} leafAttr The attribute used to determine leaf nodes in folder sort (defaults to "leaf")
37576  * @cfg {Boolean} caseSensitive true for case sensitive sort (defaults to false)
37577  * @cfg {Function} sortType A custom "casting" function used to convert node values before sorting
37578  * @constructor
37579  * @param {TreePanel} tree
37580  * @param {Object} config
37581  */
37582 Roo.tree.TreeSorter = function(tree, config){
37583     Roo.apply(this, config);
37584     tree.on("beforechildrenrendered", this.doSort, this);
37585     tree.on("append", this.updateSort, this);
37586     tree.on("insert", this.updateSort, this);
37587     
37588     var dsc = this.dir && this.dir.toLowerCase() == "desc";
37589     var p = this.property || "text";
37590     var sortType = this.sortType;
37591     var fs = this.folderSort;
37592     var cs = this.caseSensitive === true;
37593     var leafAttr = this.leafAttr || 'leaf';
37594
37595     this.sortFn = function(n1, n2){
37596         if(fs){
37597             if(n1.attributes[leafAttr] && !n2.attributes[leafAttr]){
37598                 return 1;
37599             }
37600             if(!n1.attributes[leafAttr] && n2.attributes[leafAttr]){
37601                 return -1;
37602             }
37603         }
37604         var v1 = sortType ? sortType(n1) : (cs ? n1.attributes[p] : n1.attributes[p].toUpperCase());
37605         var v2 = sortType ? sortType(n2) : (cs ? n2.attributes[p] : n2.attributes[p].toUpperCase());
37606         if(v1 < v2){
37607                         return dsc ? +1 : -1;
37608                 }else if(v1 > v2){
37609                         return dsc ? -1 : +1;
37610         }else{
37611                 return 0;
37612         }
37613     };
37614 };
37615
37616 Roo.tree.TreeSorter.prototype = {
37617     doSort : function(node){
37618         node.sort(this.sortFn);
37619     },
37620     
37621     compareNodes : function(n1, n2){
37622         return (n1.text.toUpperCase() > n2.text.toUpperCase() ? 1 : -1);
37623     },
37624     
37625     updateSort : function(tree, node){
37626         if(node.childrenRendered){
37627             this.doSort.defer(1, this, [node]);
37628         }
37629     }
37630 };/*
37631  * Based on:
37632  * Ext JS Library 1.1.1
37633  * Copyright(c) 2006-2007, Ext JS, LLC.
37634  *
37635  * Originally Released Under LGPL - original licence link has changed is not relivant.
37636  *
37637  * Fork - LGPL
37638  * <script type="text/javascript">
37639  */
37640
37641 if(Roo.dd.DropZone){
37642     
37643 Roo.tree.TreeDropZone = function(tree, config){
37644     this.allowParentInsert = false;
37645     this.allowContainerDrop = false;
37646     this.appendOnly = false;
37647     Roo.tree.TreeDropZone.superclass.constructor.call(this, tree.innerCt, config);
37648     this.tree = tree;
37649     this.lastInsertClass = "x-tree-no-status";
37650     this.dragOverData = {};
37651 };
37652
37653 Roo.extend(Roo.tree.TreeDropZone, Roo.dd.DropZone, {
37654     ddGroup : "TreeDD",
37655     scroll:  true,
37656     
37657     expandDelay : 1000,
37658     
37659     expandNode : function(node){
37660         if(node.hasChildNodes() && !node.isExpanded()){
37661             node.expand(false, null, this.triggerCacheRefresh.createDelegate(this));
37662         }
37663     },
37664     
37665     queueExpand : function(node){
37666         this.expandProcId = this.expandNode.defer(this.expandDelay, this, [node]);
37667     },
37668     
37669     cancelExpand : function(){
37670         if(this.expandProcId){
37671             clearTimeout(this.expandProcId);
37672             this.expandProcId = false;
37673         }
37674     },
37675     
37676     isValidDropPoint : function(n, pt, dd, e, data){
37677         if(!n || !data){ return false; }
37678         var targetNode = n.node;
37679         var dropNode = data.node;
37680         // default drop rules
37681         if(!(targetNode && targetNode.isTarget && pt)){
37682             return false;
37683         }
37684         if(pt == "append" && targetNode.allowChildren === false){
37685             return false;
37686         }
37687         if((pt == "above" || pt == "below") && (targetNode.parentNode && targetNode.parentNode.allowChildren === false)){
37688             return false;
37689         }
37690         if(dropNode && (targetNode == dropNode || dropNode.contains(targetNode))){
37691             return false;
37692         }
37693         // reuse the object
37694         var overEvent = this.dragOverData;
37695         overEvent.tree = this.tree;
37696         overEvent.target = targetNode;
37697         overEvent.data = data;
37698         overEvent.point = pt;
37699         overEvent.source = dd;
37700         overEvent.rawEvent = e;
37701         overEvent.dropNode = dropNode;
37702         overEvent.cancel = false;  
37703         var result = this.tree.fireEvent("nodedragover", overEvent);
37704         return overEvent.cancel === false && result !== false;
37705     },
37706     
37707     getDropPoint : function(e, n, dd)
37708     {
37709         var tn = n.node;
37710         if(tn.isRoot){
37711             return tn.allowChildren !== false ? "append" : false; // always append for root
37712         }
37713         var dragEl = n.ddel;
37714         var t = Roo.lib.Dom.getY(dragEl), b = t + dragEl.offsetHeight;
37715         var y = Roo.lib.Event.getPageY(e);
37716         //var noAppend = tn.allowChildren === false || tn.isLeaf();
37717         
37718         // we may drop nodes anywhere, as long as allowChildren has not been set to false..
37719         var noAppend = tn.allowChildren === false;
37720         if(this.appendOnly || tn.parentNode.allowChildren === false){
37721             return noAppend ? false : "append";
37722         }
37723         var noBelow = false;
37724         if(!this.allowParentInsert){
37725             noBelow = tn.hasChildNodes() && tn.isExpanded();
37726         }
37727         var q = (b - t) / (noAppend ? 2 : 3);
37728         if(y >= t && y < (t + q)){
37729             return "above";
37730         }else if(!noBelow && (noAppend || y >= b-q && y <= b)){
37731             return "below";
37732         }else{
37733             return "append";
37734         }
37735     },
37736     
37737     onNodeEnter : function(n, dd, e, data)
37738     {
37739         this.cancelExpand();
37740     },
37741     
37742     onNodeOver : function(n, dd, e, data)
37743     {
37744        
37745         var pt = this.getDropPoint(e, n, dd);
37746         var node = n.node;
37747         
37748         // auto node expand check
37749         if(!this.expandProcId && pt == "append" && node.hasChildNodes() && !n.node.isExpanded()){
37750             this.queueExpand(node);
37751         }else if(pt != "append"){
37752             this.cancelExpand();
37753         }
37754         
37755         // set the insert point style on the target node
37756         var returnCls = this.dropNotAllowed;
37757         if(this.isValidDropPoint(n, pt, dd, e, data)){
37758            if(pt){
37759                var el = n.ddel;
37760                var cls;
37761                if(pt == "above"){
37762                    returnCls = n.node.isFirst() ? "x-tree-drop-ok-above" : "x-tree-drop-ok-between";
37763                    cls = "x-tree-drag-insert-above";
37764                }else if(pt == "below"){
37765                    returnCls = n.node.isLast() ? "x-tree-drop-ok-below" : "x-tree-drop-ok-between";
37766                    cls = "x-tree-drag-insert-below";
37767                }else{
37768                    returnCls = "x-tree-drop-ok-append";
37769                    cls = "x-tree-drag-append";
37770                }
37771                if(this.lastInsertClass != cls){
37772                    Roo.fly(el).replaceClass(this.lastInsertClass, cls);
37773                    this.lastInsertClass = cls;
37774                }
37775            }
37776        }
37777        return returnCls;
37778     },
37779     
37780     onNodeOut : function(n, dd, e, data){
37781         
37782         this.cancelExpand();
37783         this.removeDropIndicators(n);
37784     },
37785     
37786     onNodeDrop : function(n, dd, e, data){
37787         var point = this.getDropPoint(e, n, dd);
37788         var targetNode = n.node;
37789         targetNode.ui.startDrop();
37790         if(!this.isValidDropPoint(n, point, dd, e, data)){
37791             targetNode.ui.endDrop();
37792             return false;
37793         }
37794         // first try to find the drop node
37795         var dropNode = data.node || (dd.getTreeNode ? dd.getTreeNode(data, targetNode, point, e) : null);
37796         var dropEvent = {
37797             tree : this.tree,
37798             target: targetNode,
37799             data: data,
37800             point: point,
37801             source: dd,
37802             rawEvent: e,
37803             dropNode: dropNode,
37804             cancel: !dropNode   
37805         };
37806         var retval = this.tree.fireEvent("beforenodedrop", dropEvent);
37807         if(retval === false || dropEvent.cancel === true || !dropEvent.dropNode){
37808             targetNode.ui.endDrop();
37809             return false;
37810         }
37811         // allow target changing
37812         targetNode = dropEvent.target;
37813         if(point == "append" && !targetNode.isExpanded()){
37814             targetNode.expand(false, null, function(){
37815                 this.completeDrop(dropEvent);
37816             }.createDelegate(this));
37817         }else{
37818             this.completeDrop(dropEvent);
37819         }
37820         return true;
37821     },
37822     
37823     completeDrop : function(de){
37824         var ns = de.dropNode, p = de.point, t = de.target;
37825         if(!(ns instanceof Array)){
37826             ns = [ns];
37827         }
37828         var n;
37829         for(var i = 0, len = ns.length; i < len; i++){
37830             n = ns[i];
37831             if(p == "above"){
37832                 t.parentNode.insertBefore(n, t);
37833             }else if(p == "below"){
37834                 t.parentNode.insertBefore(n, t.nextSibling);
37835             }else{
37836                 t.appendChild(n);
37837             }
37838         }
37839         n.ui.focus();
37840         if(this.tree.hlDrop){
37841             n.ui.highlight();
37842         }
37843         t.ui.endDrop();
37844         this.tree.fireEvent("nodedrop", de);
37845     },
37846     
37847     afterNodeMoved : function(dd, data, e, targetNode, dropNode){
37848         if(this.tree.hlDrop){
37849             dropNode.ui.focus();
37850             dropNode.ui.highlight();
37851         }
37852         this.tree.fireEvent("nodedrop", this.tree, targetNode, data, dd, e);
37853     },
37854     
37855     getTree : function(){
37856         return this.tree;
37857     },
37858     
37859     removeDropIndicators : function(n){
37860         if(n && n.ddel){
37861             var el = n.ddel;
37862             Roo.fly(el).removeClass([
37863                     "x-tree-drag-insert-above",
37864                     "x-tree-drag-insert-below",
37865                     "x-tree-drag-append"]);
37866             this.lastInsertClass = "_noclass";
37867         }
37868     },
37869     
37870     beforeDragDrop : function(target, e, id){
37871         this.cancelExpand();
37872         return true;
37873     },
37874     
37875     afterRepair : function(data){
37876         if(data && Roo.enableFx){
37877             data.node.ui.highlight();
37878         }
37879         this.hideProxy();
37880     } 
37881     
37882 });
37883
37884 }
37885 /*
37886  * Based on:
37887  * Ext JS Library 1.1.1
37888  * Copyright(c) 2006-2007, Ext JS, LLC.
37889  *
37890  * Originally Released Under LGPL - original licence link has changed is not relivant.
37891  *
37892  * Fork - LGPL
37893  * <script type="text/javascript">
37894  */
37895  
37896
37897 if(Roo.dd.DragZone){
37898 Roo.tree.TreeDragZone = function(tree, config){
37899     Roo.tree.TreeDragZone.superclass.constructor.call(this, tree.getTreeEl(), config);
37900     this.tree = tree;
37901 };
37902
37903 Roo.extend(Roo.tree.TreeDragZone, Roo.dd.DragZone, {
37904     ddGroup : "TreeDD",
37905    
37906     onBeforeDrag : function(data, e){
37907         var n = data.node;
37908         return n && n.draggable && !n.disabled;
37909     },
37910      
37911     
37912     onInitDrag : function(e){
37913         var data = this.dragData;
37914         this.tree.getSelectionModel().select(data.node);
37915         this.proxy.update("");
37916         data.node.ui.appendDDGhost(this.proxy.ghost.dom);
37917         this.tree.fireEvent("startdrag", this.tree, data.node, e);
37918     },
37919     
37920     getRepairXY : function(e, data){
37921         return data.node.ui.getDDRepairXY();
37922     },
37923     
37924     onEndDrag : function(data, e){
37925         this.tree.fireEvent("enddrag", this.tree, data.node, e);
37926         
37927         
37928     },
37929     
37930     onValidDrop : function(dd, e, id){
37931         this.tree.fireEvent("dragdrop", this.tree, this.dragData.node, dd, e);
37932         this.hideProxy();
37933     },
37934     
37935     beforeInvalidDrop : function(e, id){
37936         // this scrolls the original position back into view
37937         var sm = this.tree.getSelectionModel();
37938         sm.clearSelections();
37939         sm.select(this.dragData.node);
37940     }
37941 });
37942 }/*
37943  * Based on:
37944  * Ext JS Library 1.1.1
37945  * Copyright(c) 2006-2007, Ext JS, LLC.
37946  *
37947  * Originally Released Under LGPL - original licence link has changed is not relivant.
37948  *
37949  * Fork - LGPL
37950  * <script type="text/javascript">
37951  */
37952 /**
37953  * @class Roo.tree.TreeEditor
37954  * @extends Roo.Editor
37955  * Provides editor functionality for inline tree node editing.  Any valid {@link Roo.form.Field} can be used
37956  * as the editor field.
37957  * @constructor
37958  * @param {Object} config (used to be the tree panel.)
37959  * @param {Object} oldconfig DEPRECIATED Either a prebuilt {@link Roo.form.Field} instance or a Field config object
37960  * 
37961  * @cfg {Roo.tree.TreePanel} tree The tree to bind to.
37962  * @cfg {Roo.form.TextField} field [required] The field configuration
37963  *
37964  * 
37965  */
37966 Roo.tree.TreeEditor = function(config, oldconfig) { // was -- (tree, config){
37967     var tree = config;
37968     var field;
37969     if (oldconfig) { // old style..
37970         field = oldconfig.events ? oldconfig : new Roo.form.TextField(oldconfig);
37971     } else {
37972         // new style..
37973         tree = config.tree;
37974         config.field = config.field  || {};
37975         config.field.xtype = 'TextField';
37976         field = Roo.factory(config.field, Roo.form);
37977     }
37978     config = config || {};
37979     
37980     
37981     this.addEvents({
37982         /**
37983          * @event beforenodeedit
37984          * Fires when editing is initiated, but before the value changes.  Editing can be canceled by returning
37985          * false from the handler of this event.
37986          * @param {Editor} this
37987          * @param {Roo.tree.Node} node 
37988          */
37989         "beforenodeedit" : true
37990     });
37991     
37992     //Roo.log(config);
37993     Roo.tree.TreeEditor.superclass.constructor.call(this, field, config);
37994
37995     this.tree = tree;
37996
37997     tree.on('beforeclick', this.beforeNodeClick, this);
37998     tree.getTreeEl().on('mousedown', this.hide, this);
37999     this.on('complete', this.updateNode, this);
38000     this.on('beforestartedit', this.fitToTree, this);
38001     this.on('startedit', this.bindScroll, this, {delay:10});
38002     this.on('specialkey', this.onSpecialKey, this);
38003 };
38004
38005 Roo.extend(Roo.tree.TreeEditor, Roo.Editor, {
38006     /**
38007      * @cfg {String} alignment
38008      * The position to align to (see {@link Roo.Element#alignTo} for more details, defaults to "l-l").
38009      */
38010     alignment: "l-l",
38011     // inherit
38012     autoSize: false,
38013     /**
38014      * @cfg {Boolean} hideEl
38015      * True to hide the bound element while the editor is displayed (defaults to false)
38016      */
38017     hideEl : false,
38018     /**
38019      * @cfg {String} cls
38020      * CSS class to apply to the editor (defaults to "x-small-editor x-tree-editor")
38021      */
38022     cls: "x-small-editor x-tree-editor",
38023     /**
38024      * @cfg {Boolean} shim
38025      * True to shim the editor if selects/iframes could be displayed beneath it (defaults to false)
38026      */
38027     shim:false,
38028     // inherit
38029     shadow:"frame",
38030     /**
38031      * @cfg {Number} maxWidth
38032      * The maximum width in pixels of the editor field (defaults to 250).  Note that if the maxWidth would exceed
38033      * the containing tree element's size, it will be automatically limited for you to the container width, taking
38034      * scroll and client offsets into account prior to each edit.
38035      */
38036     maxWidth: 250,
38037
38038     editDelay : 350,
38039
38040     // private
38041     fitToTree : function(ed, el){
38042         var td = this.tree.getTreeEl().dom, nd = el.dom;
38043         if(td.scrollLeft >  nd.offsetLeft){ // ensure the node left point is visible
38044             td.scrollLeft = nd.offsetLeft;
38045         }
38046         var w = Math.min(
38047                 this.maxWidth,
38048                 (td.clientWidth > 20 ? td.clientWidth : td.offsetWidth) - Math.max(0, nd.offsetLeft-td.scrollLeft) - /*cushion*/5);
38049         this.setSize(w, '');
38050         
38051         return this.fireEvent('beforenodeedit', this, this.editNode);
38052         
38053     },
38054
38055     // private
38056     triggerEdit : function(node){
38057         this.completeEdit();
38058         this.editNode = node;
38059         this.startEdit(node.ui.textNode, node.text);
38060     },
38061
38062     // private
38063     bindScroll : function(){
38064         this.tree.getTreeEl().on('scroll', this.cancelEdit, this);
38065     },
38066
38067     // private
38068     beforeNodeClick : function(node, e){
38069         var sinceLast = (this.lastClick ? this.lastClick.getElapsed() : 0);
38070         this.lastClick = new Date();
38071         if(sinceLast > this.editDelay && this.tree.getSelectionModel().isSelected(node)){
38072             e.stopEvent();
38073             this.triggerEdit(node);
38074             return false;
38075         }
38076         return true;
38077     },
38078
38079     // private
38080     updateNode : function(ed, value){
38081         this.tree.getTreeEl().un('scroll', this.cancelEdit, this);
38082         this.editNode.setText(value);
38083     },
38084
38085     // private
38086     onHide : function(){
38087         Roo.tree.TreeEditor.superclass.onHide.call(this);
38088         if(this.editNode){
38089             this.editNode.ui.focus();
38090         }
38091     },
38092
38093     // private
38094     onSpecialKey : function(field, e){
38095         var k = e.getKey();
38096         if(k == e.ESC){
38097             e.stopEvent();
38098             this.cancelEdit();
38099         }else if(k == e.ENTER && !e.hasModifier()){
38100             e.stopEvent();
38101             this.completeEdit();
38102         }
38103     }
38104 });//<Script type="text/javascript">
38105 /*
38106  * Based on:
38107  * Ext JS Library 1.1.1
38108  * Copyright(c) 2006-2007, Ext JS, LLC.
38109  *
38110  * Originally Released Under LGPL - original licence link has changed is not relivant.
38111  *
38112  * Fork - LGPL
38113  * <script type="text/javascript">
38114  */
38115  
38116 /**
38117  * Not documented??? - probably should be...
38118  */
38119
38120 Roo.tree.ColumnNodeUI = Roo.extend(Roo.tree.TreeNodeUI, {
38121     //focus: Roo.emptyFn, // prevent odd scrolling behavior
38122     
38123     renderElements : function(n, a, targetNode, bulkRender){
38124         //consel.log("renderElements?");
38125         this.indentMarkup = n.parentNode ? n.parentNode.ui.getChildIndent() : '';
38126
38127         var t = n.getOwnerTree();
38128         var tid = Pman.Tab.Document_TypesTree.tree.el.id;
38129         
38130         var cols = t.columns;
38131         var bw = t.borderWidth;
38132         var c = cols[0];
38133         var href = a.href ? a.href : Roo.isGecko ? "" : "#";
38134          var cb = typeof a.checked == "boolean";
38135         var tx = String.format('{0}',n.text || (c.renderer ? c.renderer(a[c.dataIndex], n, a) : a[c.dataIndex]));
38136         var colcls = 'x-t-' + tid + '-c0';
38137         var buf = [
38138             '<li class="x-tree-node">',
38139             
38140                 
38141                 '<div class="x-tree-node-el ', a.cls,'">',
38142                     // extran...
38143                     '<div class="x-tree-col ', colcls, '" style="width:', c.width-bw, 'px;">',
38144                 
38145                 
38146                         '<span class="x-tree-node-indent">',this.indentMarkup,'</span>',
38147                         '<img src="', this.emptyIcon, '" class="x-tree-ec-icon  " />',
38148                         '<img src="', a.icon || this.emptyIcon, '" class="x-tree-node-icon',
38149                            (a.icon ? ' x-tree-node-inline-icon' : ''),
38150                            (a.iconCls ? ' '+a.iconCls : ''),
38151                            '" unselectable="on" />',
38152                         (cb ? ('<input class="x-tree-node-cb" type="checkbox" ' + 
38153                              (a.checked ? 'checked="checked" />' : ' />')) : ''),
38154                              
38155                         '<a class="x-tree-node-anchor" hidefocus="on" href="',href,'" tabIndex="1" ',
38156                             (a.hrefTarget ? ' target="' +a.hrefTarget + '"' : ''), '>',
38157                             '<span unselectable="on" qtip="' + tx + '">',
38158                              tx,
38159                              '</span></a>' ,
38160                     '</div>',
38161                      '<a class="x-tree-node-anchor" hidefocus="on" href="',href,'" tabIndex="1" ',
38162                             (a.hrefTarget ? ' target="' +a.hrefTarget + '"' : ''), '>'
38163                  ];
38164         for(var i = 1, len = cols.length; i < len; i++){
38165             c = cols[i];
38166             colcls = 'x-t-' + tid + '-c' +i;
38167             tx = String.format('{0}', (c.renderer ? c.renderer(a[c.dataIndex], n, a) : a[c.dataIndex]));
38168             buf.push('<div class="x-tree-col ', colcls, ' ' ,(c.cls?c.cls:''),'" style="width:',c.width-bw,'px;">',
38169                         '<div class="x-tree-col-text" qtip="' + tx +'">',tx,"</div>",
38170                       "</div>");
38171          }
38172          
38173          buf.push(
38174             '</a>',
38175             '<div class="x-clear"></div></div>',
38176             '<ul class="x-tree-node-ct" style="display:none;"></ul>',
38177             "</li>");
38178         
38179         if(bulkRender !== true && n.nextSibling && n.nextSibling.ui.getEl()){
38180             this.wrap = Roo.DomHelper.insertHtml("beforeBegin",
38181                                 n.nextSibling.ui.getEl(), buf.join(""));
38182         }else{
38183             this.wrap = Roo.DomHelper.insertHtml("beforeEnd", targetNode, buf.join(""));
38184         }
38185         var el = this.wrap.firstChild;
38186         this.elRow = el;
38187         this.elNode = el.firstChild;
38188         this.ranchor = el.childNodes[1];
38189         this.ctNode = this.wrap.childNodes[1];
38190         var cs = el.firstChild.childNodes;
38191         this.indentNode = cs[0];
38192         this.ecNode = cs[1];
38193         this.iconNode = cs[2];
38194         var index = 3;
38195         if(cb){
38196             this.checkbox = cs[3];
38197             index++;
38198         }
38199         this.anchor = cs[index];
38200         
38201         this.textNode = cs[index].firstChild;
38202         
38203         //el.on("click", this.onClick, this);
38204         //el.on("dblclick", this.onDblClick, this);
38205         
38206         
38207        // console.log(this);
38208     },
38209     initEvents : function(){
38210         Roo.tree.ColumnNodeUI.superclass.initEvents.call(this);
38211         
38212             
38213         var a = this.ranchor;
38214
38215         var el = Roo.get(a);
38216
38217         if(Roo.isOpera){ // opera render bug ignores the CSS
38218             el.setStyle("text-decoration", "none");
38219         }
38220
38221         el.on("click", this.onClick, this);
38222         el.on("dblclick", this.onDblClick, this);
38223         el.on("contextmenu", this.onContextMenu, this);
38224         
38225     },
38226     
38227     /*onSelectedChange : function(state){
38228         if(state){
38229             this.focus();
38230             this.addClass("x-tree-selected");
38231         }else{
38232             //this.blur();
38233             this.removeClass("x-tree-selected");
38234         }
38235     },*/
38236     addClass : function(cls){
38237         if(this.elRow){
38238             Roo.fly(this.elRow).addClass(cls);
38239         }
38240         
38241     },
38242     
38243     
38244     removeClass : function(cls){
38245         if(this.elRow){
38246             Roo.fly(this.elRow).removeClass(cls);
38247         }
38248     }
38249
38250     
38251     
38252 });//<Script type="text/javascript">
38253
38254 /*
38255  * Based on:
38256  * Ext JS Library 1.1.1
38257  * Copyright(c) 2006-2007, Ext JS, LLC.
38258  *
38259  * Originally Released Under LGPL - original licence link has changed is not relivant.
38260  *
38261  * Fork - LGPL
38262  * <script type="text/javascript">
38263  */
38264  
38265
38266 /**
38267  * @class Roo.tree.ColumnTree
38268  * @extends Roo.tree.TreePanel
38269  * @cfg {Object} columns  Including width, header, renderer, cls, dataIndex 
38270  * @cfg {int} borderWidth  compined right/left border allowance
38271  * @constructor
38272  * @param {String/HTMLElement/Element} el The container element
38273  * @param {Object} config
38274  */
38275 Roo.tree.ColumnTree =  function(el, config)
38276 {
38277    Roo.tree.ColumnTree.superclass.constructor.call(this, el , config);
38278    this.addEvents({
38279         /**
38280         * @event resize
38281         * Fire this event on a container when it resizes
38282         * @param {int} w Width
38283         * @param {int} h Height
38284         */
38285        "resize" : true
38286     });
38287     this.on('resize', this.onResize, this);
38288 };
38289
38290 Roo.extend(Roo.tree.ColumnTree, Roo.tree.TreePanel, {
38291     //lines:false,
38292     
38293     
38294     borderWidth: Roo.isBorderBox ? 0 : 2, 
38295     headEls : false,
38296     
38297     render : function(){
38298         // add the header.....
38299        
38300         Roo.tree.ColumnTree.superclass.render.apply(this);
38301         
38302         this.el.addClass('x-column-tree');
38303         
38304         this.headers = this.el.createChild(
38305             {cls:'x-tree-headers'},this.innerCt.dom);
38306    
38307         var cols = this.columns, c;
38308         var totalWidth = 0;
38309         this.headEls = [];
38310         var  len = cols.length;
38311         for(var i = 0; i < len; i++){
38312              c = cols[i];
38313              totalWidth += c.width;
38314             this.headEls.push(this.headers.createChild({
38315                  cls:'x-tree-hd ' + (c.cls?c.cls+'-hd':''),
38316                  cn: {
38317                      cls:'x-tree-hd-text',
38318                      html: c.header
38319                  },
38320                  style:'width:'+(c.width-this.borderWidth)+'px;'
38321              }));
38322         }
38323         this.headers.createChild({cls:'x-clear'});
38324         // prevent floats from wrapping when clipped
38325         this.headers.setWidth(totalWidth);
38326         //this.innerCt.setWidth(totalWidth);
38327         this.innerCt.setStyle({ overflow: 'auto' });
38328         this.onResize(this.width, this.height);
38329              
38330         
38331     },
38332     onResize : function(w,h)
38333     {
38334         this.height = h;
38335         this.width = w;
38336         // resize cols..
38337         this.innerCt.setWidth(this.width);
38338         this.innerCt.setHeight(this.height-20);
38339         
38340         // headers...
38341         var cols = this.columns, c;
38342         var totalWidth = 0;
38343         var expEl = false;
38344         var len = cols.length;
38345         for(var i = 0; i < len; i++){
38346             c = cols[i];
38347             if (this.autoExpandColumn !== false && c.dataIndex == this.autoExpandColumn) {
38348                 // it's the expander..
38349                 expEl  = this.headEls[i];
38350                 continue;
38351             }
38352             totalWidth += c.width;
38353             
38354         }
38355         if (expEl) {
38356             expEl.setWidth(  ((w - totalWidth)-this.borderWidth - 20));
38357         }
38358         this.headers.setWidth(w-20);
38359
38360         
38361         
38362         
38363     }
38364 });
38365 /*
38366  * Based on:
38367  * Ext JS Library 1.1.1
38368  * Copyright(c) 2006-2007, Ext JS, LLC.
38369  *
38370  * Originally Released Under LGPL - original licence link has changed is not relivant.
38371  *
38372  * Fork - LGPL
38373  * <script type="text/javascript">
38374  */
38375  
38376 /**
38377  * @class Roo.menu.Menu
38378  * @extends Roo.util.Observable
38379  * @children Roo.menu.BaseItem
38380  * A menu object.  This is the container to which you add all other menu items.  Menu can also serve a as a base class
38381  * when you want a specialzed menu based off of another component (like {@link Roo.menu.DateMenu} for example).
38382  * @constructor
38383  * Creates a new Menu
38384  * @param {Object} config Configuration options
38385  */
38386 Roo.menu.Menu = function(config){
38387     
38388     Roo.menu.Menu.superclass.constructor.call(this, config);
38389     
38390     this.id = this.id || Roo.id();
38391     this.addEvents({
38392         /**
38393          * @event beforeshow
38394          * Fires before this menu is displayed
38395          * @param {Roo.menu.Menu} this
38396          */
38397         beforeshow : true,
38398         /**
38399          * @event beforehide
38400          * Fires before this menu is hidden
38401          * @param {Roo.menu.Menu} this
38402          */
38403         beforehide : true,
38404         /**
38405          * @event show
38406          * Fires after this menu is displayed
38407          * @param {Roo.menu.Menu} this
38408          */
38409         show : true,
38410         /**
38411          * @event hide
38412          * Fires after this menu is hidden
38413          * @param {Roo.menu.Menu} this
38414          */
38415         hide : true,
38416         /**
38417          * @event click
38418          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
38419          * @param {Roo.menu.Menu} this
38420          * @param {Roo.menu.Item} menuItem The menu item that was clicked
38421          * @param {Roo.EventObject} e
38422          */
38423         click : true,
38424         /**
38425          * @event mouseover
38426          * Fires when the mouse is hovering over this menu
38427          * @param {Roo.menu.Menu} this
38428          * @param {Roo.EventObject} e
38429          * @param {Roo.menu.Item} menuItem The menu item that was clicked
38430          */
38431         mouseover : true,
38432         /**
38433          * @event mouseout
38434          * Fires when the mouse exits this menu
38435          * @param {Roo.menu.Menu} this
38436          * @param {Roo.EventObject} e
38437          * @param {Roo.menu.Item} menuItem The menu item that was clicked
38438          */
38439         mouseout : true,
38440         /**
38441          * @event itemclick
38442          * Fires when a menu item contained in this menu is clicked
38443          * @param {Roo.menu.BaseItem} baseItem The BaseItem that was clicked
38444          * @param {Roo.EventObject} e
38445          */
38446         itemclick: true
38447     });
38448     if (this.registerMenu) {
38449         Roo.menu.MenuMgr.register(this);
38450     }
38451     
38452     var mis = this.items;
38453     this.items = new Roo.util.MixedCollection();
38454     if(mis){
38455         this.add.apply(this, mis);
38456     }
38457 };
38458
38459 Roo.extend(Roo.menu.Menu, Roo.util.Observable, {
38460     /**
38461      * @cfg {Number} minWidth The minimum width of the menu in pixels (defaults to 120)
38462      */
38463     minWidth : 120,
38464     /**
38465      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop"
38466      * for bottom-right shadow (defaults to "sides")
38467      */
38468     shadow : "sides",
38469     /**
38470      * @cfg {String} subMenuAlign The {@link Roo.Element#alignTo} anchor position value to use for submenus of
38471      * this menu (defaults to "tl-tr?")
38472      */
38473     subMenuAlign : "tl-tr?",
38474     /**
38475      * @cfg {String} defaultAlign The default {@link Roo.Element#alignTo) anchor position value for this menu
38476      * relative to its element of origin (defaults to "tl-bl?")
38477      */
38478     defaultAlign : "tl-bl?",
38479     /**
38480      * @cfg {Boolean} allowOtherMenus True to allow multiple menus to be displayed at the same time (defaults to false)
38481      */
38482     allowOtherMenus : false,
38483     /**
38484      * @cfg {Boolean} registerMenu True (default) - means that clicking on screen etc. hides it.
38485      */
38486     registerMenu : true,
38487
38488     hidden:true,
38489
38490     // private
38491     render : function(){
38492         if(this.el){
38493             return;
38494         }
38495         var el = this.el = new Roo.Layer({
38496             cls: "x-menu",
38497             shadow:this.shadow,
38498             constrain: false,
38499             parentEl: this.parentEl || document.body,
38500             zindex:15000
38501         });
38502
38503         this.keyNav = new Roo.menu.MenuNav(this);
38504
38505         if(this.plain){
38506             el.addClass("x-menu-plain");
38507         }
38508         if(this.cls){
38509             el.addClass(this.cls);
38510         }
38511         // generic focus element
38512         this.focusEl = el.createChild({
38513             tag: "a", cls: "x-menu-focus", href: "#", onclick: "return false;", tabIndex:"-1"
38514         });
38515         var ul = el.createChild({tag: "ul", cls: "x-menu-list"});
38516         //disabling touch- as it's causing issues ..
38517         //ul.on(Roo.isTouch ? 'touchstart' : 'click'   , this.onClick, this);
38518         ul.on('click'   , this.onClick, this);
38519         
38520         
38521         ul.on("mouseover", this.onMouseOver, this);
38522         ul.on("mouseout", this.onMouseOut, this);
38523         this.items.each(function(item){
38524             if (item.hidden) {
38525                 return;
38526             }
38527             
38528             var li = document.createElement("li");
38529             li.className = "x-menu-list-item";
38530             ul.dom.appendChild(li);
38531             item.render(li, this);
38532         }, this);
38533         this.ul = ul;
38534         this.autoWidth();
38535     },
38536
38537     // private
38538     autoWidth : function(){
38539         var el = this.el, ul = this.ul;
38540         if(!el){
38541             return;
38542         }
38543         var w = this.width;
38544         if(w){
38545             el.setWidth(w);
38546         }else if(Roo.isIE){
38547             el.setWidth(this.minWidth);
38548             var t = el.dom.offsetWidth; // force recalc
38549             el.setWidth(ul.getWidth()+el.getFrameWidth("lr"));
38550         }
38551     },
38552
38553     // private
38554     delayAutoWidth : function(){
38555         if(this.rendered){
38556             if(!this.awTask){
38557                 this.awTask = new Roo.util.DelayedTask(this.autoWidth, this);
38558             }
38559             this.awTask.delay(20);
38560         }
38561     },
38562
38563     // private
38564     findTargetItem : function(e){
38565         var t = e.getTarget(".x-menu-list-item", this.ul,  true);
38566         if(t && t.menuItemId){
38567             return this.items.get(t.menuItemId);
38568         }
38569     },
38570
38571     // private
38572     onClick : function(e){
38573         Roo.log("menu.onClick");
38574         var t = this.findTargetItem(e);
38575         if(!t){
38576             return;
38577         }
38578         Roo.log(e);
38579         if (Roo.isTouch && e.type == 'touchstart' && t.menu  && !t.disabled) {
38580             if(t == this.activeItem && t.shouldDeactivate(e)){
38581                 this.activeItem.deactivate();
38582                 delete this.activeItem;
38583                 return;
38584             }
38585             if(t.canActivate){
38586                 this.setActiveItem(t, true);
38587             }
38588             return;
38589             
38590             
38591         }
38592         
38593         t.onClick(e);
38594         this.fireEvent("click", this, t, e);
38595     },
38596
38597     // private
38598     setActiveItem : function(item, autoExpand){
38599         if(item != this.activeItem){
38600             if(this.activeItem){
38601                 this.activeItem.deactivate();
38602             }
38603             this.activeItem = item;
38604             item.activate(autoExpand);
38605         }else if(autoExpand){
38606             item.expandMenu();
38607         }
38608     },
38609
38610     // private
38611     tryActivate : function(start, step){
38612         var items = this.items;
38613         for(var i = start, len = items.length; i >= 0 && i < len; i+= step){
38614             var item = items.get(i);
38615             if(!item.disabled && item.canActivate){
38616                 this.setActiveItem(item, false);
38617                 return item;
38618             }
38619         }
38620         return false;
38621     },
38622
38623     // private
38624     onMouseOver : function(e){
38625         var t;
38626         if(t = this.findTargetItem(e)){
38627             if(t.canActivate && !t.disabled){
38628                 this.setActiveItem(t, true);
38629             }
38630         }
38631         this.fireEvent("mouseover", this, e, t);
38632     },
38633
38634     // private
38635     onMouseOut : function(e){
38636         var t;
38637         if(t = this.findTargetItem(e)){
38638             if(t == this.activeItem && t.shouldDeactivate(e)){
38639                 this.activeItem.deactivate();
38640                 delete this.activeItem;
38641             }
38642         }
38643         this.fireEvent("mouseout", this, e, t);
38644     },
38645
38646     /**
38647      * Read-only.  Returns true if the menu is currently displayed, else false.
38648      * @type Boolean
38649      */
38650     isVisible : function(){
38651         return this.el && !this.hidden;
38652     },
38653
38654     /**
38655      * Displays this menu relative to another element
38656      * @param {String/HTMLElement/Roo.Element} element The element to align to
38657      * @param {String} position (optional) The {@link Roo.Element#alignTo} anchor position to use in aligning to
38658      * the element (defaults to this.defaultAlign)
38659      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
38660      */
38661     show : function(el, pos, parentMenu){
38662         this.parentMenu = parentMenu;
38663         if(!this.el){
38664             this.render();
38665         }
38666         this.fireEvent("beforeshow", this);
38667         this.showAt(this.el.getAlignToXY(el, pos || this.defaultAlign), parentMenu, false);
38668     },
38669
38670     /**
38671      * Displays this menu at a specific xy position
38672      * @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)
38673      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
38674      */
38675     showAt : function(xy, parentMenu, /* private: */_e){
38676         this.parentMenu = parentMenu;
38677         if(!this.el){
38678             this.render();
38679         }
38680         if(_e !== false){
38681             this.fireEvent("beforeshow", this);
38682             xy = this.el.adjustForConstraints(xy);
38683         }
38684         this.el.setXY(xy);
38685         this.el.show();
38686         this.hidden = false;
38687         this.focus();
38688         this.fireEvent("show", this);
38689     },
38690
38691     focus : function(){
38692         if(!this.hidden){
38693             this.doFocus.defer(50, this);
38694         }
38695     },
38696
38697     doFocus : function(){
38698         if(!this.hidden){
38699             this.focusEl.focus();
38700         }
38701     },
38702
38703     /**
38704      * Hides this menu and optionally all parent menus
38705      * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
38706      */
38707     hide : function(deep){
38708         if(this.el && this.isVisible()){
38709             this.fireEvent("beforehide", this);
38710             if(this.activeItem){
38711                 this.activeItem.deactivate();
38712                 this.activeItem = null;
38713             }
38714             this.el.hide();
38715             this.hidden = true;
38716             this.fireEvent("hide", this);
38717         }
38718         if(deep === true && this.parentMenu){
38719             this.parentMenu.hide(true);
38720         }
38721     },
38722
38723     /**
38724      * Addds one or more items of any type supported by the Menu class, or that can be converted into menu items.
38725      * Any of the following are valid:
38726      * <ul>
38727      * <li>Any menu item object based on {@link Roo.menu.Item}</li>
38728      * <li>An HTMLElement object which will be converted to a menu item</li>
38729      * <li>A menu item config object that will be created as a new menu item</li>
38730      * <li>A string, which can either be '-' or 'separator' to add a menu separator, otherwise
38731      * it will be converted into a {@link Roo.menu.TextItem} and added</li>
38732      * </ul>
38733      * Usage:
38734      * <pre><code>
38735 // Create the menu
38736 var menu = new Roo.menu.Menu();
38737
38738 // Create a menu item to add by reference
38739 var menuItem = new Roo.menu.Item({ text: 'New Item!' });
38740
38741 // Add a bunch of items at once using different methods.
38742 // Only the last item added will be returned.
38743 var item = menu.add(
38744     menuItem,                // add existing item by ref
38745     'Dynamic Item',          // new TextItem
38746     '-',                     // new separator
38747     { text: 'Config Item' }  // new item by config
38748 );
38749 </code></pre>
38750      * @param {Mixed} args One or more menu items, menu item configs or other objects that can be converted to menu items
38751      * @return {Roo.menu.Item} The menu item that was added, or the last one if multiple items were added
38752      */
38753     add : function(){
38754         var a = arguments, l = a.length, item;
38755         for(var i = 0; i < l; i++){
38756             var el = a[i];
38757             if ((typeof(el) == "object") && el.xtype && el.xns) {
38758                 el = Roo.factory(el, Roo.menu);
38759             }
38760             
38761             if(el.render){ // some kind of Item
38762                 item = this.addItem(el);
38763             }else if(typeof el == "string"){ // string
38764                 if(el == "separator" || el == "-"){
38765                     item = this.addSeparator();
38766                 }else{
38767                     item = this.addText(el);
38768                 }
38769             }else if(el.tagName || el.el){ // element
38770                 item = this.addElement(el);
38771             }else if(typeof el == "object"){ // must be menu item config?
38772                 item = this.addMenuItem(el);
38773             }
38774         }
38775         return item;
38776     },
38777
38778     /**
38779      * Returns this menu's underlying {@link Roo.Element} object
38780      * @return {Roo.Element} The element
38781      */
38782     getEl : function(){
38783         if(!this.el){
38784             this.render();
38785         }
38786         return this.el;
38787     },
38788
38789     /**
38790      * Adds a separator bar to the menu
38791      * @return {Roo.menu.Item} The menu item that was added
38792      */
38793     addSeparator : function(){
38794         return this.addItem(new Roo.menu.Separator());
38795     },
38796
38797     /**
38798      * Adds an {@link Roo.Element} object to the menu
38799      * @param {String/HTMLElement/Roo.Element} el The element or DOM node to add, or its id
38800      * @return {Roo.menu.Item} The menu item that was added
38801      */
38802     addElement : function(el){
38803         return this.addItem(new Roo.menu.BaseItem(el));
38804     },
38805
38806     /**
38807      * Adds an existing object based on {@link Roo.menu.Item} to the menu
38808      * @param {Roo.menu.Item} item The menu item to add
38809      * @return {Roo.menu.Item} The menu item that was added
38810      */
38811     addItem : function(item){
38812         this.items.add(item);
38813         if(this.ul){
38814             var li = document.createElement("li");
38815             li.className = "x-menu-list-item";
38816             this.ul.dom.appendChild(li);
38817             item.render(li, this);
38818             this.delayAutoWidth();
38819         }
38820         return item;
38821     },
38822
38823     /**
38824      * Creates a new {@link Roo.menu.Item} based an the supplied config object and adds it to the menu
38825      * @param {Object} config A MenuItem config object
38826      * @return {Roo.menu.Item} The menu item that was added
38827      */
38828     addMenuItem : function(config){
38829         if(!(config instanceof Roo.menu.Item)){
38830             if(typeof config.checked == "boolean"){ // must be check menu item config?
38831                 config = new Roo.menu.CheckItem(config);
38832             }else{
38833                 config = new Roo.menu.Item(config);
38834             }
38835         }
38836         return this.addItem(config);
38837     },
38838
38839     /**
38840      * Creates a new {@link Roo.menu.TextItem} with the supplied text and adds it to the menu
38841      * @param {String} text The text to display in the menu item
38842      * @return {Roo.menu.Item} The menu item that was added
38843      */
38844     addText : function(text){
38845         return this.addItem(new Roo.menu.TextItem({ text : text }));
38846     },
38847
38848     /**
38849      * Inserts an existing object based on {@link Roo.menu.Item} to the menu at a specified index
38850      * @param {Number} index The index in the menu's list of current items where the new item should be inserted
38851      * @param {Roo.menu.Item} item The menu item to add
38852      * @return {Roo.menu.Item} The menu item that was added
38853      */
38854     insert : function(index, item){
38855         this.items.insert(index, item);
38856         if(this.ul){
38857             var li = document.createElement("li");
38858             li.className = "x-menu-list-item";
38859             this.ul.dom.insertBefore(li, this.ul.dom.childNodes[index]);
38860             item.render(li, this);
38861             this.delayAutoWidth();
38862         }
38863         return item;
38864     },
38865
38866     /**
38867      * Removes an {@link Roo.menu.Item} from the menu and destroys the object
38868      * @param {Roo.menu.Item} item The menu item to remove
38869      */
38870     remove : function(item){
38871         this.items.removeKey(item.id);
38872         item.destroy();
38873     },
38874
38875     /**
38876      * Removes and destroys all items in the menu
38877      */
38878     removeAll : function(){
38879         var f;
38880         while(f = this.items.first()){
38881             this.remove(f);
38882         }
38883     }
38884 });
38885
38886 // MenuNav is a private utility class used internally by the Menu
38887 Roo.menu.MenuNav = function(menu){
38888     Roo.menu.MenuNav.superclass.constructor.call(this, menu.el);
38889     this.scope = this.menu = menu;
38890 };
38891
38892 Roo.extend(Roo.menu.MenuNav, Roo.KeyNav, {
38893     doRelay : function(e, h){
38894         var k = e.getKey();
38895         if(!this.menu.activeItem && e.isNavKeyPress() && k != e.SPACE && k != e.RETURN){
38896             this.menu.tryActivate(0, 1);
38897             return false;
38898         }
38899         return h.call(this.scope || this, e, this.menu);
38900     },
38901
38902     up : function(e, m){
38903         if(!m.tryActivate(m.items.indexOf(m.activeItem)-1, -1)){
38904             m.tryActivate(m.items.length-1, -1);
38905         }
38906     },
38907
38908     down : function(e, m){
38909         if(!m.tryActivate(m.items.indexOf(m.activeItem)+1, 1)){
38910             m.tryActivate(0, 1);
38911         }
38912     },
38913
38914     right : function(e, m){
38915         if(m.activeItem){
38916             m.activeItem.expandMenu(true);
38917         }
38918     },
38919
38920     left : function(e, m){
38921         m.hide();
38922         if(m.parentMenu && m.parentMenu.activeItem){
38923             m.parentMenu.activeItem.activate();
38924         }
38925     },
38926
38927     enter : function(e, m){
38928         if(m.activeItem){
38929             e.stopPropagation();
38930             m.activeItem.onClick(e);
38931             m.fireEvent("click", this, m.activeItem);
38932             return true;
38933         }
38934     }
38935 });/*
38936  * Based on:
38937  * Ext JS Library 1.1.1
38938  * Copyright(c) 2006-2007, Ext JS, LLC.
38939  *
38940  * Originally Released Under LGPL - original licence link has changed is not relivant.
38941  *
38942  * Fork - LGPL
38943  * <script type="text/javascript">
38944  */
38945  
38946 /**
38947  * @class Roo.menu.MenuMgr
38948  * Provides a common registry of all menu items on a page so that they can be easily accessed by id.
38949  * @static
38950  */
38951 Roo.menu.MenuMgr = function(){
38952    var menus, active, groups = {}, attached = false, lastShow = new Date();
38953
38954    // private - called when first menu is created
38955    function init(){
38956        menus = {};
38957        active = new Roo.util.MixedCollection();
38958        Roo.get(document).addKeyListener(27, function(){
38959            if(active.length > 0){
38960                hideAll();
38961            }
38962        });
38963    }
38964
38965    // private
38966    function hideAll(){
38967        if(active && active.length > 0){
38968            var c = active.clone();
38969            c.each(function(m){
38970                m.hide();
38971            });
38972        }
38973    }
38974
38975    // private
38976    function onHide(m){
38977        active.remove(m);
38978        if(active.length < 1){
38979            Roo.get(document).un("mousedown", onMouseDown);
38980            attached = false;
38981        }
38982    }
38983
38984    // private
38985    function onShow(m){
38986        var last = active.last();
38987        lastShow = new Date();
38988        active.add(m);
38989        if(!attached){
38990            Roo.get(document).on("mousedown", onMouseDown);
38991            attached = true;
38992        }
38993        if(m.parentMenu){
38994           m.getEl().setZIndex(parseInt(m.parentMenu.getEl().getStyle("z-index"), 10) + 3);
38995           m.parentMenu.activeChild = m;
38996        }else if(last && last.isVisible()){
38997           m.getEl().setZIndex(parseInt(last.getEl().getStyle("z-index"), 10) + 3);
38998        }
38999    }
39000
39001    // private
39002    function onBeforeHide(m){
39003        if(m.activeChild){
39004            m.activeChild.hide();
39005        }
39006        if(m.autoHideTimer){
39007            clearTimeout(m.autoHideTimer);
39008            delete m.autoHideTimer;
39009        }
39010    }
39011
39012    // private
39013    function onBeforeShow(m){
39014        var pm = m.parentMenu;
39015        if(!pm && !m.allowOtherMenus){
39016            hideAll();
39017        }else if(pm && pm.activeChild && active != m){
39018            pm.activeChild.hide();
39019        }
39020    }
39021
39022    // private
39023    function onMouseDown(e){
39024        if(lastShow.getElapsed() > 50 && active.length > 0 && !e.getTarget(".x-menu")){
39025            hideAll();
39026        }
39027    }
39028
39029    // private
39030    function onBeforeCheck(mi, state){
39031        if(state){
39032            var g = groups[mi.group];
39033            for(var i = 0, l = g.length; i < l; i++){
39034                if(g[i] != mi){
39035                    g[i].setChecked(false);
39036                }
39037            }
39038        }
39039    }
39040
39041    return {
39042
39043        /**
39044         * Hides all menus that are currently visible
39045         */
39046        hideAll : function(){
39047             hideAll();  
39048        },
39049
39050        // private
39051        register : function(menu){
39052            if(!menus){
39053                init();
39054            }
39055            menus[menu.id] = menu;
39056            menu.on("beforehide", onBeforeHide);
39057            menu.on("hide", onHide);
39058            menu.on("beforeshow", onBeforeShow);
39059            menu.on("show", onShow);
39060            var g = menu.group;
39061            if(g && menu.events["checkchange"]){
39062                if(!groups[g]){
39063                    groups[g] = [];
39064                }
39065                groups[g].push(menu);
39066                menu.on("checkchange", onCheck);
39067            }
39068        },
39069
39070         /**
39071          * Returns a {@link Roo.menu.Menu} object
39072          * @param {String/Object} menu The string menu id, an existing menu object reference, or a Menu config that will
39073          * be used to generate and return a new Menu instance.
39074          */
39075        get : function(menu){
39076            if(typeof menu == "string"){ // menu id
39077                return menus[menu];
39078            }else if(menu.events){  // menu instance
39079                return menu;
39080            }else if(typeof menu.length == 'number'){ // array of menu items?
39081                return new Roo.menu.Menu({items:menu});
39082            }else{ // otherwise, must be a config
39083                return new Roo.menu.Menu(menu);
39084            }
39085        },
39086
39087        // private
39088        unregister : function(menu){
39089            delete menus[menu.id];
39090            menu.un("beforehide", onBeforeHide);
39091            menu.un("hide", onHide);
39092            menu.un("beforeshow", onBeforeShow);
39093            menu.un("show", onShow);
39094            var g = menu.group;
39095            if(g && menu.events["checkchange"]){
39096                groups[g].remove(menu);
39097                menu.un("checkchange", onCheck);
39098            }
39099        },
39100
39101        // private
39102        registerCheckable : function(menuItem){
39103            var g = menuItem.group;
39104            if(g){
39105                if(!groups[g]){
39106                    groups[g] = [];
39107                }
39108                groups[g].push(menuItem);
39109                menuItem.on("beforecheckchange", onBeforeCheck);
39110            }
39111        },
39112
39113        // private
39114        unregisterCheckable : function(menuItem){
39115            var g = menuItem.group;
39116            if(g){
39117                groups[g].remove(menuItem);
39118                menuItem.un("beforecheckchange", onBeforeCheck);
39119            }
39120        }
39121    };
39122 }();/*
39123  * Based on:
39124  * Ext JS Library 1.1.1
39125  * Copyright(c) 2006-2007, Ext JS, LLC.
39126  *
39127  * Originally Released Under LGPL - original licence link has changed is not relivant.
39128  *
39129  * Fork - LGPL
39130  * <script type="text/javascript">
39131  */
39132  
39133
39134 /**
39135  * @class Roo.menu.BaseItem
39136  * @extends Roo.Component
39137  * @abstract
39138  * The base class for all items that render into menus.  BaseItem provides default rendering, activated state
39139  * management and base configuration options shared by all menu components.
39140  * @constructor
39141  * Creates a new BaseItem
39142  * @param {Object} config Configuration options
39143  */
39144 Roo.menu.BaseItem = function(config){
39145     Roo.menu.BaseItem.superclass.constructor.call(this, config);
39146
39147     this.addEvents({
39148         /**
39149          * @event click
39150          * Fires when this item is clicked
39151          * @param {Roo.menu.BaseItem} this
39152          * @param {Roo.EventObject} e
39153          */
39154         click: true,
39155         /**
39156          * @event activate
39157          * Fires when this item is activated
39158          * @param {Roo.menu.BaseItem} this
39159          */
39160         activate : true,
39161         /**
39162          * @event deactivate
39163          * Fires when this item is deactivated
39164          * @param {Roo.menu.BaseItem} this
39165          */
39166         deactivate : true
39167     });
39168
39169     if(this.handler){
39170         this.on("click", this.handler, this.scope, true);
39171     }
39172 };
39173
39174 Roo.extend(Roo.menu.BaseItem, Roo.Component, {
39175     /**
39176      * @cfg {Function} handler
39177      * A function that will handle the click event of this menu item (defaults to undefined)
39178      */
39179     /**
39180      * @cfg {Boolean} canActivate True if this item can be visually activated (defaults to false)
39181      */
39182     canActivate : false,
39183     
39184      /**
39185      * @cfg {Boolean} hidden True to prevent creation of this menu item (defaults to false)
39186      */
39187     hidden: false,
39188     
39189     /**
39190      * @cfg {String} activeClass The CSS class to use when the item becomes activated (defaults to "x-menu-item-active")
39191      */
39192     activeClass : "x-menu-item-active",
39193     /**
39194      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to true)
39195      */
39196     hideOnClick : true,
39197     /**
39198      * @cfg {Number} hideDelay Length of time in milliseconds to wait before hiding after a click (defaults to 100)
39199      */
39200     hideDelay : 100,
39201
39202     // private
39203     ctype: "Roo.menu.BaseItem",
39204
39205     // private
39206     actionMode : "container",
39207
39208     // private
39209     render : function(container, parentMenu){
39210         this.parentMenu = parentMenu;
39211         Roo.menu.BaseItem.superclass.render.call(this, container);
39212         this.container.menuItemId = this.id;
39213     },
39214
39215     // private
39216     onRender : function(container, position){
39217         this.el = Roo.get(this.el);
39218         container.dom.appendChild(this.el.dom);
39219     },
39220
39221     // private
39222     onClick : function(e){
39223         if(!this.disabled && this.fireEvent("click", this, e) !== false
39224                 && this.parentMenu.fireEvent("itemclick", this, e) !== false){
39225             this.handleClick(e);
39226         }else{
39227             e.stopEvent();
39228         }
39229     },
39230
39231     // private
39232     activate : function(){
39233         if(this.disabled){
39234             return false;
39235         }
39236         var li = this.container;
39237         li.addClass(this.activeClass);
39238         this.region = li.getRegion().adjust(2, 2, -2, -2);
39239         this.fireEvent("activate", this);
39240         return true;
39241     },
39242
39243     // private
39244     deactivate : function(){
39245         this.container.removeClass(this.activeClass);
39246         this.fireEvent("deactivate", this);
39247     },
39248
39249     // private
39250     shouldDeactivate : function(e){
39251         return !this.region || !this.region.contains(e.getPoint());
39252     },
39253
39254     // private
39255     handleClick : function(e){
39256         if(this.hideOnClick){
39257             this.parentMenu.hide.defer(this.hideDelay, this.parentMenu, [true]);
39258         }
39259     },
39260
39261     // private
39262     expandMenu : function(autoActivate){
39263         // do nothing
39264     },
39265
39266     // private
39267     hideMenu : function(){
39268         // do nothing
39269     }
39270 });/*
39271  * Based on:
39272  * Ext JS Library 1.1.1
39273  * Copyright(c) 2006-2007, Ext JS, LLC.
39274  *
39275  * Originally Released Under LGPL - original licence link has changed is not relivant.
39276  *
39277  * Fork - LGPL
39278  * <script type="text/javascript">
39279  */
39280  
39281 /**
39282  * @class Roo.menu.Adapter
39283  * @extends Roo.menu.BaseItem
39284  * @abstract
39285  * 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.
39286  * It provides basic rendering, activation management and enable/disable logic required to work in menus.
39287  * @constructor
39288  * Creates a new Adapter
39289  * @param {Object} config Configuration options
39290  */
39291 Roo.menu.Adapter = function(component, config){
39292     Roo.menu.Adapter.superclass.constructor.call(this, config);
39293     this.component = component;
39294 };
39295 Roo.extend(Roo.menu.Adapter, Roo.menu.BaseItem, {
39296     // private
39297     canActivate : true,
39298
39299     // private
39300     onRender : function(container, position){
39301         this.component.render(container);
39302         this.el = this.component.getEl();
39303     },
39304
39305     // private
39306     activate : function(){
39307         if(this.disabled){
39308             return false;
39309         }
39310         this.component.focus();
39311         this.fireEvent("activate", this);
39312         return true;
39313     },
39314
39315     // private
39316     deactivate : function(){
39317         this.fireEvent("deactivate", this);
39318     },
39319
39320     // private
39321     disable : function(){
39322         this.component.disable();
39323         Roo.menu.Adapter.superclass.disable.call(this);
39324     },
39325
39326     // private
39327     enable : function(){
39328         this.component.enable();
39329         Roo.menu.Adapter.superclass.enable.call(this);
39330     }
39331 });/*
39332  * Based on:
39333  * Ext JS Library 1.1.1
39334  * Copyright(c) 2006-2007, Ext JS, LLC.
39335  *
39336  * Originally Released Under LGPL - original licence link has changed is not relivant.
39337  *
39338  * Fork - LGPL
39339  * <script type="text/javascript">
39340  */
39341
39342 /**
39343  * @class Roo.menu.TextItem
39344  * @extends Roo.menu.BaseItem
39345  * Adds a static text string to a menu, usually used as either a heading or group separator.
39346  * Note: old style constructor with text is still supported.
39347  * 
39348  * @constructor
39349  * Creates a new TextItem
39350  * @param {Object} cfg Configuration
39351  */
39352 Roo.menu.TextItem = function(cfg){
39353     if (typeof(cfg) == 'string') {
39354         this.text = cfg;
39355     } else {
39356         Roo.apply(this,cfg);
39357     }
39358     
39359     Roo.menu.TextItem.superclass.constructor.call(this);
39360 };
39361
39362 Roo.extend(Roo.menu.TextItem, Roo.menu.BaseItem, {
39363     /**
39364      * @cfg {String} text Text to show on item.
39365      */
39366     text : '',
39367     
39368     /**
39369      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to false)
39370      */
39371     hideOnClick : false,
39372     /**
39373      * @cfg {String} itemCls The default CSS class to use for text items (defaults to "x-menu-text")
39374      */
39375     itemCls : "x-menu-text",
39376
39377     // private
39378     onRender : function(){
39379         var s = document.createElement("span");
39380         s.className = this.itemCls;
39381         s.innerHTML = this.text;
39382         this.el = s;
39383         Roo.menu.TextItem.superclass.onRender.apply(this, arguments);
39384     }
39385 });/*
39386  * Based on:
39387  * Ext JS Library 1.1.1
39388  * Copyright(c) 2006-2007, Ext JS, LLC.
39389  *
39390  * Originally Released Under LGPL - original licence link has changed is not relivant.
39391  *
39392  * Fork - LGPL
39393  * <script type="text/javascript">
39394  */
39395
39396 /**
39397  * @class Roo.menu.Separator
39398  * @extends Roo.menu.BaseItem
39399  * Adds a separator bar to a menu, used to divide logical groups of menu items. Generally you will
39400  * add one of these by using "-" in you call to add() or in your items config rather than creating one directly.
39401  * @constructor
39402  * @param {Object} config Configuration options
39403  */
39404 Roo.menu.Separator = function(config){
39405     Roo.menu.Separator.superclass.constructor.call(this, config);
39406 };
39407
39408 Roo.extend(Roo.menu.Separator, Roo.menu.BaseItem, {
39409     /**
39410      * @cfg {String} itemCls The default CSS class to use for separators (defaults to "x-menu-sep")
39411      */
39412     itemCls : "x-menu-sep",
39413     /**
39414      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to false)
39415      */
39416     hideOnClick : false,
39417
39418     // private
39419     onRender : function(li){
39420         var s = document.createElement("span");
39421         s.className = this.itemCls;
39422         s.innerHTML = "&#160;";
39423         this.el = s;
39424         li.addClass("x-menu-sep-li");
39425         Roo.menu.Separator.superclass.onRender.apply(this, arguments);
39426     }
39427 });/*
39428  * Based on:
39429  * Ext JS Library 1.1.1
39430  * Copyright(c) 2006-2007, Ext JS, LLC.
39431  *
39432  * Originally Released Under LGPL - original licence link has changed is not relivant.
39433  *
39434  * Fork - LGPL
39435  * <script type="text/javascript">
39436  */
39437 /**
39438  * @class Roo.menu.Item
39439  * @extends Roo.menu.BaseItem
39440  * A base class for all menu items that require menu-related functionality (like sub-menus) and are not static
39441  * display items.  Item extends the base functionality of {@link Roo.menu.BaseItem} by adding menu-specific
39442  * activation and click handling.
39443  * @constructor
39444  * Creates a new Item
39445  * @param {Object} config Configuration options
39446  */
39447 Roo.menu.Item = function(config){
39448     Roo.menu.Item.superclass.constructor.call(this, config);
39449     if(this.menu){
39450         this.menu = Roo.menu.MenuMgr.get(this.menu);
39451     }
39452 };
39453 Roo.extend(Roo.menu.Item, Roo.menu.BaseItem, {
39454     /**
39455      * @cfg {Roo.menu.Menu} menu
39456      * A Sub menu
39457      */
39458     /**
39459      * @cfg {String} text
39460      * The text to show on the menu item.
39461      */
39462     text: '',
39463      /**
39464      * @cfg {String} HTML to render in menu
39465      * The text to show on the menu item (HTML version).
39466      */
39467     html: '',
39468     /**
39469      * @cfg {String} icon
39470      * The path to an icon to display in this menu item (defaults to Roo.BLANK_IMAGE_URL)
39471      */
39472     icon: undefined,
39473     /**
39474      * @cfg {String} itemCls The default CSS class to use for menu items (defaults to "x-menu-item")
39475      */
39476     itemCls : "x-menu-item",
39477     /**
39478      * @cfg {Boolean} canActivate True if this item can be visually activated (defaults to true)
39479      */
39480     canActivate : true,
39481     /**
39482      * @cfg {Number} showDelay Length of time in milliseconds to wait before showing this item (defaults to 200)
39483      */
39484     showDelay: 200,
39485     // doc'd in BaseItem
39486     hideDelay: 200,
39487
39488     // private
39489     ctype: "Roo.menu.Item",
39490     
39491     // private
39492     onRender : function(container, position){
39493         var el = document.createElement("a");
39494         el.hideFocus = true;
39495         el.unselectable = "on";
39496         el.href = this.href || "#";
39497         if(this.hrefTarget){
39498             el.target = this.hrefTarget;
39499         }
39500         el.className = this.itemCls + (this.menu ?  " x-menu-item-arrow" : "") + (this.cls ?  " " + this.cls : "");
39501         
39502         var html = this.html.length ? this.html  : String.format('{0}',this.text);
39503         
39504         el.innerHTML = String.format(
39505                 '<img src="{0}" class="x-menu-item-icon {1}" />' + html,
39506                 this.icon || Roo.BLANK_IMAGE_URL, this.iconCls || '');
39507         this.el = el;
39508         Roo.menu.Item.superclass.onRender.call(this, container, position);
39509     },
39510
39511     /**
39512      * Sets the text to display in this menu item
39513      * @param {String} text The text to display
39514      * @param {Boolean} isHTML true to indicate text is pure html.
39515      */
39516     setText : function(text, isHTML){
39517         if (isHTML) {
39518             this.html = text;
39519         } else {
39520             this.text = text;
39521             this.html = '';
39522         }
39523         if(this.rendered){
39524             var html = this.html.length ? this.html  : String.format('{0}',this.text);
39525      
39526             this.el.update(String.format(
39527                 '<img src="{0}" class="x-menu-item-icon {2}">' + html,
39528                 this.icon || Roo.BLANK_IMAGE_URL, this.text, this.iconCls || ''));
39529             this.parentMenu.autoWidth();
39530         }
39531     },
39532
39533     // private
39534     handleClick : function(e){
39535         if(!this.href){ // if no link defined, stop the event automatically
39536             e.stopEvent();
39537         }
39538         Roo.menu.Item.superclass.handleClick.apply(this, arguments);
39539     },
39540
39541     // private
39542     activate : function(autoExpand){
39543         if(Roo.menu.Item.superclass.activate.apply(this, arguments)){
39544             this.focus();
39545             if(autoExpand){
39546                 this.expandMenu();
39547             }
39548         }
39549         return true;
39550     },
39551
39552     // private
39553     shouldDeactivate : function(e){
39554         if(Roo.menu.Item.superclass.shouldDeactivate.call(this, e)){
39555             if(this.menu && this.menu.isVisible()){
39556                 return !this.menu.getEl().getRegion().contains(e.getPoint());
39557             }
39558             return true;
39559         }
39560         return false;
39561     },
39562
39563     // private
39564     deactivate : function(){
39565         Roo.menu.Item.superclass.deactivate.apply(this, arguments);
39566         this.hideMenu();
39567     },
39568
39569     // private
39570     expandMenu : function(autoActivate){
39571         if(!this.disabled && this.menu){
39572             clearTimeout(this.hideTimer);
39573             delete this.hideTimer;
39574             if(!this.menu.isVisible() && !this.showTimer){
39575                 this.showTimer = this.deferExpand.defer(this.showDelay, this, [autoActivate]);
39576             }else if (this.menu.isVisible() && autoActivate){
39577                 this.menu.tryActivate(0, 1);
39578             }
39579         }
39580     },
39581
39582     // private
39583     deferExpand : function(autoActivate){
39584         delete this.showTimer;
39585         this.menu.show(this.container, this.parentMenu.subMenuAlign || "tl-tr?", this.parentMenu);
39586         if(autoActivate){
39587             this.menu.tryActivate(0, 1);
39588         }
39589     },
39590
39591     // private
39592     hideMenu : function(){
39593         clearTimeout(this.showTimer);
39594         delete this.showTimer;
39595         if(!this.hideTimer && this.menu && this.menu.isVisible()){
39596             this.hideTimer = this.deferHide.defer(this.hideDelay, this);
39597         }
39598     },
39599
39600     // private
39601     deferHide : function(){
39602         delete this.hideTimer;
39603         this.menu.hide();
39604     }
39605 });/*
39606  * Based on:
39607  * Ext JS Library 1.1.1
39608  * Copyright(c) 2006-2007, Ext JS, LLC.
39609  *
39610  * Originally Released Under LGPL - original licence link has changed is not relivant.
39611  *
39612  * Fork - LGPL
39613  * <script type="text/javascript">
39614  */
39615  
39616 /**
39617  * @class Roo.menu.CheckItem
39618  * @extends Roo.menu.Item
39619  * Adds a menu item that contains a checkbox by default, but can also be part of a radio group.
39620  * @constructor
39621  * Creates a new CheckItem
39622  * @param {Object} config Configuration options
39623  */
39624 Roo.menu.CheckItem = function(config){
39625     Roo.menu.CheckItem.superclass.constructor.call(this, config);
39626     this.addEvents({
39627         /**
39628          * @event beforecheckchange
39629          * Fires before the checked value is set, providing an opportunity to cancel if needed
39630          * @param {Roo.menu.CheckItem} this
39631          * @param {Boolean} checked The new checked value that will be set
39632          */
39633         "beforecheckchange" : true,
39634         /**
39635          * @event checkchange
39636          * Fires after the checked value has been set
39637          * @param {Roo.menu.CheckItem} this
39638          * @param {Boolean} checked The checked value that was set
39639          */
39640         "checkchange" : true
39641     });
39642     if(this.checkHandler){
39643         this.on('checkchange', this.checkHandler, this.scope);
39644     }
39645 };
39646 Roo.extend(Roo.menu.CheckItem, Roo.menu.Item, {
39647     /**
39648      * @cfg {String} group
39649      * All check items with the same group name will automatically be grouped into a single-select
39650      * radio button group (defaults to '')
39651      */
39652     /**
39653      * @cfg {String} itemCls The default CSS class to use for check items (defaults to "x-menu-item x-menu-check-item")
39654      */
39655     itemCls : "x-menu-item x-menu-check-item",
39656     /**
39657      * @cfg {String} groupClass The default CSS class to use for radio group check items (defaults to "x-menu-group-item")
39658      */
39659     groupClass : "x-menu-group-item",
39660
39661     /**
39662      * @cfg {Boolean} checked True to initialize this checkbox as checked (defaults to false).  Note that
39663      * if this checkbox is part of a radio group (group = true) only the last item in the group that is
39664      * initialized with checked = true will be rendered as checked.
39665      */
39666     checked: false,
39667
39668     // private
39669     ctype: "Roo.menu.CheckItem",
39670
39671     // private
39672     onRender : function(c){
39673         Roo.menu.CheckItem.superclass.onRender.apply(this, arguments);
39674         if(this.group){
39675             this.el.addClass(this.groupClass);
39676         }
39677         Roo.menu.MenuMgr.registerCheckable(this);
39678         if(this.checked){
39679             this.checked = false;
39680             this.setChecked(true, true);
39681         }
39682     },
39683
39684     // private
39685     destroy : function(){
39686         if(this.rendered){
39687             Roo.menu.MenuMgr.unregisterCheckable(this);
39688         }
39689         Roo.menu.CheckItem.superclass.destroy.apply(this, arguments);
39690     },
39691
39692     /**
39693      * Set the checked state of this item
39694      * @param {Boolean} checked The new checked value
39695      * @param {Boolean} suppressEvent (optional) True to prevent the checkchange event from firing (defaults to false)
39696      */
39697     setChecked : function(state, suppressEvent){
39698         if(this.checked != state && this.fireEvent("beforecheckchange", this, state) !== false){
39699             if(this.container){
39700                 this.container[state ? "addClass" : "removeClass"]("x-menu-item-checked");
39701             }
39702             this.checked = state;
39703             if(suppressEvent !== true){
39704                 this.fireEvent("checkchange", this, state);
39705             }
39706         }
39707     },
39708
39709     // private
39710     handleClick : function(e){
39711        if(!this.disabled && !(this.checked && this.group)){// disable unselect on radio item
39712            this.setChecked(!this.checked);
39713        }
39714        Roo.menu.CheckItem.superclass.handleClick.apply(this, arguments);
39715     }
39716 });/*
39717  * Based on:
39718  * Ext JS Library 1.1.1
39719  * Copyright(c) 2006-2007, Ext JS, LLC.
39720  *
39721  * Originally Released Under LGPL - original licence link has changed is not relivant.
39722  *
39723  * Fork - LGPL
39724  * <script type="text/javascript">
39725  */
39726  
39727 /**
39728  * @class Roo.menu.DateItem
39729  * @extends Roo.menu.Adapter
39730  * A menu item that wraps the {@link Roo.DatPicker} component.
39731  * @constructor
39732  * Creates a new DateItem
39733  * @param {Object} config Configuration options
39734  */
39735 Roo.menu.DateItem = function(config){
39736     Roo.menu.DateItem.superclass.constructor.call(this, new Roo.DatePicker(config), config);
39737     /** The Roo.DatePicker object @type Roo.DatePicker */
39738     this.picker = this.component;
39739     this.addEvents({select: true});
39740     
39741     this.picker.on("render", function(picker){
39742         picker.getEl().swallowEvent("click");
39743         picker.container.addClass("x-menu-date-item");
39744     });
39745
39746     this.picker.on("select", this.onSelect, this);
39747 };
39748
39749 Roo.extend(Roo.menu.DateItem, Roo.menu.Adapter, {
39750     // private
39751     onSelect : function(picker, date){
39752         this.fireEvent("select", this, date, picker);
39753         Roo.menu.DateItem.superclass.handleClick.call(this);
39754     }
39755 });/*
39756  * Based on:
39757  * Ext JS Library 1.1.1
39758  * Copyright(c) 2006-2007, Ext JS, LLC.
39759  *
39760  * Originally Released Under LGPL - original licence link has changed is not relivant.
39761  *
39762  * Fork - LGPL
39763  * <script type="text/javascript">
39764  */
39765  
39766 /**
39767  * @class Roo.menu.ColorItem
39768  * @extends Roo.menu.Adapter
39769  * A menu item that wraps the {@link Roo.ColorPalette} component.
39770  * @constructor
39771  * Creates a new ColorItem
39772  * @param {Object} config Configuration options
39773  */
39774 Roo.menu.ColorItem = function(config){
39775     Roo.menu.ColorItem.superclass.constructor.call(this, new Roo.ColorPalette(config), config);
39776     /** The Roo.ColorPalette object @type Roo.ColorPalette */
39777     this.palette = this.component;
39778     this.relayEvents(this.palette, ["select"]);
39779     if(this.selectHandler){
39780         this.on('select', this.selectHandler, this.scope);
39781     }
39782 };
39783 Roo.extend(Roo.menu.ColorItem, Roo.menu.Adapter);/*
39784  * Based on:
39785  * Ext JS Library 1.1.1
39786  * Copyright(c) 2006-2007, Ext JS, LLC.
39787  *
39788  * Originally Released Under LGPL - original licence link has changed is not relivant.
39789  *
39790  * Fork - LGPL
39791  * <script type="text/javascript">
39792  */
39793  
39794
39795 /**
39796  * @class Roo.menu.DateMenu
39797  * @extends Roo.menu.Menu
39798  * A menu containing a {@link Roo.menu.DateItem} component (which provides a date picker).
39799  * @constructor
39800  * Creates a new DateMenu
39801  * @param {Object} config Configuration options
39802  */
39803 Roo.menu.DateMenu = function(config){
39804     Roo.menu.DateMenu.superclass.constructor.call(this, config);
39805     this.plain = true;
39806     var di = new Roo.menu.DateItem(config);
39807     this.add(di);
39808     /**
39809      * The {@link Roo.DatePicker} instance for this DateMenu
39810      * @type DatePicker
39811      */
39812     this.picker = di.picker;
39813     /**
39814      * @event select
39815      * @param {DatePicker} picker
39816      * @param {Date} date
39817      */
39818     this.relayEvents(di, ["select"]);
39819     this.on('beforeshow', function(){
39820         if(this.picker){
39821             this.picker.hideMonthPicker(false);
39822         }
39823     }, this);
39824 };
39825 Roo.extend(Roo.menu.DateMenu, Roo.menu.Menu, {
39826     cls:'x-date-menu'
39827 });/*
39828  * Based on:
39829  * Ext JS Library 1.1.1
39830  * Copyright(c) 2006-2007, Ext JS, LLC.
39831  *
39832  * Originally Released Under LGPL - original licence link has changed is not relivant.
39833  *
39834  * Fork - LGPL
39835  * <script type="text/javascript">
39836  */
39837  
39838
39839 /**
39840  * @class Roo.menu.ColorMenu
39841  * @extends Roo.menu.Menu
39842  * A menu containing a {@link Roo.menu.ColorItem} component (which provides a basic color picker).
39843  * @constructor
39844  * Creates a new ColorMenu
39845  * @param {Object} config Configuration options
39846  */
39847 Roo.menu.ColorMenu = function(config){
39848     Roo.menu.ColorMenu.superclass.constructor.call(this, config);
39849     this.plain = true;
39850     var ci = new Roo.menu.ColorItem(config);
39851     this.add(ci);
39852     /**
39853      * The {@link Roo.ColorPalette} instance for this ColorMenu
39854      * @type ColorPalette
39855      */
39856     this.palette = ci.palette;
39857     /**
39858      * @event select
39859      * @param {ColorPalette} palette
39860      * @param {String} color
39861      */
39862     this.relayEvents(ci, ["select"]);
39863 };
39864 Roo.extend(Roo.menu.ColorMenu, Roo.menu.Menu);/*
39865  * Based on:
39866  * Ext JS Library 1.1.1
39867  * Copyright(c) 2006-2007, Ext JS, LLC.
39868  *
39869  * Originally Released Under LGPL - original licence link has changed is not relivant.
39870  *
39871  * Fork - LGPL
39872  * <script type="text/javascript">
39873  */
39874  
39875 /**
39876  * @class Roo.form.TextItem
39877  * @extends Roo.BoxComponent
39878  * Base class for form fields that provides default event handling, sizing, value handling and other functionality.
39879  * @constructor
39880  * Creates a new TextItem
39881  * @param {Object} config Configuration options
39882  */
39883 Roo.form.TextItem = function(config){
39884     Roo.form.TextItem.superclass.constructor.call(this, config);
39885 };
39886
39887 Roo.extend(Roo.form.TextItem, Roo.BoxComponent,  {
39888     
39889     /**
39890      * @cfg {String} tag the tag for this item (default div)
39891      */
39892     tag : 'div',
39893     /**
39894      * @cfg {String} html the content for this item
39895      */
39896     html : '',
39897     
39898     getAutoCreate : function()
39899     {
39900         var cfg = {
39901             id: this.id,
39902             tag: this.tag,
39903             html: this.html,
39904             cls: 'x-form-item'
39905         };
39906         
39907         return cfg;
39908         
39909     },
39910     
39911     onRender : function(ct, position)
39912     {
39913         Roo.form.TextItem.superclass.onRender.call(this, ct, position);
39914         
39915         if(!this.el){
39916             var cfg = this.getAutoCreate();
39917             if(!cfg.name){
39918                 cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
39919             }
39920             if (!cfg.name.length) {
39921                 delete cfg.name;
39922             }
39923             this.el = ct.createChild(cfg, position);
39924         }
39925     },
39926     /*
39927      * setHTML
39928      * @param {String} html update the Contents of the element.
39929      */
39930     setHTML : function(html)
39931     {
39932         this.fieldEl.dom.innerHTML = html;
39933     }
39934     
39935 });/*
39936  * Based on:
39937  * Ext JS Library 1.1.1
39938  * Copyright(c) 2006-2007, Ext JS, LLC.
39939  *
39940  * Originally Released Under LGPL - original licence link has changed is not relivant.
39941  *
39942  * Fork - LGPL
39943  * <script type="text/javascript">
39944  */
39945  
39946 /**
39947  * @class Roo.form.Field
39948  * @extends Roo.BoxComponent
39949  * Base class for form fields that provides default event handling, sizing, value handling and other functionality.
39950  * @constructor
39951  * Creates a new Field
39952  * @param {Object} config Configuration options
39953  */
39954 Roo.form.Field = function(config){
39955     Roo.form.Field.superclass.constructor.call(this, config);
39956 };
39957
39958 Roo.extend(Roo.form.Field, Roo.BoxComponent,  {
39959     /**
39960      * @cfg {String} fieldLabel Label to use when rendering a form.
39961      */
39962        /**
39963      * @cfg {String} qtip Mouse over tip
39964      */
39965      
39966     /**
39967      * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
39968      */
39969     invalidClass : "x-form-invalid",
39970     /**
39971      * @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")
39972      */
39973     invalidText : "The value in this field is invalid",
39974     /**
39975      * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
39976      */
39977     focusClass : "x-form-focus",
39978     /**
39979      * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
39980       automatic validation (defaults to "keyup").
39981      */
39982     validationEvent : "keyup",
39983     /**
39984      * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
39985      */
39986     validateOnBlur : true,
39987     /**
39988      * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
39989      */
39990     validationDelay : 250,
39991     /**
39992      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
39993      * {tag: "input", type: "text", size: "20", autocomplete: "off"})
39994      */
39995     defaultAutoCreate : {tag: "input", type: "text", size: "20", autocomplete: "new-password"},
39996     /**
39997      * @cfg {String} fieldClass The default CSS class for the field (defaults to "x-form-field")
39998      */
39999     fieldClass : "x-form-field",
40000     /**
40001      * @cfg {String} msgTarget The location where error text should display.  Should be one of the following values (defaults to 'qtip'):
40002      *<pre>
40003 Value         Description
40004 -----------   ----------------------------------------------------------------------
40005 qtip          Display a quick tip when the user hovers over the field
40006 title         Display a default browser title attribute popup
40007 under         Add a block div beneath the field containing the error text
40008 side          Add an error icon to the right of the field with a popup on hover
40009 [element id]  Add the error text directly to the innerHTML of the specified element
40010 </pre>
40011      */
40012     msgTarget : 'qtip',
40013     /**
40014      * @cfg {String} msgFx <b>Experimental</b> The effect used when displaying a validation message under the field (defaults to 'normal').
40015      */
40016     msgFx : 'normal',
40017
40018     /**
40019      * @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.
40020      */
40021     readOnly : false,
40022
40023     /**
40024      * @cfg {Boolean} disabled True to disable the field (defaults to false).
40025      */
40026     disabled : false,
40027
40028     /**
40029      * @cfg {String} inputType The type attribute for input fields -- e.g. radio, text, password (defaults to "text").
40030      */
40031     inputType : undefined,
40032     
40033     /**
40034      * @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).
40035          */
40036         tabIndex : undefined,
40037         
40038     // private
40039     isFormField : true,
40040
40041     // private
40042     hasFocus : false,
40043     /**
40044      * @property {Roo.Element} fieldEl
40045      * Element Containing the rendered Field (with label etc.)
40046      */
40047     /**
40048      * @cfg {Mixed} value A value to initialize this field with.
40049      */
40050     value : undefined,
40051
40052     /**
40053      * @cfg {String} name The field's HTML name attribute.
40054      */
40055     /**
40056      * @cfg {String} cls A CSS class to apply to the field's underlying element.
40057      */
40058     // private
40059     loadedValue : false,
40060      
40061      
40062         // private ??
40063         initComponent : function(){
40064         Roo.form.Field.superclass.initComponent.call(this);
40065         this.addEvents({
40066             /**
40067              * @event focus
40068              * Fires when this field receives input focus.
40069              * @param {Roo.form.Field} this
40070              */
40071             focus : true,
40072             /**
40073              * @event blur
40074              * Fires when this field loses input focus.
40075              * @param {Roo.form.Field} this
40076              */
40077             blur : true,
40078             /**
40079              * @event specialkey
40080              * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
40081              * {@link Roo.EventObject#getKey} to determine which key was pressed.
40082              * @param {Roo.form.Field} this
40083              * @param {Roo.EventObject} e The event object
40084              */
40085             specialkey : true,
40086             /**
40087              * @event change
40088              * Fires just before the field blurs if the field value has changed.
40089              * @param {Roo.form.Field} this
40090              * @param {Mixed} newValue The new value
40091              * @param {Mixed} oldValue The original value
40092              */
40093             change : true,
40094             /**
40095              * @event invalid
40096              * Fires after the field has been marked as invalid.
40097              * @param {Roo.form.Field} this
40098              * @param {String} msg The validation message
40099              */
40100             invalid : true,
40101             /**
40102              * @event valid
40103              * Fires after the field has been validated with no errors.
40104              * @param {Roo.form.Field} this
40105              */
40106             valid : true,
40107              /**
40108              * @event keyup
40109              * Fires after the key up
40110              * @param {Roo.form.Field} this
40111              * @param {Roo.EventObject}  e The event Object
40112              */
40113             keyup : true
40114         });
40115     },
40116
40117     /**
40118      * Returns the name attribute of the field if available
40119      * @return {String} name The field name
40120      */
40121     getName: function(){
40122          return this.rendered && this.el.dom.name ? this.el.dom.name : (this.hiddenName || '');
40123     },
40124
40125     // private
40126     onRender : function(ct, position){
40127         Roo.form.Field.superclass.onRender.call(this, ct, position);
40128         if(!this.el){
40129             var cfg = this.getAutoCreate();
40130             if(!cfg.name){
40131                 cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
40132             }
40133             if (!cfg.name.length) {
40134                 delete cfg.name;
40135             }
40136             if(this.inputType){
40137                 cfg.type = this.inputType;
40138             }
40139             this.el = ct.createChild(cfg, position);
40140         }
40141         var type = this.el.dom.type;
40142         if(type){
40143             if(type == 'password'){
40144                 type = 'text';
40145             }
40146             this.el.addClass('x-form-'+type);
40147         }
40148         if(this.readOnly){
40149             this.el.dom.readOnly = true;
40150         }
40151         if(this.tabIndex !== undefined){
40152             this.el.dom.setAttribute('tabIndex', this.tabIndex);
40153         }
40154
40155         this.el.addClass([this.fieldClass, this.cls]);
40156         this.initValue();
40157     },
40158
40159     /**
40160      * Apply the behaviors of this component to an existing element. <b>This is used instead of render().</b>
40161      * @param {String/HTMLElement/Element} el The id of the node, a DOM node or an existing Element
40162      * @return {Roo.form.Field} this
40163      */
40164     applyTo : function(target){
40165         this.allowDomMove = false;
40166         this.el = Roo.get(target);
40167         this.render(this.el.dom.parentNode);
40168         return this;
40169     },
40170
40171     // private
40172     initValue : function(){
40173         if(this.value !== undefined){
40174             this.setValue(this.value);
40175         }else if(this.el.dom.value.length > 0){
40176             this.setValue(this.el.dom.value);
40177         }
40178     },
40179
40180     /**
40181      * Returns true if this field has been changed since it was originally loaded and is not disabled.
40182      * DEPRICATED  - it never worked well - use hasChanged/resetHasChanged.
40183      */
40184     isDirty : function() {
40185         if(this.disabled) {
40186             return false;
40187         }
40188         return String(this.getValue()) !== String(this.originalValue);
40189     },
40190
40191     /**
40192      * stores the current value in loadedValue
40193      */
40194     resetHasChanged : function()
40195     {
40196         this.loadedValue = String(this.getValue());
40197     },
40198     /**
40199      * checks the current value against the 'loaded' value.
40200      * Note - will return false if 'resetHasChanged' has not been called first.
40201      */
40202     hasChanged : function()
40203     {
40204         if(this.disabled || this.readOnly) {
40205             return false;
40206         }
40207         return this.loadedValue !== false && String(this.getValue()) !== this.loadedValue;
40208     },
40209     
40210     
40211     
40212     // private
40213     afterRender : function(){
40214         Roo.form.Field.superclass.afterRender.call(this);
40215         this.initEvents();
40216     },
40217
40218     // private
40219     fireKey : function(e){
40220         //Roo.log('field ' + e.getKey());
40221         if(e.isNavKeyPress()){
40222             this.fireEvent("specialkey", this, e);
40223         }
40224     },
40225
40226     /**
40227      * Resets the current field value to the originally loaded value and clears any validation messages
40228      */
40229     reset : function(){
40230         this.setValue(this.resetValue);
40231         this.originalValue = this.getValue();
40232         this.clearInvalid();
40233     },
40234
40235     // private
40236     initEvents : function(){
40237         // safari killled keypress - so keydown is now used..
40238         this.el.on("keydown" , this.fireKey,  this);
40239         this.el.on("focus", this.onFocus,  this);
40240         this.el.on("blur", this.onBlur,  this);
40241         this.el.relayEvent('keyup', this);
40242
40243         // reference to original value for reset
40244         this.originalValue = this.getValue();
40245         this.resetValue =  this.getValue();
40246     },
40247
40248     // private
40249     onFocus : function(){
40250         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
40251             this.el.addClass(this.focusClass);
40252         }
40253         if(!this.hasFocus){
40254             this.hasFocus = true;
40255             this.startValue = this.getValue();
40256             this.fireEvent("focus", this);
40257         }
40258     },
40259
40260     beforeBlur : Roo.emptyFn,
40261
40262     // private
40263     onBlur : function(){
40264         this.beforeBlur();
40265         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
40266             this.el.removeClass(this.focusClass);
40267         }
40268         this.hasFocus = false;
40269         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
40270             this.validate();
40271         }
40272         var v = this.getValue();
40273         if(String(v) !== String(this.startValue)){
40274             this.fireEvent('change', this, v, this.startValue);
40275         }
40276         this.fireEvent("blur", this);
40277     },
40278
40279     /**
40280      * Returns whether or not the field value is currently valid
40281      * @param {Boolean} preventMark True to disable marking the field invalid
40282      * @return {Boolean} True if the value is valid, else false
40283      */
40284     isValid : function(preventMark){
40285         if(this.disabled){
40286             return true;
40287         }
40288         var restore = this.preventMark;
40289         this.preventMark = preventMark === true;
40290         var v = this.validateValue(this.processValue(this.getRawValue()));
40291         this.preventMark = restore;
40292         return v;
40293     },
40294
40295     /**
40296      * Validates the field value
40297      * @return {Boolean} True if the value is valid, else false
40298      */
40299     validate : function(){
40300         if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
40301             this.clearInvalid();
40302             return true;
40303         }
40304         return false;
40305     },
40306
40307     processValue : function(value){
40308         return value;
40309     },
40310
40311     // private
40312     // Subclasses should provide the validation implementation by overriding this
40313     validateValue : function(value){
40314         return true;
40315     },
40316
40317     /**
40318      * Mark this field as invalid
40319      * @param {String} msg The validation message
40320      */
40321     markInvalid : function(msg){
40322         if(!this.rendered || this.preventMark){ // not rendered
40323             return;
40324         }
40325         
40326         var obj = (typeof(this.combo) != 'undefined') ? this.combo : this; // fix the combox array!!
40327         
40328         obj.el.addClass(this.invalidClass);
40329         msg = msg || this.invalidText;
40330         switch(this.msgTarget){
40331             case 'qtip':
40332                 obj.el.dom.qtip = msg;
40333                 obj.el.dom.qclass = 'x-form-invalid-tip';
40334                 if(Roo.QuickTips){ // fix for floating editors interacting with DND
40335                     Roo.QuickTips.enable();
40336                 }
40337                 break;
40338             case 'title':
40339                 this.el.dom.title = msg;
40340                 break;
40341             case 'under':
40342                 if(!this.errorEl){
40343                     var elp = this.el.findParent('.x-form-element', 5, true);
40344                     this.errorEl = elp.createChild({cls:'x-form-invalid-msg'});
40345                     this.errorEl.setWidth(elp.getWidth(true)-20);
40346                 }
40347                 this.errorEl.update(msg);
40348                 Roo.form.Field.msgFx[this.msgFx].show(this.errorEl, this);
40349                 break;
40350             case 'side':
40351                 if(!this.errorIcon){
40352                     var elp = this.el.findParent('.x-form-element', 5, true);
40353                     this.errorIcon = elp.createChild({cls:'x-form-invalid-icon'});
40354                 }
40355                 this.alignErrorIcon();
40356                 this.errorIcon.dom.qtip = msg;
40357                 this.errorIcon.dom.qclass = 'x-form-invalid-tip';
40358                 this.errorIcon.show();
40359                 this.on('resize', this.alignErrorIcon, this);
40360                 break;
40361             default:
40362                 var t = Roo.getDom(this.msgTarget);
40363                 t.innerHTML = msg;
40364                 t.style.display = this.msgDisplay;
40365                 break;
40366         }
40367         this.fireEvent('invalid', this, msg);
40368     },
40369
40370     // private
40371     alignErrorIcon : function(){
40372         this.errorIcon.alignTo(this.el, 'tl-tr', [2, 0]);
40373     },
40374
40375     /**
40376      * Clear any invalid styles/messages for this field
40377      */
40378     clearInvalid : function(){
40379         if(!this.rendered || this.preventMark){ // not rendered
40380             return;
40381         }
40382         var obj = (typeof(this.combo) != 'undefined') ? this.combo : this; // fix the combox array!!
40383         
40384         obj.el.removeClass(this.invalidClass);
40385         switch(this.msgTarget){
40386             case 'qtip':
40387                 obj.el.dom.qtip = '';
40388                 break;
40389             case 'title':
40390                 this.el.dom.title = '';
40391                 break;
40392             case 'under':
40393                 if(this.errorEl){
40394                     Roo.form.Field.msgFx[this.msgFx].hide(this.errorEl, this);
40395                 }
40396                 break;
40397             case 'side':
40398                 if(this.errorIcon){
40399                     this.errorIcon.dom.qtip = '';
40400                     this.errorIcon.hide();
40401                     this.un('resize', this.alignErrorIcon, this);
40402                 }
40403                 break;
40404             default:
40405                 var t = Roo.getDom(this.msgTarget);
40406                 t.innerHTML = '';
40407                 t.style.display = 'none';
40408                 break;
40409         }
40410         this.fireEvent('valid', this);
40411     },
40412
40413     /**
40414      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
40415      * @return {Mixed} value The field value
40416      */
40417     getRawValue : function(){
40418         var v = this.el.getValue();
40419         
40420         return v;
40421     },
40422
40423     /**
40424      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
40425      * @return {Mixed} value The field value
40426      */
40427     getValue : function(){
40428         var v = this.el.getValue();
40429          
40430         return v;
40431     },
40432
40433     /**
40434      * Sets the underlying DOM field's value directly, bypassing validation.  To set the value with validation see {@link #setValue}.
40435      * @param {Mixed} value The value to set
40436      */
40437     setRawValue : function(v){
40438         return this.el.dom.value = (v === null || v === undefined ? '' : v);
40439     },
40440
40441     /**
40442      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
40443      * @param {Mixed} value The value to set
40444      */
40445     setValue : function(v){
40446         this.value = v;
40447         if(this.rendered){
40448             this.el.dom.value = (v === null || v === undefined ? '' : v);
40449              this.validate();
40450         }
40451     },
40452
40453     adjustSize : function(w, h){
40454         var s = Roo.form.Field.superclass.adjustSize.call(this, w, h);
40455         s.width = this.adjustWidth(this.el.dom.tagName, s.width);
40456         return s;
40457     },
40458
40459     adjustWidth : function(tag, w){
40460         tag = tag.toLowerCase();
40461         if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
40462             if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
40463                 if(tag == 'input'){
40464                     return w + 2;
40465                 }
40466                 if(tag == 'textarea'){
40467                     return w-2;
40468                 }
40469             }else if(Roo.isOpera){
40470                 if(tag == 'input'){
40471                     return w + 2;
40472                 }
40473                 if(tag == 'textarea'){
40474                     return w-2;
40475                 }
40476             }
40477         }
40478         return w;
40479     }
40480 });
40481
40482
40483 // anything other than normal should be considered experimental
40484 Roo.form.Field.msgFx = {
40485     normal : {
40486         show: function(msgEl, f){
40487             msgEl.setDisplayed('block');
40488         },
40489
40490         hide : function(msgEl, f){
40491             msgEl.setDisplayed(false).update('');
40492         }
40493     },
40494
40495     slide : {
40496         show: function(msgEl, f){
40497             msgEl.slideIn('t', {stopFx:true});
40498         },
40499
40500         hide : function(msgEl, f){
40501             msgEl.slideOut('t', {stopFx:true,useDisplay:true});
40502         }
40503     },
40504
40505     slideRight : {
40506         show: function(msgEl, f){
40507             msgEl.fixDisplay();
40508             msgEl.alignTo(f.el, 'tl-tr');
40509             msgEl.slideIn('l', {stopFx:true});
40510         },
40511
40512         hide : function(msgEl, f){
40513             msgEl.slideOut('l', {stopFx:true,useDisplay:true});
40514         }
40515     }
40516 };/*
40517  * Based on:
40518  * Ext JS Library 1.1.1
40519  * Copyright(c) 2006-2007, Ext JS, LLC.
40520  *
40521  * Originally Released Under LGPL - original licence link has changed is not relivant.
40522  *
40523  * Fork - LGPL
40524  * <script type="text/javascript">
40525  */
40526  
40527
40528 /**
40529  * @class Roo.form.TextField
40530  * @extends Roo.form.Field
40531  * Basic text field.  Can be used as a direct replacement for traditional text inputs, or as the base
40532  * class for more sophisticated input controls (like {@link Roo.form.TextArea} and {@link Roo.form.ComboBox}).
40533  * @constructor
40534  * Creates a new TextField
40535  * @param {Object} config Configuration options
40536  */
40537 Roo.form.TextField = function(config){
40538     Roo.form.TextField.superclass.constructor.call(this, config);
40539     this.addEvents({
40540         /**
40541          * @event autosize
40542          * Fires when the autosize function is triggered.  The field may or may not have actually changed size
40543          * according to the default logic, but this event provides a hook for the developer to apply additional
40544          * logic at runtime to resize the field if needed.
40545              * @param {Roo.form.Field} this This text field
40546              * @param {Number} width The new field width
40547              */
40548         autosize : true
40549     });
40550 };
40551
40552 Roo.extend(Roo.form.TextField, Roo.form.Field,  {
40553     /**
40554      * @cfg {Boolean} grow True if this field should automatically grow and shrink to its content
40555      */
40556     grow : false,
40557     /**
40558      * @cfg {Number} growMin The minimum width to allow when grow = true (defaults to 30)
40559      */
40560     growMin : 30,
40561     /**
40562      * @cfg {Number} growMax The maximum width to allow when grow = true (defaults to 800)
40563      */
40564     growMax : 800,
40565     /**
40566      * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
40567      */
40568     vtype : null,
40569     /**
40570      * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
40571      */
40572     maskRe : null,
40573     /**
40574      * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
40575      */
40576     disableKeyFilter : false,
40577     /**
40578      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
40579      */
40580     allowBlank : true,
40581     /**
40582      * @cfg {Number} minLength Minimum input field length required (defaults to 0)
40583      */
40584     minLength : 0,
40585     /**
40586      * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
40587      */
40588     maxLength : Number.MAX_VALUE,
40589     /**
40590      * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
40591      */
40592     minLengthText : "The minimum length for this field is {0}",
40593     /**
40594      * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
40595      */
40596     maxLengthText : "The maximum length for this field is {0}",
40597     /**
40598      * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
40599      */
40600     selectOnFocus : false,
40601     /**
40602      * @cfg {Boolean} allowLeadingSpace True to prevent the stripping of leading white space 
40603      */    
40604     allowLeadingSpace : false,
40605     /**
40606      * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
40607      */
40608     blankText : "This field is required",
40609     /**
40610      * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
40611      * If available, this function will be called only after the basic validators all return true, and will be passed the
40612      * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
40613      */
40614     validator : null,
40615     /**
40616      * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
40617      * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
40618      * current field value.  If the test fails, the field will be marked invalid using {@link #regexText}.
40619      */
40620     regex : null,
40621     /**
40622      * @cfg {String} regexText The error text to display if {@link #regex} is used and the test fails during validation (defaults to "")
40623      */
40624     regexText : "",
40625     /**
40626      * @cfg {String} emptyText The default text to display in an empty field - placeholder... (defaults to null).
40627      */
40628     emptyText : null,
40629    
40630
40631     // private
40632     initEvents : function()
40633     {
40634         if (this.emptyText) {
40635             this.el.attr('placeholder', this.emptyText);
40636         }
40637         
40638         Roo.form.TextField.superclass.initEvents.call(this);
40639         if(this.validationEvent == 'keyup'){
40640             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
40641             this.el.on('keyup', this.filterValidation, this);
40642         }
40643         else if(this.validationEvent !== false){
40644             this.el.on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
40645         }
40646         
40647         if(this.selectOnFocus){
40648             this.on("focus", this.preFocus, this);
40649         }
40650         if (!this.allowLeadingSpace) {
40651             this.on('blur', this.cleanLeadingSpace, this);
40652         }
40653         
40654         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
40655             this.el.on("keypress", this.filterKeys, this);
40656         }
40657         if(this.grow){
40658             this.el.on("keyup", this.onKeyUp,  this, {buffer:50});
40659             this.el.on("click", this.autoSize,  this);
40660         }
40661         if(this.el.is('input[type=password]') && Roo.isSafari){
40662             this.el.on('keydown', this.SafariOnKeyDown, this);
40663         }
40664     },
40665
40666     processValue : function(value){
40667         if(this.stripCharsRe){
40668             var newValue = value.replace(this.stripCharsRe, '');
40669             if(newValue !== value){
40670                 this.setRawValue(newValue);
40671                 return newValue;
40672             }
40673         }
40674         return value;
40675     },
40676
40677     filterValidation : function(e){
40678         if(!e.isNavKeyPress()){
40679             this.validationTask.delay(this.validationDelay);
40680         }
40681     },
40682
40683     // private
40684     onKeyUp : function(e){
40685         if(!e.isNavKeyPress()){
40686             this.autoSize();
40687         }
40688     },
40689     // private - clean the leading white space
40690     cleanLeadingSpace : function(e)
40691     {
40692         if ( this.inputType == 'file') {
40693             return;
40694         }
40695         
40696         this.setValue((this.getValue() + '').replace(/^\s+/,''));
40697     },
40698     /**
40699      * Resets the current field value to the originally-loaded value and clears any validation messages.
40700      *  
40701      */
40702     reset : function(){
40703         Roo.form.TextField.superclass.reset.call(this);
40704        
40705     }, 
40706     // private
40707     preFocus : function(){
40708         
40709         if(this.selectOnFocus){
40710             this.el.dom.select();
40711         }
40712     },
40713
40714     
40715     // private
40716     filterKeys : function(e){
40717         var k = e.getKey();
40718         if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
40719             return;
40720         }
40721         var c = e.getCharCode(), cc = String.fromCharCode(c);
40722         if(Roo.isIE && (e.isSpecialKey() || !cc)){
40723             return;
40724         }
40725         if(!this.maskRe.test(cc)){
40726             e.stopEvent();
40727         }
40728     },
40729
40730     setValue : function(v){
40731         
40732         Roo.form.TextField.superclass.setValue.apply(this, arguments);
40733         
40734         this.autoSize();
40735     },
40736
40737     /**
40738      * Validates a value according to the field's validation rules and marks the field as invalid
40739      * if the validation fails
40740      * @param {Mixed} value The value to validate
40741      * @return {Boolean} True if the value is valid, else false
40742      */
40743     validateValue : function(value){
40744         if(value.length < 1)  { // if it's blank
40745              if(this.allowBlank){
40746                 this.clearInvalid();
40747                 return true;
40748              }else{
40749                 this.markInvalid(this.blankText);
40750                 return false;
40751              }
40752         }
40753         if(value.length < this.minLength){
40754             this.markInvalid(String.format(this.minLengthText, this.minLength));
40755             return false;
40756         }
40757         if(value.length > this.maxLength){
40758             this.markInvalid(String.format(this.maxLengthText, this.maxLength));
40759             return false;
40760         }
40761         if(this.vtype){
40762             var vt = Roo.form.VTypes;
40763             if(!vt[this.vtype](value, this)){
40764                 this.markInvalid(this.vtypeText || vt[this.vtype +'Text']);
40765                 return false;
40766             }
40767         }
40768         if(typeof this.validator == "function"){
40769             var msg = this.validator(value);
40770             if(msg !== true){
40771                 this.markInvalid(msg);
40772                 return false;
40773             }
40774         }
40775         if(this.regex && !this.regex.test(value)){
40776             this.markInvalid(this.regexText);
40777             return false;
40778         }
40779         return true;
40780     },
40781
40782     /**
40783      * Selects text in this field
40784      * @param {Number} start (optional) The index where the selection should start (defaults to 0)
40785      * @param {Number} end (optional) The index where the selection should end (defaults to the text length)
40786      */
40787     selectText : function(start, end){
40788         var v = this.getRawValue();
40789         if(v.length > 0){
40790             start = start === undefined ? 0 : start;
40791             end = end === undefined ? v.length : end;
40792             var d = this.el.dom;
40793             if(d.setSelectionRange){
40794                 d.setSelectionRange(start, end);
40795             }else if(d.createTextRange){
40796                 var range = d.createTextRange();
40797                 range.moveStart("character", start);
40798                 range.moveEnd("character", v.length-end);
40799                 range.select();
40800             }
40801         }
40802     },
40803
40804     /**
40805      * Automatically grows the field to accomodate the width of the text up to the maximum field width allowed.
40806      * This only takes effect if grow = true, and fires the autosize event.
40807      */
40808     autoSize : function(){
40809         if(!this.grow || !this.rendered){
40810             return;
40811         }
40812         if(!this.metrics){
40813             this.metrics = Roo.util.TextMetrics.createInstance(this.el);
40814         }
40815         var el = this.el;
40816         var v = el.dom.value;
40817         var d = document.createElement('div');
40818         d.appendChild(document.createTextNode(v));
40819         v = d.innerHTML;
40820         d = null;
40821         v += "&#160;";
40822         var w = Math.min(this.growMax, Math.max(this.metrics.getWidth(v) + /* add extra padding */ 10, this.growMin));
40823         this.el.setWidth(w);
40824         this.fireEvent("autosize", this, w);
40825     },
40826     
40827     // private
40828     SafariOnKeyDown : function(event)
40829     {
40830         // this is a workaround for a password hang bug on chrome/ webkit.
40831         
40832         var isSelectAll = false;
40833         
40834         if(this.el.dom.selectionEnd > 0){
40835             isSelectAll = (this.el.dom.selectionEnd - this.el.dom.selectionStart - this.getValue().length == 0) ? true : false;
40836         }
40837         if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
40838             event.preventDefault();
40839             this.setValue('');
40840             return;
40841         }
40842         
40843         if(isSelectAll && event.getCharCode() > 31){ // backspace and delete key
40844             
40845             event.preventDefault();
40846             // this is very hacky as keydown always get's upper case.
40847             
40848             var cc = String.fromCharCode(event.getCharCode());
40849             
40850             
40851             this.setValue( event.shiftKey ?  cc : cc.toLowerCase());
40852             
40853         }
40854         
40855         
40856     }
40857 });/*
40858  * Based on:
40859  * Ext JS Library 1.1.1
40860  * Copyright(c) 2006-2007, Ext JS, LLC.
40861  *
40862  * Originally Released Under LGPL - original licence link has changed is not relivant.
40863  *
40864  * Fork - LGPL
40865  * <script type="text/javascript">
40866  */
40867  
40868 /**
40869  * @class Roo.form.Hidden
40870  * @extends Roo.form.TextField
40871  * Simple Hidden element used on forms 
40872  * 
40873  * usage: form.add(new Roo.form.HiddenField({ 'name' : 'test1' }));
40874  * 
40875  * @constructor
40876  * Creates a new Hidden form element.
40877  * @param {Object} config Configuration options
40878  */
40879
40880
40881
40882 // easy hidden field...
40883 Roo.form.Hidden = function(config){
40884     Roo.form.Hidden.superclass.constructor.call(this, config);
40885 };
40886   
40887 Roo.extend(Roo.form.Hidden, Roo.form.TextField, {
40888     fieldLabel:      '',
40889     inputType:      'hidden',
40890     width:          50,
40891     allowBlank:     true,
40892     labelSeparator: '',
40893     hidden:         true,
40894     itemCls :       'x-form-item-display-none'
40895
40896
40897 });
40898
40899
40900 /*
40901  * Based on:
40902  * Ext JS Library 1.1.1
40903  * Copyright(c) 2006-2007, Ext JS, LLC.
40904  *
40905  * Originally Released Under LGPL - original licence link has changed is not relivant.
40906  *
40907  * Fork - LGPL
40908  * <script type="text/javascript">
40909  */
40910  
40911 /**
40912  * @class Roo.form.TriggerField
40913  * @extends Roo.form.TextField
40914  * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
40915  * The trigger has no default action, so you must assign a function to implement the trigger click handler by
40916  * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
40917  * for which you can provide a custom implementation.  For example:
40918  * <pre><code>
40919 var trigger = new Roo.form.TriggerField();
40920 trigger.onTriggerClick = myTriggerFn;
40921 trigger.applyTo('my-field');
40922 </code></pre>
40923  *
40924  * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
40925  * {@link Roo.form.DateField} and {@link Roo.form.ComboBox} are perfect examples of this.
40926  * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
40927  * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
40928  * @constructor
40929  * Create a new TriggerField.
40930  * @param {Object} config Configuration options (valid {@Roo.form.TextField} config options will also be applied
40931  * to the base TextField)
40932  */
40933 Roo.form.TriggerField = function(config){
40934     this.mimicing = false;
40935     Roo.form.TriggerField.superclass.constructor.call(this, config);
40936 };
40937
40938 Roo.extend(Roo.form.TriggerField, Roo.form.TextField,  {
40939     /**
40940      * @cfg {String} triggerClass A CSS class to apply to the trigger
40941      */
40942     /**
40943      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
40944      * {tag: "input", type: "text", size: "16", autocomplete: "off"})
40945      */
40946     defaultAutoCreate : {tag: "input", type: "text", size: "16", autocomplete: "new-password"},
40947     /**
40948      * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
40949      */
40950     hideTrigger:false,
40951
40952     /** @cfg {Boolean} grow @hide */
40953     /** @cfg {Number} growMin @hide */
40954     /** @cfg {Number} growMax @hide */
40955
40956     /**
40957      * @hide 
40958      * @method
40959      */
40960     autoSize: Roo.emptyFn,
40961     // private
40962     monitorTab : true,
40963     // private
40964     deferHeight : true,
40965
40966     
40967     actionMode : 'wrap',
40968     // private
40969     onResize : function(w, h){
40970         Roo.form.TriggerField.superclass.onResize.apply(this, arguments);
40971         if(typeof w == 'number'){
40972             var x = w - this.trigger.getWidth();
40973             this.el.setWidth(this.adjustWidth('input', x));
40974             this.trigger.setStyle('left', x+'px');
40975         }
40976     },
40977
40978     // private
40979     adjustSize : Roo.BoxComponent.prototype.adjustSize,
40980
40981     // private
40982     getResizeEl : function(){
40983         return this.wrap;
40984     },
40985
40986     // private
40987     getPositionEl : function(){
40988         return this.wrap;
40989     },
40990
40991     // private
40992     alignErrorIcon : function(){
40993         this.errorIcon.alignTo(this.wrap, 'tl-tr', [2, 0]);
40994     },
40995
40996     // private
40997     onRender : function(ct, position){
40998         Roo.form.TriggerField.superclass.onRender.call(this, ct, position);
40999         this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
41000         this.trigger = this.wrap.createChild(this.triggerConfig ||
41001                 {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.triggerClass});
41002         if(this.hideTrigger){
41003             this.trigger.setDisplayed(false);
41004         }
41005         this.initTrigger();
41006         if(!this.width){
41007             this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
41008         }
41009     },
41010
41011     // private
41012     initTrigger : function(){
41013         this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
41014         this.trigger.addClassOnOver('x-form-trigger-over');
41015         this.trigger.addClassOnClick('x-form-trigger-click');
41016     },
41017
41018     // private
41019     onDestroy : function(){
41020         if(this.trigger){
41021             this.trigger.removeAllListeners();
41022             this.trigger.remove();
41023         }
41024         if(this.wrap){
41025             this.wrap.remove();
41026         }
41027         Roo.form.TriggerField.superclass.onDestroy.call(this);
41028     },
41029
41030     // private
41031     onFocus : function(){
41032         Roo.form.TriggerField.superclass.onFocus.call(this);
41033         if(!this.mimicing){
41034             this.wrap.addClass('x-trigger-wrap-focus');
41035             this.mimicing = true;
41036             Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
41037             if(this.monitorTab){
41038                 this.el.on("keydown", this.checkTab, this);
41039             }
41040         }
41041     },
41042
41043     // private
41044     checkTab : function(e){
41045         if(e.getKey() == e.TAB){
41046             this.triggerBlur();
41047         }
41048     },
41049
41050     // private
41051     onBlur : function(){
41052         // do nothing
41053     },
41054
41055     // private
41056     mimicBlur : function(e, t){
41057         if(!this.wrap.contains(t) && this.validateBlur()){
41058             this.triggerBlur();
41059         }
41060     },
41061
41062     // private
41063     triggerBlur : function(){
41064         this.mimicing = false;
41065         Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
41066         if(this.monitorTab){
41067             this.el.un("keydown", this.checkTab, this);
41068         }
41069         this.wrap.removeClass('x-trigger-wrap-focus');
41070         Roo.form.TriggerField.superclass.onBlur.call(this);
41071     },
41072
41073     // private
41074     // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
41075     validateBlur : function(e, t){
41076         return true;
41077     },
41078
41079     // private
41080     onDisable : function(){
41081         Roo.form.TriggerField.superclass.onDisable.call(this);
41082         if(this.wrap){
41083             this.wrap.addClass('x-item-disabled');
41084         }
41085     },
41086
41087     // private
41088     onEnable : function(){
41089         Roo.form.TriggerField.superclass.onEnable.call(this);
41090         if(this.wrap){
41091             this.wrap.removeClass('x-item-disabled');
41092         }
41093     },
41094
41095     // private
41096     onShow : function(){
41097         var ae = this.getActionEl();
41098         
41099         if(ae){
41100             ae.dom.style.display = '';
41101             ae.dom.style.visibility = 'visible';
41102         }
41103     },
41104
41105     // private
41106     
41107     onHide : function(){
41108         var ae = this.getActionEl();
41109         ae.dom.style.display = 'none';
41110     },
41111
41112     /**
41113      * The function that should handle the trigger's click event.  This method does nothing by default until overridden
41114      * by an implementing function.
41115      * @method
41116      * @param {EventObject} e
41117      */
41118     onTriggerClick : Roo.emptyFn
41119 });
41120
41121 // TwinTriggerField is not a public class to be used directly.  It is meant as an abstract base class
41122 // to be extended by an implementing class.  For an example of implementing this class, see the custom
41123 // SearchField implementation here: http://extjs.com/deploy/ext/examples/form/custom.html
41124 Roo.form.TwinTriggerField = Roo.extend(Roo.form.TriggerField, {
41125     initComponent : function(){
41126         Roo.form.TwinTriggerField.superclass.initComponent.call(this);
41127
41128         this.triggerConfig = {
41129             tag:'span', cls:'x-form-twin-triggers', cn:[
41130             {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.trigger1Class},
41131             {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.trigger2Class}
41132         ]};
41133     },
41134
41135     getTrigger : function(index){
41136         return this.triggers[index];
41137     },
41138
41139     initTrigger : function(){
41140         var ts = this.trigger.select('.x-form-trigger', true);
41141         this.wrap.setStyle('overflow', 'hidden');
41142         var triggerField = this;
41143         ts.each(function(t, all, index){
41144             t.hide = function(){
41145                 var w = triggerField.wrap.getWidth();
41146                 this.dom.style.display = 'none';
41147                 triggerField.el.setWidth(w-triggerField.trigger.getWidth());
41148             };
41149             t.show = function(){
41150                 var w = triggerField.wrap.getWidth();
41151                 this.dom.style.display = '';
41152                 triggerField.el.setWidth(w-triggerField.trigger.getWidth());
41153             };
41154             var triggerIndex = 'Trigger'+(index+1);
41155
41156             if(this['hide'+triggerIndex]){
41157                 t.dom.style.display = 'none';
41158             }
41159             t.on("click", this['on'+triggerIndex+'Click'], this, {preventDefault:true});
41160             t.addClassOnOver('x-form-trigger-over');
41161             t.addClassOnClick('x-form-trigger-click');
41162         }, this);
41163         this.triggers = ts.elements;
41164     },
41165
41166     onTrigger1Click : Roo.emptyFn,
41167     onTrigger2Click : Roo.emptyFn
41168 });/*
41169  * Based on:
41170  * Ext JS Library 1.1.1
41171  * Copyright(c) 2006-2007, Ext JS, LLC.
41172  *
41173  * Originally Released Under LGPL - original licence link has changed is not relivant.
41174  *
41175  * Fork - LGPL
41176  * <script type="text/javascript">
41177  */
41178  
41179 /**
41180  * @class Roo.form.TextArea
41181  * @extends Roo.form.TextField
41182  * Multiline text field.  Can be used as a direct replacement for traditional textarea fields, plus adds
41183  * support for auto-sizing.
41184  * @constructor
41185  * Creates a new TextArea
41186  * @param {Object} config Configuration options
41187  */
41188 Roo.form.TextArea = function(config){
41189     Roo.form.TextArea.superclass.constructor.call(this, config);
41190     // these are provided exchanges for backwards compat
41191     // minHeight/maxHeight were replaced by growMin/growMax to be
41192     // compatible with TextField growing config values
41193     if(this.minHeight !== undefined){
41194         this.growMin = this.minHeight;
41195     }
41196     if(this.maxHeight !== undefined){
41197         this.growMax = this.maxHeight;
41198     }
41199 };
41200
41201 Roo.extend(Roo.form.TextArea, Roo.form.TextField,  {
41202     /**
41203      * @cfg {Number} growMin The minimum height to allow when grow = true (defaults to 60)
41204      */
41205     growMin : 60,
41206     /**
41207      * @cfg {Number} growMax The maximum height to allow when grow = true (defaults to 1000)
41208      */
41209     growMax: 1000,
41210     /**
41211      * @cfg {Boolean} preventScrollbars True to prevent scrollbars from appearing regardless of how much text is
41212      * in the field (equivalent to setting overflow: hidden, defaults to false)
41213      */
41214     preventScrollbars: false,
41215     /**
41216      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
41217      * {tag: "textarea", style: "width:300px;height:60px;", autocomplete: "off"})
41218      */
41219
41220     // private
41221     onRender : function(ct, position){
41222         if(!this.el){
41223             this.defaultAutoCreate = {
41224                 tag: "textarea",
41225                 style:"width:300px;height:60px;",
41226                 autocomplete: "new-password"
41227             };
41228         }
41229         Roo.form.TextArea.superclass.onRender.call(this, ct, position);
41230         if(this.grow){
41231             this.textSizeEl = Roo.DomHelper.append(document.body, {
41232                 tag: "pre", cls: "x-form-grow-sizer"
41233             });
41234             if(this.preventScrollbars){
41235                 this.el.setStyle("overflow", "hidden");
41236             }
41237             this.el.setHeight(this.growMin);
41238         }
41239     },
41240
41241     onDestroy : function(){
41242         if(this.textSizeEl){
41243             this.textSizeEl.parentNode.removeChild(this.textSizeEl);
41244         }
41245         Roo.form.TextArea.superclass.onDestroy.call(this);
41246     },
41247
41248     // private
41249     onKeyUp : function(e){
41250         if(!e.isNavKeyPress() || e.getKey() == e.ENTER){
41251             this.autoSize();
41252         }
41253     },
41254
41255     /**
41256      * Automatically grows the field to accomodate the height of the text up to the maximum field height allowed.
41257      * This only takes effect if grow = true, and fires the autosize event if the height changes.
41258      */
41259     autoSize : function(){
41260         if(!this.grow || !this.textSizeEl){
41261             return;
41262         }
41263         var el = this.el;
41264         var v = el.dom.value;
41265         var ts = this.textSizeEl;
41266
41267         ts.innerHTML = '';
41268         ts.appendChild(document.createTextNode(v));
41269         v = ts.innerHTML;
41270
41271         Roo.fly(ts).setWidth(this.el.getWidth());
41272         if(v.length < 1){
41273             v = "&#160;&#160;";
41274         }else{
41275             if(Roo.isIE){
41276                 v = v.replace(/\n/g, '<p>&#160;</p>');
41277             }
41278             v += "&#160;\n&#160;";
41279         }
41280         ts.innerHTML = v;
41281         var h = Math.min(this.growMax, Math.max(ts.offsetHeight, this.growMin));
41282         if(h != this.lastHeight){
41283             this.lastHeight = h;
41284             this.el.setHeight(h);
41285             this.fireEvent("autosize", this, h);
41286         }
41287     }
41288 });/*
41289  * Based on:
41290  * Ext JS Library 1.1.1
41291  * Copyright(c) 2006-2007, Ext JS, LLC.
41292  *
41293  * Originally Released Under LGPL - original licence link has changed is not relivant.
41294  *
41295  * Fork - LGPL
41296  * <script type="text/javascript">
41297  */
41298  
41299
41300 /**
41301  * @class Roo.form.NumberField
41302  * @extends Roo.form.TextField
41303  * Numeric text field that provides automatic keystroke filtering and numeric validation.
41304  * @constructor
41305  * Creates a new NumberField
41306  * @param {Object} config Configuration options
41307  */
41308 Roo.form.NumberField = function(config){
41309     Roo.form.NumberField.superclass.constructor.call(this, config);
41310 };
41311
41312 Roo.extend(Roo.form.NumberField, Roo.form.TextField,  {
41313     /**
41314      * @cfg {String} fieldClass The default CSS class for the field (defaults to "x-form-field x-form-num-field")
41315      */
41316     fieldClass: "x-form-field x-form-num-field",
41317     /**
41318      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
41319      */
41320     allowDecimals : true,
41321     /**
41322      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
41323      */
41324     decimalSeparator : ".",
41325     /**
41326      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
41327      */
41328     decimalPrecision : 2,
41329     /**
41330      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
41331      */
41332     allowNegative : true,
41333     /**
41334      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
41335      */
41336     minValue : Number.NEGATIVE_INFINITY,
41337     /**
41338      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
41339      */
41340     maxValue : Number.MAX_VALUE,
41341     /**
41342      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
41343      */
41344     minText : "The minimum value for this field is {0}",
41345     /**
41346      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
41347      */
41348     maxText : "The maximum value for this field is {0}",
41349     /**
41350      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
41351      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
41352      */
41353     nanText : "{0} is not a valid number",
41354
41355     // private
41356     initEvents : function(){
41357         Roo.form.NumberField.superclass.initEvents.call(this);
41358         var allowed = "0123456789";
41359         if(this.allowDecimals){
41360             allowed += this.decimalSeparator;
41361         }
41362         if(this.allowNegative){
41363             allowed += "-";
41364         }
41365         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
41366         var keyPress = function(e){
41367             var k = e.getKey();
41368             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
41369                 return;
41370             }
41371             var c = e.getCharCode();
41372             if(allowed.indexOf(String.fromCharCode(c)) === -1){
41373                 e.stopEvent();
41374             }
41375         };
41376         this.el.on("keypress", keyPress, this);
41377     },
41378
41379     // private
41380     validateValue : function(value){
41381         if(!Roo.form.NumberField.superclass.validateValue.call(this, value)){
41382             return false;
41383         }
41384         if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
41385              return true;
41386         }
41387         var num = this.parseValue(value);
41388         if(isNaN(num)){
41389             this.markInvalid(String.format(this.nanText, value));
41390             return false;
41391         }
41392         if(num < this.minValue){
41393             this.markInvalid(String.format(this.minText, this.minValue));
41394             return false;
41395         }
41396         if(num > this.maxValue){
41397             this.markInvalid(String.format(this.maxText, this.maxValue));
41398             return false;
41399         }
41400         return true;
41401     },
41402
41403     getValue : function(){
41404         return this.fixPrecision(this.parseValue(Roo.form.NumberField.superclass.getValue.call(this)));
41405     },
41406
41407     // private
41408     parseValue : function(value){
41409         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
41410         return isNaN(value) ? '' : value;
41411     },
41412
41413     // private
41414     fixPrecision : function(value){
41415         var nan = isNaN(value);
41416         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
41417             return nan ? '' : value;
41418         }
41419         return parseFloat(value).toFixed(this.decimalPrecision);
41420     },
41421
41422     setValue : function(v){
41423         v = this.fixPrecision(v);
41424         Roo.form.NumberField.superclass.setValue.call(this, String(v).replace(".", this.decimalSeparator));
41425     },
41426
41427     // private
41428     decimalPrecisionFcn : function(v){
41429         return Math.floor(v);
41430     },
41431
41432     beforeBlur : function(){
41433         var v = this.parseValue(this.getRawValue());
41434         if(v){
41435             this.setValue(v);
41436         }
41437     }
41438 });/*
41439  * Based on:
41440  * Ext JS Library 1.1.1
41441  * Copyright(c) 2006-2007, Ext JS, LLC.
41442  *
41443  * Originally Released Under LGPL - original licence link has changed is not relivant.
41444  *
41445  * Fork - LGPL
41446  * <script type="text/javascript">
41447  */
41448  
41449 /**
41450  * @class Roo.form.DateField
41451  * @extends Roo.form.TriggerField
41452  * Provides a date input field with a {@link Roo.DatePicker} dropdown and automatic date validation.
41453 * @constructor
41454 * Create a new DateField
41455 * @param {Object} config
41456  */
41457 Roo.form.DateField = function(config)
41458 {
41459     Roo.form.DateField.superclass.constructor.call(this, config);
41460     
41461       this.addEvents({
41462          
41463         /**
41464          * @event select
41465          * Fires when a date is selected
41466              * @param {Roo.form.DateField} combo This combo box
41467              * @param {Date} date The date selected
41468              */
41469         'select' : true
41470          
41471     });
41472     
41473     
41474     if(typeof this.minValue == "string") {
41475         this.minValue = this.parseDate(this.minValue);
41476     }
41477     if(typeof this.maxValue == "string") {
41478         this.maxValue = this.parseDate(this.maxValue);
41479     }
41480     this.ddMatch = null;
41481     if(this.disabledDates){
41482         var dd = this.disabledDates;
41483         var re = "(?:";
41484         for(var i = 0; i < dd.length; i++){
41485             re += dd[i];
41486             if(i != dd.length-1) {
41487                 re += "|";
41488             }
41489         }
41490         this.ddMatch = new RegExp(re + ")");
41491     }
41492 };
41493
41494 Roo.extend(Roo.form.DateField, Roo.form.TriggerField,  {
41495     /**
41496      * @cfg {String} format
41497      * The default date format string which can be overriden for localization support.  The format must be
41498      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
41499      */
41500     format : "m/d/y",
41501     /**
41502      * @cfg {String} altFormats
41503      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
41504      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
41505      */
41506     altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
41507     /**
41508      * @cfg {Array} disabledDays
41509      * An array of days to disable, 0 based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
41510      */
41511     disabledDays : null,
41512     /**
41513      * @cfg {String} disabledDaysText
41514      * The tooltip to display when the date falls on a disabled day (defaults to 'Disabled')
41515      */
41516     disabledDaysText : "Disabled",
41517     /**
41518      * @cfg {Array} disabledDates
41519      * An array of "dates" to disable, as strings. These strings will be used to build a dynamic regular
41520      * expression so they are very powerful. Some examples:
41521      * <ul>
41522      * <li>["03/08/2003", "09/16/2003"] would disable those exact dates</li>
41523      * <li>["03/08", "09/16"] would disable those days for every year</li>
41524      * <li>["^03/08"] would only match the beginning (useful if you are using short years)</li>
41525      * <li>["03/../2006"] would disable every day in March 2006</li>
41526      * <li>["^03"] would disable every day in every March</li>
41527      * </ul>
41528      * In order to support regular expressions, if you are using a date format that has "." in it, you will have to
41529      * escape the dot when restricting dates. For example: ["03\\.08\\.03"].
41530      */
41531     disabledDates : null,
41532     /**
41533      * @cfg {String} disabledDatesText
41534      * The tooltip text to display when the date falls on a disabled date (defaults to 'Disabled')
41535      */
41536     disabledDatesText : "Disabled",
41537     /**
41538      * @cfg {Date/String} minValue
41539      * The minimum allowed date. Can be either a Javascript date object or a string date in a
41540      * valid format (defaults to null).
41541      */
41542     minValue : null,
41543     /**
41544      * @cfg {Date/String} maxValue
41545      * The maximum allowed date. Can be either a Javascript date object or a string date in a
41546      * valid format (defaults to null).
41547      */
41548     maxValue : null,
41549     /**
41550      * @cfg {String} minText
41551      * The error text to display when the date in the cell is before minValue (defaults to
41552      * 'The date in this field must be after {minValue}').
41553      */
41554     minText : "The date in this field must be equal to or after {0}",
41555     /**
41556      * @cfg {String} maxText
41557      * The error text to display when the date in the cell is after maxValue (defaults to
41558      * 'The date in this field must be before {maxValue}').
41559      */
41560     maxText : "The date in this field must be equal to or before {0}",
41561     /**
41562      * @cfg {String} invalidText
41563      * The error text to display when the date in the field is invalid (defaults to
41564      * '{value} is not a valid date - it must be in the format {format}').
41565      */
41566     invalidText : "{0} is not a valid date - it must be in the format {1}",
41567     /**
41568      * @cfg {String} triggerClass
41569      * An additional CSS class used to style the trigger button.  The trigger will always get the
41570      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-date-trigger'
41571      * which displays a calendar icon).
41572      */
41573     triggerClass : 'x-form-date-trigger',
41574     
41575
41576     /**
41577      * @cfg {Boolean} useIso
41578      * if enabled, then the date field will use a hidden field to store the 
41579      * real value as iso formated date. default (false)
41580      */ 
41581     useIso : false,
41582     /**
41583      * @cfg {String/Object} autoCreate
41584      * A DomHelper element spec, or true for a default element spec (defaults to
41585      * {tag: "input", type: "text", size: "10", autocomplete: "off"})
41586      */ 
41587     // private
41588     defaultAutoCreate : {tag: "input", type: "text", size: "10", autocomplete: "off"},
41589     
41590     // private
41591     hiddenField: false,
41592     
41593     onRender : function(ct, position)
41594     {
41595         Roo.form.DateField.superclass.onRender.call(this, ct, position);
41596         if (this.useIso) {
41597             //this.el.dom.removeAttribute('name'); 
41598             Roo.log("Changing name?");
41599             this.el.dom.setAttribute('name', this.name + '____hidden___' ); 
41600             this.hiddenField = this.el.insertSibling({ tag:'input', type:'hidden', name: this.name },
41601                     'before', true);
41602             this.hiddenField.value = this.value ? this.formatDate(this.value, 'Y-m-d') : '';
41603             // prevent input submission
41604             this.hiddenName = this.name;
41605         }
41606             
41607             
41608     },
41609     
41610     // private
41611     validateValue : function(value)
41612     {
41613         value = this.formatDate(value);
41614         if(!Roo.form.DateField.superclass.validateValue.call(this, value)){
41615             Roo.log('super failed');
41616             return false;
41617         }
41618         if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
41619              return true;
41620         }
41621         var svalue = value;
41622         value = this.parseDate(value);
41623         if(!value){
41624             Roo.log('parse date failed' + svalue);
41625             this.markInvalid(String.format(this.invalidText, svalue, this.format));
41626             return false;
41627         }
41628         var time = value.getTime();
41629         if(this.minValue && time < this.minValue.getTime()){
41630             this.markInvalid(String.format(this.minText, this.formatDate(this.minValue)));
41631             return false;
41632         }
41633         if(this.maxValue && time > this.maxValue.getTime()){
41634             this.markInvalid(String.format(this.maxText, this.formatDate(this.maxValue)));
41635             return false;
41636         }
41637         if(this.disabledDays){
41638             var day = value.getDay();
41639             for(var i = 0; i < this.disabledDays.length; i++) {
41640                 if(day === this.disabledDays[i]){
41641                     this.markInvalid(this.disabledDaysText);
41642                     return false;
41643                 }
41644             }
41645         }
41646         var fvalue = this.formatDate(value);
41647         if(this.ddMatch && this.ddMatch.test(fvalue)){
41648             this.markInvalid(String.format(this.disabledDatesText, fvalue));
41649             return false;
41650         }
41651         return true;
41652     },
41653
41654     // private
41655     // Provides logic to override the default TriggerField.validateBlur which just returns true
41656     validateBlur : function(){
41657         return !this.menu || !this.menu.isVisible();
41658     },
41659     
41660     getName: function()
41661     {
41662         // returns hidden if it's set..
41663         if (!this.rendered) {return ''};
41664         return !this.hiddenName && this.el.dom.name  ? this.el.dom.name : (this.hiddenName || '');
41665         
41666     },
41667
41668     /**
41669      * Returns the current date value of the date field.
41670      * @return {Date} The date value
41671      */
41672     getValue : function(){
41673         
41674         return  this.hiddenField ?
41675                 this.hiddenField.value :
41676                 this.parseDate(Roo.form.DateField.superclass.getValue.call(this)) || "";
41677     },
41678
41679     /**
41680      * Sets the value of the date field.  You can pass a date object or any string that can be parsed into a valid
41681      * date, using DateField.format as the date format, according to the same rules as {@link Date#parseDate}
41682      * (the default format used is "m/d/y").
41683      * <br />Usage:
41684      * <pre><code>
41685 //All of these calls set the same date value (May 4, 2006)
41686
41687 //Pass a date object:
41688 var dt = new Date('5/4/06');
41689 dateField.setValue(dt);
41690
41691 //Pass a date string (default format):
41692 dateField.setValue('5/4/06');
41693
41694 //Pass a date string (custom format):
41695 dateField.format = 'Y-m-d';
41696 dateField.setValue('2006-5-4');
41697 </code></pre>
41698      * @param {String/Date} date The date or valid date string
41699      */
41700     setValue : function(date){
41701         if (this.hiddenField) {
41702             this.hiddenField.value = this.formatDate(this.parseDate(date), 'Y-m-d');
41703         }
41704         Roo.form.DateField.superclass.setValue.call(this, this.formatDate(this.parseDate(date)));
41705         // make sure the value field is always stored as a date..
41706         this.value = this.parseDate(date);
41707         
41708         
41709     },
41710
41711     // private
41712     parseDate : function(value){
41713         if(!value || value instanceof Date){
41714             return value;
41715         }
41716         var v = Date.parseDate(value, this.format);
41717          if (!v && this.useIso) {
41718             v = Date.parseDate(value, 'Y-m-d');
41719         }
41720         if(!v && this.altFormats){
41721             if(!this.altFormatsArray){
41722                 this.altFormatsArray = this.altFormats.split("|");
41723             }
41724             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
41725                 v = Date.parseDate(value, this.altFormatsArray[i]);
41726             }
41727         }
41728         return v;
41729     },
41730
41731     // private
41732     formatDate : function(date, fmt){
41733         return (!date || !(date instanceof Date)) ?
41734                date : date.dateFormat(fmt || this.format);
41735     },
41736
41737     // private
41738     menuListeners : {
41739         select: function(m, d){
41740             
41741             this.setValue(d);
41742             this.fireEvent('select', this, d);
41743         },
41744         show : function(){ // retain focus styling
41745             this.onFocus();
41746         },
41747         hide : function(){
41748             this.focus.defer(10, this);
41749             var ml = this.menuListeners;
41750             this.menu.un("select", ml.select,  this);
41751             this.menu.un("show", ml.show,  this);
41752             this.menu.un("hide", ml.hide,  this);
41753         }
41754     },
41755
41756     // private
41757     // Implements the default empty TriggerField.onTriggerClick function to display the DatePicker
41758     onTriggerClick : function(){
41759         if(this.disabled){
41760             return;
41761         }
41762         if(this.menu == null){
41763             this.menu = new Roo.menu.DateMenu();
41764         }
41765         Roo.apply(this.menu.picker,  {
41766             showClear: this.allowBlank,
41767             minDate : this.minValue,
41768             maxDate : this.maxValue,
41769             disabledDatesRE : this.ddMatch,
41770             disabledDatesText : this.disabledDatesText,
41771             disabledDays : this.disabledDays,
41772             disabledDaysText : this.disabledDaysText,
41773             format : this.useIso ? 'Y-m-d' : this.format,
41774             minText : String.format(this.minText, this.formatDate(this.minValue)),
41775             maxText : String.format(this.maxText, this.formatDate(this.maxValue))
41776         });
41777         this.menu.on(Roo.apply({}, this.menuListeners, {
41778             scope:this
41779         }));
41780         this.menu.picker.setValue(this.getValue() || new Date());
41781         this.menu.show(this.el, "tl-bl?");
41782     },
41783
41784     beforeBlur : function(){
41785         var v = this.parseDate(this.getRawValue());
41786         if(v){
41787             this.setValue(v);
41788         }
41789     },
41790
41791     /*@
41792      * overide
41793      * 
41794      */
41795     isDirty : function() {
41796         if(this.disabled) {
41797             return false;
41798         }
41799         
41800         if(typeof(this.startValue) === 'undefined'){
41801             return false;
41802         }
41803         
41804         return String(this.getValue()) !== String(this.startValue);
41805         
41806     },
41807     // @overide
41808     cleanLeadingSpace : function(e)
41809     {
41810        return;
41811     }
41812     
41813 });/*
41814  * Based on:
41815  * Ext JS Library 1.1.1
41816  * Copyright(c) 2006-2007, Ext JS, LLC.
41817  *
41818  * Originally Released Under LGPL - original licence link has changed is not relivant.
41819  *
41820  * Fork - LGPL
41821  * <script type="text/javascript">
41822  */
41823  
41824 /**
41825  * @class Roo.form.MonthField
41826  * @extends Roo.form.TriggerField
41827  * Provides a date input field with a {@link Roo.DatePicker} dropdown and automatic date validation.
41828 * @constructor
41829 * Create a new MonthField
41830 * @param {Object} config
41831  */
41832 Roo.form.MonthField = function(config){
41833     
41834     Roo.form.MonthField.superclass.constructor.call(this, config);
41835     
41836       this.addEvents({
41837          
41838         /**
41839          * @event select
41840          * Fires when a date is selected
41841              * @param {Roo.form.MonthFieeld} combo This combo box
41842              * @param {Date} date The date selected
41843              */
41844         'select' : true
41845          
41846     });
41847     
41848     
41849     if(typeof this.minValue == "string") {
41850         this.minValue = this.parseDate(this.minValue);
41851     }
41852     if(typeof this.maxValue == "string") {
41853         this.maxValue = this.parseDate(this.maxValue);
41854     }
41855     this.ddMatch = null;
41856     if(this.disabledDates){
41857         var dd = this.disabledDates;
41858         var re = "(?:";
41859         for(var i = 0; i < dd.length; i++){
41860             re += dd[i];
41861             if(i != dd.length-1) {
41862                 re += "|";
41863             }
41864         }
41865         this.ddMatch = new RegExp(re + ")");
41866     }
41867 };
41868
41869 Roo.extend(Roo.form.MonthField, Roo.form.TriggerField,  {
41870     /**
41871      * @cfg {String} format
41872      * The default date format string which can be overriden for localization support.  The format must be
41873      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
41874      */
41875     format : "M Y",
41876     /**
41877      * @cfg {String} altFormats
41878      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
41879      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
41880      */
41881     altFormats : "M Y|m/Y|m-y|m-Y|my|mY",
41882     /**
41883      * @cfg {Array} disabledDays
41884      * An array of days to disable, 0 based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
41885      */
41886     disabledDays : [0,1,2,3,4,5,6],
41887     /**
41888      * @cfg {String} disabledDaysText
41889      * The tooltip to display when the date falls on a disabled day (defaults to 'Disabled')
41890      */
41891     disabledDaysText : "Disabled",
41892     /**
41893      * @cfg {Array} disabledDates
41894      * An array of "dates" to disable, as strings. These strings will be used to build a dynamic regular
41895      * expression so they are very powerful. Some examples:
41896      * <ul>
41897      * <li>["03/08/2003", "09/16/2003"] would disable those exact dates</li>
41898      * <li>["03/08", "09/16"] would disable those days for every year</li>
41899      * <li>["^03/08"] would only match the beginning (useful if you are using short years)</li>
41900      * <li>["03/../2006"] would disable every day in March 2006</li>
41901      * <li>["^03"] would disable every day in every March</li>
41902      * </ul>
41903      * In order to support regular expressions, if you are using a date format that has "." in it, you will have to
41904      * escape the dot when restricting dates. For example: ["03\\.08\\.03"].
41905      */
41906     disabledDates : null,
41907     /**
41908      * @cfg {String} disabledDatesText
41909      * The tooltip text to display when the date falls on a disabled date (defaults to 'Disabled')
41910      */
41911     disabledDatesText : "Disabled",
41912     /**
41913      * @cfg {Date/String} minValue
41914      * The minimum allowed date. Can be either a Javascript date object or a string date in a
41915      * valid format (defaults to null).
41916      */
41917     minValue : null,
41918     /**
41919      * @cfg {Date/String} maxValue
41920      * The maximum allowed date. Can be either a Javascript date object or a string date in a
41921      * valid format (defaults to null).
41922      */
41923     maxValue : null,
41924     /**
41925      * @cfg {String} minText
41926      * The error text to display when the date in the cell is before minValue (defaults to
41927      * 'The date in this field must be after {minValue}').
41928      */
41929     minText : "The date in this field must be equal to or after {0}",
41930     /**
41931      * @cfg {String} maxTextf
41932      * The error text to display when the date in the cell is after maxValue (defaults to
41933      * 'The date in this field must be before {maxValue}').
41934      */
41935     maxText : "The date in this field must be equal to or before {0}",
41936     /**
41937      * @cfg {String} invalidText
41938      * The error text to display when the date in the field is invalid (defaults to
41939      * '{value} is not a valid date - it must be in the format {format}').
41940      */
41941     invalidText : "{0} is not a valid date - it must be in the format {1}",
41942     /**
41943      * @cfg {String} triggerClass
41944      * An additional CSS class used to style the trigger button.  The trigger will always get the
41945      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-date-trigger'
41946      * which displays a calendar icon).
41947      */
41948     triggerClass : 'x-form-date-trigger',
41949     
41950
41951     /**
41952      * @cfg {Boolean} useIso
41953      * if enabled, then the date field will use a hidden field to store the 
41954      * real value as iso formated date. default (true)
41955      */ 
41956     useIso : true,
41957     /**
41958      * @cfg {String/Object} autoCreate
41959      * A DomHelper element spec, or true for a default element spec (defaults to
41960      * {tag: "input", type: "text", size: "10", autocomplete: "off"})
41961      */ 
41962     // private
41963     defaultAutoCreate : {tag: "input", type: "text", size: "10", autocomplete: "new-password"},
41964     
41965     // private
41966     hiddenField: false,
41967     
41968     hideMonthPicker : false,
41969     
41970     onRender : function(ct, position)
41971     {
41972         Roo.form.MonthField.superclass.onRender.call(this, ct, position);
41973         if (this.useIso) {
41974             this.el.dom.removeAttribute('name'); 
41975             this.hiddenField = this.el.insertSibling({ tag:'input', type:'hidden', name: this.name },
41976                     'before', true);
41977             this.hiddenField.value = this.value ? this.formatDate(this.value, 'Y-m-d') : '';
41978             // prevent input submission
41979             this.hiddenName = this.name;
41980         }
41981             
41982             
41983     },
41984     
41985     // private
41986     validateValue : function(value)
41987     {
41988         value = this.formatDate(value);
41989         if(!Roo.form.MonthField.superclass.validateValue.call(this, value)){
41990             return false;
41991         }
41992         if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
41993              return true;
41994         }
41995         var svalue = value;
41996         value = this.parseDate(value);
41997         if(!value){
41998             this.markInvalid(String.format(this.invalidText, svalue, this.format));
41999             return false;
42000         }
42001         var time = value.getTime();
42002         if(this.minValue && time < this.minValue.getTime()){
42003             this.markInvalid(String.format(this.minText, this.formatDate(this.minValue)));
42004             return false;
42005         }
42006         if(this.maxValue && time > this.maxValue.getTime()){
42007             this.markInvalid(String.format(this.maxText, this.formatDate(this.maxValue)));
42008             return false;
42009         }
42010         /*if(this.disabledDays){
42011             var day = value.getDay();
42012             for(var i = 0; i < this.disabledDays.length; i++) {
42013                 if(day === this.disabledDays[i]){
42014                     this.markInvalid(this.disabledDaysText);
42015                     return false;
42016                 }
42017             }
42018         }
42019         */
42020         var fvalue = this.formatDate(value);
42021         /*if(this.ddMatch && this.ddMatch.test(fvalue)){
42022             this.markInvalid(String.format(this.disabledDatesText, fvalue));
42023             return false;
42024         }
42025         */
42026         return true;
42027     },
42028
42029     // private
42030     // Provides logic to override the default TriggerField.validateBlur which just returns true
42031     validateBlur : function(){
42032         return !this.menu || !this.menu.isVisible();
42033     },
42034
42035     /**
42036      * Returns the current date value of the date field.
42037      * @return {Date} The date value
42038      */
42039     getValue : function(){
42040         
42041         
42042         
42043         return  this.hiddenField ?
42044                 this.hiddenField.value :
42045                 this.parseDate(Roo.form.MonthField.superclass.getValue.call(this)) || "";
42046     },
42047
42048     /**
42049      * Sets the value of the date field.  You can pass a date object or any string that can be parsed into a valid
42050      * date, using MonthField.format as the date format, according to the same rules as {@link Date#parseDate}
42051      * (the default format used is "m/d/y").
42052      * <br />Usage:
42053      * <pre><code>
42054 //All of these calls set the same date value (May 4, 2006)
42055
42056 //Pass a date object:
42057 var dt = new Date('5/4/06');
42058 monthField.setValue(dt);
42059
42060 //Pass a date string (default format):
42061 monthField.setValue('5/4/06');
42062
42063 //Pass a date string (custom format):
42064 monthField.format = 'Y-m-d';
42065 monthField.setValue('2006-5-4');
42066 </code></pre>
42067      * @param {String/Date} date The date or valid date string
42068      */
42069     setValue : function(date){
42070         Roo.log('month setValue' + date);
42071         // can only be first of month..
42072         
42073         var val = this.parseDate(date);
42074         
42075         if (this.hiddenField) {
42076             this.hiddenField.value = this.formatDate(this.parseDate(date), 'Y-m-d');
42077         }
42078         Roo.form.MonthField.superclass.setValue.call(this, this.formatDate(this.parseDate(date)));
42079         this.value = this.parseDate(date);
42080     },
42081
42082     // private
42083     parseDate : function(value){
42084         if(!value || value instanceof Date){
42085             value = value ? Date.parseDate(value.format('Y-m') + '-01', 'Y-m-d') : null;
42086             return value;
42087         }
42088         var v = Date.parseDate(value, this.format);
42089         if (!v && this.useIso) {
42090             v = Date.parseDate(value, 'Y-m-d');
42091         }
42092         if (v) {
42093             // 
42094             v = Date.parseDate(v.format('Y-m') +'-01', 'Y-m-d');
42095         }
42096         
42097         
42098         if(!v && this.altFormats){
42099             if(!this.altFormatsArray){
42100                 this.altFormatsArray = this.altFormats.split("|");
42101             }
42102             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
42103                 v = Date.parseDate(value, this.altFormatsArray[i]);
42104             }
42105         }
42106         return v;
42107     },
42108
42109     // private
42110     formatDate : function(date, fmt){
42111         return (!date || !(date instanceof Date)) ?
42112                date : date.dateFormat(fmt || this.format);
42113     },
42114
42115     // private
42116     menuListeners : {
42117         select: function(m, d){
42118             this.setValue(d);
42119             this.fireEvent('select', this, d);
42120         },
42121         show : function(){ // retain focus styling
42122             this.onFocus();
42123         },
42124         hide : function(){
42125             this.focus.defer(10, this);
42126             var ml = this.menuListeners;
42127             this.menu.un("select", ml.select,  this);
42128             this.menu.un("show", ml.show,  this);
42129             this.menu.un("hide", ml.hide,  this);
42130         }
42131     },
42132     // private
42133     // Implements the default empty TriggerField.onTriggerClick function to display the DatePicker
42134     onTriggerClick : function(){
42135         if(this.disabled){
42136             return;
42137         }
42138         if(this.menu == null){
42139             this.menu = new Roo.menu.DateMenu();
42140            
42141         }
42142         
42143         Roo.apply(this.menu.picker,  {
42144             
42145             showClear: this.allowBlank,
42146             minDate : this.minValue,
42147             maxDate : this.maxValue,
42148             disabledDatesRE : this.ddMatch,
42149             disabledDatesText : this.disabledDatesText,
42150             
42151             format : this.useIso ? 'Y-m-d' : this.format,
42152             minText : String.format(this.minText, this.formatDate(this.minValue)),
42153             maxText : String.format(this.maxText, this.formatDate(this.maxValue))
42154             
42155         });
42156          this.menu.on(Roo.apply({}, this.menuListeners, {
42157             scope:this
42158         }));
42159        
42160         
42161         var m = this.menu;
42162         var p = m.picker;
42163         
42164         // hide month picker get's called when we called by 'before hide';
42165         
42166         var ignorehide = true;
42167         p.hideMonthPicker  = function(disableAnim){
42168             if (ignorehide) {
42169                 return;
42170             }
42171              if(this.monthPicker){
42172                 Roo.log("hideMonthPicker called");
42173                 if(disableAnim === true){
42174                     this.monthPicker.hide();
42175                 }else{
42176                     this.monthPicker.slideOut('t', {duration:.2});
42177                     p.setValue(new Date(m.picker.mpSelYear, m.picker.mpSelMonth, 1));
42178                     p.fireEvent("select", this, this.value);
42179                     m.hide();
42180                 }
42181             }
42182         }
42183         
42184         Roo.log('picker set value');
42185         Roo.log(this.getValue());
42186         p.setValue(this.getValue() ? this.parseDate(this.getValue()) : new Date());
42187         m.show(this.el, 'tl-bl?');
42188         ignorehide  = false;
42189         // this will trigger hideMonthPicker..
42190         
42191         
42192         // hidden the day picker
42193         Roo.select('.x-date-picker table', true).first().dom.style.visibility = "hidden";
42194         
42195         
42196         
42197       
42198         
42199         p.showMonthPicker.defer(100, p);
42200     
42201         
42202        
42203     },
42204
42205     beforeBlur : function(){
42206         var v = this.parseDate(this.getRawValue());
42207         if(v){
42208             this.setValue(v);
42209         }
42210     }
42211
42212     /** @cfg {Boolean} grow @hide */
42213     /** @cfg {Number} growMin @hide */
42214     /** @cfg {Number} growMax @hide */
42215     /**
42216      * @hide
42217      * @method autoSize
42218      */
42219 });/*
42220  * Based on:
42221  * Ext JS Library 1.1.1
42222  * Copyright(c) 2006-2007, Ext JS, LLC.
42223  *
42224  * Originally Released Under LGPL - original licence link has changed is not relivant.
42225  *
42226  * Fork - LGPL
42227  * <script type="text/javascript">
42228  */
42229  
42230
42231 /**
42232  * @class Roo.form.ComboBox
42233  * @extends Roo.form.TriggerField
42234  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
42235  * @constructor
42236  * Create a new ComboBox.
42237  * @param {Object} config Configuration options
42238  */
42239 Roo.form.ComboBox = function(config){
42240     Roo.form.ComboBox.superclass.constructor.call(this, config);
42241     this.addEvents({
42242         /**
42243          * @event expand
42244          * Fires when the dropdown list is expanded
42245              * @param {Roo.form.ComboBox} combo This combo box
42246              */
42247         'expand' : true,
42248         /**
42249          * @event collapse
42250          * Fires when the dropdown list is collapsed
42251              * @param {Roo.form.ComboBox} combo This combo box
42252              */
42253         'collapse' : true,
42254         /**
42255          * @event beforeselect
42256          * Fires before a list item is selected. Return false to cancel the selection.
42257              * @param {Roo.form.ComboBox} combo This combo box
42258              * @param {Roo.data.Record} record The data record returned from the underlying store
42259              * @param {Number} index The index of the selected item in the dropdown list
42260              */
42261         'beforeselect' : true,
42262         /**
42263          * @event select
42264          * Fires when a list item is selected
42265              * @param {Roo.form.ComboBox} combo This combo box
42266              * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
42267              * @param {Number} index The index of the selected item in the dropdown list
42268              */
42269         'select' : true,
42270         /**
42271          * @event beforequery
42272          * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
42273          * The event object passed has these properties:
42274              * @param {Roo.form.ComboBox} combo This combo box
42275              * @param {String} query The query
42276              * @param {Boolean} forceAll true to force "all" query
42277              * @param {Boolean} cancel true to cancel the query
42278              * @param {Object} e The query event object
42279              */
42280         'beforequery': true,
42281          /**
42282          * @event add
42283          * Fires when the 'add' icon is pressed (add a listener to enable add button)
42284              * @param {Roo.form.ComboBox} combo This combo box
42285              */
42286         'add' : true,
42287         /**
42288          * @event edit
42289          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
42290              * @param {Roo.form.ComboBox} combo This combo box
42291              * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
42292              */
42293         'edit' : true
42294         
42295         
42296     });
42297     if(this.transform){
42298         this.allowDomMove = false;
42299         var s = Roo.getDom(this.transform);
42300         if(!this.hiddenName){
42301             this.hiddenName = s.name;
42302         }
42303         if(!this.store){
42304             this.mode = 'local';
42305             var d = [], opts = s.options;
42306             for(var i = 0, len = opts.length;i < len; i++){
42307                 var o = opts[i];
42308                 var value = (Roo.isIE ? o.getAttributeNode('value').specified : o.hasAttribute('value')) ? o.value : o.text;
42309                 if(o.selected) {
42310                     this.value = value;
42311                 }
42312                 d.push([value, o.text]);
42313             }
42314             this.store = new Roo.data.SimpleStore({
42315                 'id': 0,
42316                 fields: ['value', 'text'],
42317                 data : d
42318             });
42319             this.valueField = 'value';
42320             this.displayField = 'text';
42321         }
42322         s.name = Roo.id(); // wipe out the name in case somewhere else they have a reference
42323         if(!this.lazyRender){
42324             this.target = true;
42325             this.el = Roo.DomHelper.insertBefore(s, this.autoCreate || this.defaultAutoCreate);
42326             s.parentNode.removeChild(s); // remove it
42327             this.render(this.el.parentNode);
42328         }else{
42329             s.parentNode.removeChild(s); // remove it
42330         }
42331
42332     }
42333     if (this.store) {
42334         this.store = Roo.factory(this.store, Roo.data);
42335     }
42336     
42337     this.selectedIndex = -1;
42338     if(this.mode == 'local'){
42339         if(config.queryDelay === undefined){
42340             this.queryDelay = 10;
42341         }
42342         if(config.minChars === undefined){
42343             this.minChars = 0;
42344         }
42345     }
42346 };
42347
42348 Roo.extend(Roo.form.ComboBox, Roo.form.TriggerField, {
42349     /**
42350      * @cfg {String/HTMLElement/Element} transform The id, DOM node or element of an existing select to convert to a ComboBox
42351      */
42352     /**
42353      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
42354      * rendering into an Roo.Editor, defaults to false)
42355      */
42356     /**
42357      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
42358      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
42359      */
42360     /**
42361      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
42362      */
42363     /**
42364      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
42365      * the dropdown list (defaults to undefined, with no header element)
42366      */
42367
42368      /**
42369      * @cfg {String/Roo.Template} tpl The template to use to render the output
42370      */
42371      
42372     // private
42373     defaultAutoCreate : {tag: "input", type: "text", size: "24", autocomplete: "off"},
42374     /**
42375      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
42376      */
42377     listWidth: undefined,
42378     /**
42379      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
42380      * mode = 'remote' or 'text' if mode = 'local')
42381      */
42382     displayField: undefined,
42383     /**
42384      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
42385      * mode = 'remote' or 'value' if mode = 'local'). 
42386      * Note: use of a valueField requires the user make a selection
42387      * in order for a value to be mapped.
42388      */
42389     valueField: undefined,
42390     
42391     
42392     /**
42393      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
42394      * field's data value (defaults to the underlying DOM element's name)
42395      */
42396     hiddenName: undefined,
42397     /**
42398      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
42399      */
42400     listClass: '',
42401     /**
42402      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
42403      */
42404     selectedClass: 'x-combo-selected',
42405     /**
42406      * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
42407      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-arrow-trigger'
42408      * which displays a downward arrow icon).
42409      */
42410     triggerClass : 'x-form-arrow-trigger',
42411     /**
42412      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
42413      */
42414     shadow:'sides',
42415     /**
42416      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
42417      * anchor positions (defaults to 'tl-bl')
42418      */
42419     listAlign: 'tl-bl?',
42420     /**
42421      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
42422      */
42423     maxHeight: 300,
42424     /**
42425      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
42426      * query specified by the allQuery config option (defaults to 'query')
42427      */
42428     triggerAction: 'query',
42429     /**
42430      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
42431      * (defaults to 4, does not apply if editable = false)
42432      */
42433     minChars : 4,
42434     /**
42435      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
42436      * delay (typeAheadDelay) if it matches a known value (defaults to false)
42437      */
42438     typeAhead: false,
42439     /**
42440      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
42441      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
42442      */
42443     queryDelay: 500,
42444     /**
42445      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
42446      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
42447      */
42448     pageSize: 0,
42449     /**
42450      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
42451      * when editable = true (defaults to false)
42452      */
42453     selectOnFocus:false,
42454     /**
42455      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
42456      */
42457     queryParam: 'query',
42458     /**
42459      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
42460      * when mode = 'remote' (defaults to 'Loading...')
42461      */
42462     loadingText: 'Loading...',
42463     /**
42464      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
42465      */
42466     resizable: false,
42467     /**
42468      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
42469      */
42470     handleHeight : 8,
42471     /**
42472      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
42473      * traditional select (defaults to true)
42474      */
42475     editable: true,
42476     /**
42477      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
42478      */
42479     allQuery: '',
42480     /**
42481      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
42482      */
42483     mode: 'remote',
42484     /**
42485      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
42486      * listWidth has a higher value)
42487      */
42488     minListWidth : 70,
42489     /**
42490      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
42491      * allow the user to set arbitrary text into the field (defaults to false)
42492      */
42493     forceSelection:false,
42494     /**
42495      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
42496      * if typeAhead = true (defaults to 250)
42497      */
42498     typeAheadDelay : 250,
42499     /**
42500      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
42501      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
42502      */
42503     valueNotFoundText : undefined,
42504     /**
42505      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
42506      */
42507     blockFocus : false,
42508     
42509     /**
42510      * @cfg {Boolean} disableClear Disable showing of clear button.
42511      */
42512     disableClear : false,
42513     /**
42514      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
42515      */
42516     alwaysQuery : false,
42517     
42518     //private
42519     addicon : false,
42520     editicon: false,
42521     
42522     // element that contains real text value.. (when hidden is used..)
42523      
42524     // private
42525     onRender : function(ct, position)
42526     {
42527         Roo.form.ComboBox.superclass.onRender.call(this, ct, position);
42528         
42529         if(this.hiddenName){
42530             this.hiddenField = this.el.insertSibling({tag:'input', type:'hidden', name: this.hiddenName, id:  (this.hiddenId||this.hiddenName)},
42531                     'before', true);
42532             this.hiddenField.value =
42533                 this.hiddenValue !== undefined ? this.hiddenValue :
42534                 this.value !== undefined ? this.value : '';
42535
42536             // prevent input submission
42537             this.el.dom.removeAttribute('name');
42538              
42539              
42540         }
42541         
42542         if(Roo.isGecko){
42543             this.el.dom.setAttribute('autocomplete', 'off');
42544         }
42545
42546         var cls = 'x-combo-list';
42547
42548         this.list = new Roo.Layer({
42549             shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
42550         });
42551
42552         var lw = this.listWidth || Math.max(this.wrap.getWidth(), this.minListWidth);
42553         this.list.setWidth(lw);
42554         this.list.swallowEvent('mousewheel');
42555         this.assetHeight = 0;
42556
42557         if(this.title){
42558             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
42559             this.assetHeight += this.header.getHeight();
42560         }
42561
42562         this.innerList = this.list.createChild({cls:cls+'-inner'});
42563         this.innerList.on('mouseover', this.onViewOver, this);
42564         this.innerList.on('mousemove', this.onViewMove, this);
42565         this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
42566         
42567         if(this.allowBlank && !this.pageSize && !this.disableClear){
42568             this.footer = this.list.createChild({cls:cls+'-ft'});
42569             this.pageTb = new Roo.Toolbar(this.footer);
42570            
42571         }
42572         if(this.pageSize){
42573             this.footer = this.list.createChild({cls:cls+'-ft'});
42574             this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
42575                     {pageSize: this.pageSize});
42576             
42577         }
42578         
42579         if (this.pageTb && this.allowBlank && !this.disableClear) {
42580             var _this = this;
42581             this.pageTb.add(new Roo.Toolbar.Fill(), {
42582                 cls: 'x-btn-icon x-btn-clear',
42583                 text: '&#160;',
42584                 handler: function()
42585                 {
42586                     _this.collapse();
42587                     _this.clearValue();
42588                     _this.onSelect(false, -1);
42589                 }
42590             });
42591         }
42592         if (this.footer) {
42593             this.assetHeight += this.footer.getHeight();
42594         }
42595         
42596
42597         if(!this.tpl){
42598             this.tpl = '<div class="'+cls+'-item">{' + this.displayField + '}</div>';
42599         }
42600
42601         this.view = new Roo.View(this.innerList, this.tpl, {
42602             singleSelect:true,
42603             store: this.store,
42604             selectedClass: this.selectedClass
42605         });
42606
42607         this.view.on('click', this.onViewClick, this);
42608
42609         this.store.on('beforeload', this.onBeforeLoad, this);
42610         this.store.on('load', this.onLoad, this);
42611         this.store.on('loadexception', this.onLoadException, this);
42612
42613         if(this.resizable){
42614             this.resizer = new Roo.Resizable(this.list,  {
42615                pinned:true, handles:'se'
42616             });
42617             this.resizer.on('resize', function(r, w, h){
42618                 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
42619                 this.listWidth = w;
42620                 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
42621                 this.restrictHeight();
42622             }, this);
42623             this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
42624         }
42625         if(!this.editable){
42626             this.editable = true;
42627             this.setEditable(false);
42628         }  
42629         
42630         
42631         if (typeof(this.events.add.listeners) != 'undefined') {
42632             
42633             this.addicon = this.wrap.createChild(
42634                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });  
42635        
42636             this.addicon.on('click', function(e) {
42637                 this.fireEvent('add', this);
42638             }, this);
42639         }
42640         if (typeof(this.events.edit.listeners) != 'undefined') {
42641             
42642             this.editicon = this.wrap.createChild(
42643                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });  
42644             if (this.addicon) {
42645                 this.editicon.setStyle('margin-left', '40px');
42646             }
42647             this.editicon.on('click', function(e) {
42648                 
42649                 // we fire even  if inothing is selected..
42650                 this.fireEvent('edit', this, this.lastData );
42651                 
42652             }, this);
42653         }
42654         
42655         
42656         
42657     },
42658
42659     // private
42660     initEvents : function(){
42661         Roo.form.ComboBox.superclass.initEvents.call(this);
42662
42663         this.keyNav = new Roo.KeyNav(this.el, {
42664             "up" : function(e){
42665                 this.inKeyMode = true;
42666                 this.selectPrev();
42667             },
42668
42669             "down" : function(e){
42670                 if(!this.isExpanded()){
42671                     this.onTriggerClick();
42672                 }else{
42673                     this.inKeyMode = true;
42674                     this.selectNext();
42675                 }
42676             },
42677
42678             "enter" : function(e){
42679                 this.onViewClick();
42680                 //return true;
42681             },
42682
42683             "esc" : function(e){
42684                 this.collapse();
42685             },
42686
42687             "tab" : function(e){
42688                 this.onViewClick(false);
42689                 this.fireEvent("specialkey", this, e);
42690                 return true;
42691             },
42692
42693             scope : this,
42694
42695             doRelay : function(foo, bar, hname){
42696                 if(hname == 'down' || this.scope.isExpanded()){
42697                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
42698                 }
42699                 return true;
42700             },
42701
42702             forceKeyDown: true
42703         });
42704         this.queryDelay = Math.max(this.queryDelay || 10,
42705                 this.mode == 'local' ? 10 : 250);
42706         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
42707         if(this.typeAhead){
42708             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
42709         }
42710         if(this.editable !== false){
42711             this.el.on("keyup", this.onKeyUp, this);
42712         }
42713         if(this.forceSelection){
42714             this.on('blur', this.doForce, this);
42715         }
42716     },
42717
42718     onDestroy : function(){
42719         if(this.view){
42720             this.view.setStore(null);
42721             this.view.el.removeAllListeners();
42722             this.view.el.remove();
42723             this.view.purgeListeners();
42724         }
42725         if(this.list){
42726             this.list.destroy();
42727         }
42728         if(this.store){
42729             this.store.un('beforeload', this.onBeforeLoad, this);
42730             this.store.un('load', this.onLoad, this);
42731             this.store.un('loadexception', this.onLoadException, this);
42732         }
42733         Roo.form.ComboBox.superclass.onDestroy.call(this);
42734     },
42735
42736     // private
42737     fireKey : function(e){
42738         if(e.isNavKeyPress() && !this.list.isVisible()){
42739             this.fireEvent("specialkey", this, e);
42740         }
42741     },
42742
42743     // private
42744     onResize: function(w, h){
42745         Roo.form.ComboBox.superclass.onResize.apply(this, arguments);
42746         
42747         if(typeof w != 'number'){
42748             // we do not handle it!?!?
42749             return;
42750         }
42751         var tw = this.trigger.getWidth();
42752         tw += this.addicon ? this.addicon.getWidth() : 0;
42753         tw += this.editicon ? this.editicon.getWidth() : 0;
42754         var x = w - tw;
42755         this.el.setWidth( this.adjustWidth('input', x));
42756             
42757         this.trigger.setStyle('left', x+'px');
42758         
42759         if(this.list && this.listWidth === undefined){
42760             var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
42761             this.list.setWidth(lw);
42762             this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
42763         }
42764         
42765     
42766         
42767     },
42768
42769     /**
42770      * Allow or prevent the user from directly editing the field text.  If false is passed,
42771      * the user will only be able to select from the items defined in the dropdown list.  This method
42772      * is the runtime equivalent of setting the 'editable' config option at config time.
42773      * @param {Boolean} value True to allow the user to directly edit the field text
42774      */
42775     setEditable : function(value){
42776         if(value == this.editable){
42777             return;
42778         }
42779         this.editable = value;
42780         if(!value){
42781             this.el.dom.setAttribute('readOnly', true);
42782             this.el.on('mousedown', this.onTriggerClick,  this);
42783             this.el.addClass('x-combo-noedit');
42784         }else{
42785             this.el.dom.setAttribute('readOnly', false);
42786             this.el.un('mousedown', this.onTriggerClick,  this);
42787             this.el.removeClass('x-combo-noedit');
42788         }
42789     },
42790
42791     // private
42792     onBeforeLoad : function(){
42793         if(!this.hasFocus){
42794             return;
42795         }
42796         this.innerList.update(this.loadingText ?
42797                '<div class="loading-indicator">'+this.loadingText+'</div>' : '');
42798         this.restrictHeight();
42799         this.selectedIndex = -1;
42800     },
42801
42802     // private
42803     onLoad : function(){
42804         if(!this.hasFocus){
42805             return;
42806         }
42807         if(this.store.getCount() > 0){
42808             this.expand();
42809             this.restrictHeight();
42810             if(this.lastQuery == this.allQuery){
42811                 if(this.editable){
42812                     this.el.dom.select();
42813                 }
42814                 if(!this.selectByValue(this.value, true)){
42815                     this.select(0, true);
42816                 }
42817             }else{
42818                 this.selectNext();
42819                 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
42820                     this.taTask.delay(this.typeAheadDelay);
42821                 }
42822             }
42823         }else{
42824             this.onEmptyResults();
42825         }
42826         //this.el.focus();
42827     },
42828     // private
42829     onLoadException : function()
42830     {
42831         this.collapse();
42832         Roo.log(this.store.reader.jsonData);
42833         if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
42834             Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
42835         }
42836         
42837         
42838     },
42839     // private
42840     onTypeAhead : function(){
42841         if(this.store.getCount() > 0){
42842             var r = this.store.getAt(0);
42843             var newValue = r.data[this.displayField];
42844             var len = newValue.length;
42845             var selStart = this.getRawValue().length;
42846             if(selStart != len){
42847                 this.setRawValue(newValue);
42848                 this.selectText(selStart, newValue.length);
42849             }
42850         }
42851     },
42852
42853     // private
42854     onSelect : function(record, index){
42855         if(this.fireEvent('beforeselect', this, record, index) !== false){
42856             this.setFromData(index > -1 ? record.data : false);
42857             this.collapse();
42858             this.fireEvent('select', this, record, index);
42859         }
42860     },
42861
42862     /**
42863      * Returns the currently selected field value or empty string if no value is set.
42864      * @return {String} value The selected value
42865      */
42866     getValue : function(){
42867         if(this.valueField){
42868             return typeof this.value != 'undefined' ? this.value : '';
42869         }
42870         return Roo.form.ComboBox.superclass.getValue.call(this);
42871     },
42872
42873     /**
42874      * Clears any text/value currently set in the field
42875      */
42876     clearValue : function(){
42877         if(this.hiddenField){
42878             this.hiddenField.value = '';
42879         }
42880         this.value = '';
42881         this.setRawValue('');
42882         this.lastSelectionText = '';
42883         
42884     },
42885
42886     /**
42887      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
42888      * will be displayed in the field.  If the value does not match the data value of an existing item,
42889      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
42890      * Otherwise the field will be blank (although the value will still be set).
42891      * @param {String} value The value to match
42892      */
42893     setValue : function(v){
42894         var text = v;
42895         if(this.valueField){
42896             var r = this.findRecord(this.valueField, v);
42897             if(r){
42898                 text = r.data[this.displayField];
42899             }else if(this.valueNotFoundText !== undefined){
42900                 text = this.valueNotFoundText;
42901             }
42902         }
42903         this.lastSelectionText = text;
42904         if(this.hiddenField){
42905             this.hiddenField.value = v;
42906         }
42907         Roo.form.ComboBox.superclass.setValue.call(this, text);
42908         this.value = v;
42909     },
42910     /**
42911      * @property {Object} the last set data for the element
42912      */
42913     
42914     lastData : false,
42915     /**
42916      * Sets the value of the field based on a object which is related to the record format for the store.
42917      * @param {Object} value the value to set as. or false on reset?
42918      */
42919     setFromData : function(o){
42920         var dv = ''; // display value
42921         var vv = ''; // value value..
42922         this.lastData = o;
42923         if (this.displayField) {
42924             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
42925         } else {
42926             // this is an error condition!!!
42927             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
42928         }
42929         
42930         if(this.valueField){
42931             vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
42932         }
42933         if(this.hiddenField){
42934             this.hiddenField.value = vv;
42935             
42936             this.lastSelectionText = dv;
42937             Roo.form.ComboBox.superclass.setValue.call(this, dv);
42938             this.value = vv;
42939             return;
42940         }
42941         // no hidden field.. - we store the value in 'value', but still display
42942         // display field!!!!
42943         this.lastSelectionText = dv;
42944         Roo.form.ComboBox.superclass.setValue.call(this, dv);
42945         this.value = vv;
42946         
42947         
42948     },
42949     // private
42950     reset : function(){
42951         // overridden so that last data is reset..
42952         this.setValue(this.resetValue);
42953         this.originalValue = this.getValue();
42954         this.clearInvalid();
42955         this.lastData = false;
42956         if (this.view) {
42957             this.view.clearSelections();
42958         }
42959     },
42960     // private
42961     findRecord : function(prop, value){
42962         var record;
42963         if(this.store.getCount() > 0){
42964             this.store.each(function(r){
42965                 if(r.data[prop] == value){
42966                     record = r;
42967                     return false;
42968                 }
42969                 return true;
42970             });
42971         }
42972         return record;
42973     },
42974     
42975     getName: function()
42976     {
42977         // returns hidden if it's set..
42978         if (!this.rendered) {return ''};
42979         return !this.hiddenName && this.el.dom.name  ? this.el.dom.name : (this.hiddenName || '');
42980         
42981     },
42982     // private
42983     onViewMove : function(e, t){
42984         this.inKeyMode = false;
42985     },
42986
42987     // private
42988     onViewOver : function(e, t){
42989         if(this.inKeyMode){ // prevent key nav and mouse over conflicts
42990             return;
42991         }
42992         var item = this.view.findItemFromChild(t);
42993         if(item){
42994             var index = this.view.indexOf(item);
42995             this.select(index, false);
42996         }
42997     },
42998
42999     // private
43000     onViewClick : function(doFocus)
43001     {
43002         var index = this.view.getSelectedIndexes()[0];
43003         var r = this.store.getAt(index);
43004         if(r){
43005             this.onSelect(r, index);
43006         }
43007         if(doFocus !== false && !this.blockFocus){
43008             this.el.focus();
43009         }
43010     },
43011
43012     // private
43013     restrictHeight : function(){
43014         this.innerList.dom.style.height = '';
43015         var inner = this.innerList.dom;
43016         var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
43017         this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
43018         this.list.beginUpdate();
43019         this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
43020         this.list.alignTo(this.el, this.listAlign);
43021         this.list.endUpdate();
43022     },
43023
43024     // private
43025     onEmptyResults : function(){
43026         this.collapse();
43027     },
43028
43029     /**
43030      * Returns true if the dropdown list is expanded, else false.
43031      */
43032     isExpanded : function(){
43033         return this.list.isVisible();
43034     },
43035
43036     /**
43037      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
43038      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
43039      * @param {String} value The data value of the item to select
43040      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
43041      * selected item if it is not currently in view (defaults to true)
43042      * @return {Boolean} True if the value matched an item in the list, else false
43043      */
43044     selectByValue : function(v, scrollIntoView){
43045         if(v !== undefined && v !== null){
43046             var r = this.findRecord(this.valueField || this.displayField, v);
43047             if(r){
43048                 this.select(this.store.indexOf(r), scrollIntoView);
43049                 return true;
43050             }
43051         }
43052         return false;
43053     },
43054
43055     /**
43056      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
43057      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
43058      * @param {Number} index The zero-based index of the list item to select
43059      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
43060      * selected item if it is not currently in view (defaults to true)
43061      */
43062     select : function(index, scrollIntoView){
43063         this.selectedIndex = index;
43064         this.view.select(index);
43065         if(scrollIntoView !== false){
43066             var el = this.view.getNode(index);
43067             if(el){
43068                 this.innerList.scrollChildIntoView(el, false);
43069             }
43070         }
43071     },
43072
43073     // private
43074     selectNext : function(){
43075         var ct = this.store.getCount();
43076         if(ct > 0){
43077             if(this.selectedIndex == -1){
43078                 this.select(0);
43079             }else if(this.selectedIndex < ct-1){
43080                 this.select(this.selectedIndex+1);
43081             }
43082         }
43083     },
43084
43085     // private
43086     selectPrev : function(){
43087         var ct = this.store.getCount();
43088         if(ct > 0){
43089             if(this.selectedIndex == -1){
43090                 this.select(0);
43091             }else if(this.selectedIndex != 0){
43092                 this.select(this.selectedIndex-1);
43093             }
43094         }
43095     },
43096
43097     // private
43098     onKeyUp : function(e){
43099         if(this.editable !== false && !e.isSpecialKey()){
43100             this.lastKey = e.getKey();
43101             this.dqTask.delay(this.queryDelay);
43102         }
43103     },
43104
43105     // private
43106     validateBlur : function(){
43107         return !this.list || !this.list.isVisible();   
43108     },
43109
43110     // private
43111     initQuery : function(){
43112         this.doQuery(this.getRawValue());
43113     },
43114
43115     // private
43116     doForce : function(){
43117         if(this.el.dom.value.length > 0){
43118             this.el.dom.value =
43119                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
43120              
43121         }
43122     },
43123
43124     /**
43125      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
43126      * query allowing the query action to be canceled if needed.
43127      * @param {String} query The SQL query to execute
43128      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
43129      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
43130      * saved in the current store (defaults to false)
43131      */
43132     doQuery : function(q, forceAll){
43133         if(q === undefined || q === null){
43134             q = '';
43135         }
43136         var qe = {
43137             query: q,
43138             forceAll: forceAll,
43139             combo: this,
43140             cancel:false
43141         };
43142         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
43143             return false;
43144         }
43145         q = qe.query;
43146         forceAll = qe.forceAll;
43147         if(forceAll === true || (q.length >= this.minChars)){
43148             if(this.lastQuery != q || this.alwaysQuery){
43149                 this.lastQuery = q;
43150                 if(this.mode == 'local'){
43151                     this.selectedIndex = -1;
43152                     if(forceAll){
43153                         this.store.clearFilter();
43154                     }else{
43155                         this.store.filter(this.displayField, q);
43156                     }
43157                     this.onLoad();
43158                 }else{
43159                     this.store.baseParams[this.queryParam] = q;
43160                     this.store.load({
43161                         params: this.getParams(q)
43162                     });
43163                     this.expand();
43164                 }
43165             }else{
43166                 this.selectedIndex = -1;
43167                 this.onLoad();   
43168             }
43169         }
43170     },
43171
43172     // private
43173     getParams : function(q){
43174         var p = {};
43175         //p[this.queryParam] = q;
43176         if(this.pageSize){
43177             p.start = 0;
43178             p.limit = this.pageSize;
43179         }
43180         return p;
43181     },
43182
43183     /**
43184      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
43185      */
43186     collapse : function(){
43187         if(!this.isExpanded()){
43188             return;
43189         }
43190         this.list.hide();
43191         Roo.get(document).un('mousedown', this.collapseIf, this);
43192         Roo.get(document).un('mousewheel', this.collapseIf, this);
43193         if (!this.editable) {
43194             Roo.get(document).un('keydown', this.listKeyPress, this);
43195         }
43196         this.fireEvent('collapse', this);
43197     },
43198
43199     // private
43200     collapseIf : function(e){
43201         if(!e.within(this.wrap) && !e.within(this.list)){
43202             this.collapse();
43203         }
43204     },
43205
43206     /**
43207      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
43208      */
43209     expand : function(){
43210         if(this.isExpanded() || !this.hasFocus){
43211             return;
43212         }
43213         this.list.alignTo(this.el, this.listAlign);
43214         this.list.show();
43215         Roo.get(document).on('mousedown', this.collapseIf, this);
43216         Roo.get(document).on('mousewheel', this.collapseIf, this);
43217         if (!this.editable) {
43218             Roo.get(document).on('keydown', this.listKeyPress, this);
43219         }
43220         
43221         this.fireEvent('expand', this);
43222     },
43223
43224     // private
43225     // Implements the default empty TriggerField.onTriggerClick function
43226     onTriggerClick : function(){
43227         if(this.disabled){
43228             return;
43229         }
43230         if(this.isExpanded()){
43231             this.collapse();
43232             if (!this.blockFocus) {
43233                 this.el.focus();
43234             }
43235             
43236         }else {
43237             this.hasFocus = true;
43238             if(this.triggerAction == 'all') {
43239                 this.doQuery(this.allQuery, true);
43240             } else {
43241                 this.doQuery(this.getRawValue());
43242             }
43243             if (!this.blockFocus) {
43244                 this.el.focus();
43245             }
43246         }
43247     },
43248     listKeyPress : function(e)
43249     {
43250         //Roo.log('listkeypress');
43251         // scroll to first matching element based on key pres..
43252         if (e.isSpecialKey()) {
43253             return false;
43254         }
43255         var k = String.fromCharCode(e.getKey()).toUpperCase();
43256         //Roo.log(k);
43257         var match  = false;
43258         var csel = this.view.getSelectedNodes();
43259         var cselitem = false;
43260         if (csel.length) {
43261             var ix = this.view.indexOf(csel[0]);
43262             cselitem  = this.store.getAt(ix);
43263             if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
43264                 cselitem = false;
43265             }
43266             
43267         }
43268         
43269         this.store.each(function(v) { 
43270             if (cselitem) {
43271                 // start at existing selection.
43272                 if (cselitem.id == v.id) {
43273                     cselitem = false;
43274                 }
43275                 return;
43276             }
43277                 
43278             if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
43279                 match = this.store.indexOf(v);
43280                 return false;
43281             }
43282         }, this);
43283         
43284         if (match === false) {
43285             return true; // no more action?
43286         }
43287         // scroll to?
43288         this.view.select(match);
43289         var sn = Roo.get(this.view.getSelectedNodes()[0]);
43290         sn.scrollIntoView(sn.dom.parentNode, false);
43291     } 
43292
43293     /** 
43294     * @cfg {Boolean} grow 
43295     * @hide 
43296     */
43297     /** 
43298     * @cfg {Number} growMin 
43299     * @hide 
43300     */
43301     /** 
43302     * @cfg {Number} growMax 
43303     * @hide 
43304     */
43305     /**
43306      * @hide
43307      * @method autoSize
43308      */
43309 });/*
43310  * Copyright(c) 2010-2012, Roo J Solutions Limited
43311  *
43312  * Licence LGPL
43313  *
43314  */
43315
43316 /**
43317  * @class Roo.form.ComboBoxArray
43318  * @extends Roo.form.TextField
43319  * A facebook style adder... for lists of email / people / countries  etc...
43320  * pick multiple items from a combo box, and shows each one.
43321  *
43322  *  Fred [x]  Brian [x]  [Pick another |v]
43323  *
43324  *
43325  *  For this to work: it needs various extra information
43326  *    - normal combo problay has
43327  *      name, hiddenName
43328  *    + displayField, valueField
43329  *
43330  *    For our purpose...
43331  *
43332  *
43333  *   If we change from 'extends' to wrapping...
43334  *   
43335  *  
43336  *
43337  
43338  
43339  * @constructor
43340  * Create a new ComboBoxArray.
43341  * @param {Object} config Configuration options
43342  */
43343  
43344
43345 Roo.form.ComboBoxArray = function(config)
43346 {
43347     this.addEvents({
43348         /**
43349          * @event beforeremove
43350          * Fires before remove the value from the list
43351              * @param {Roo.form.ComboBoxArray} _self This combo box array
43352              * @param {Roo.form.ComboBoxArray.Item} item removed item
43353              */
43354         'beforeremove' : true,
43355         /**
43356          * @event remove
43357          * Fires when remove the value from the list
43358              * @param {Roo.form.ComboBoxArray} _self This combo box array
43359              * @param {Roo.form.ComboBoxArray.Item} item removed item
43360              */
43361         'remove' : true
43362         
43363         
43364     });
43365     
43366     Roo.form.ComboBoxArray.superclass.constructor.call(this, config);
43367     
43368     this.items = new Roo.util.MixedCollection(false);
43369     
43370     // construct the child combo...
43371     
43372     
43373     
43374     
43375    
43376     
43377 }
43378
43379  
43380 Roo.extend(Roo.form.ComboBoxArray, Roo.form.TextField,
43381
43382     /**
43383      * @cfg {Roo.form.ComboBox} combo [required] The combo box that is wrapped
43384      */
43385     
43386     lastData : false,
43387     
43388     // behavies liek a hiddne field
43389     inputType:      'hidden',
43390     /**
43391      * @cfg {Number} width The width of the box that displays the selected element
43392      */ 
43393     width:          300,
43394
43395     
43396     
43397     /**
43398      * @cfg {String} name    The name of the visable items on this form (eg. titles not ids)
43399      */
43400     name : false,
43401     /**
43402      * @cfg {String} hiddenName    The hidden name of the field, often contains an comma seperated list of names
43403      */
43404     hiddenName : false,
43405       /**
43406      * @cfg {String} seperator    The value seperator normally ',' 
43407      */
43408     seperator : ',',
43409     
43410     // private the array of items that are displayed..
43411     items  : false,
43412     // private - the hidden field el.
43413     hiddenEl : false,
43414     // private - the filed el..
43415     el : false,
43416     
43417     //validateValue : function() { return true; }, // all values are ok!
43418     //onAddClick: function() { },
43419     
43420     onRender : function(ct, position) 
43421     {
43422         
43423         // create the standard hidden element
43424         //Roo.form.ComboBoxArray.superclass.onRender.call(this, ct, position);
43425         
43426         
43427         // give fake names to child combo;
43428         this.combo.hiddenName = this.hiddenName ? (this.hiddenName+'-subcombo') : this.hiddenName;
43429         this.combo.name = this.name ? (this.name+'-subcombo') : this.name;
43430         
43431         this.combo = Roo.factory(this.combo, Roo.form);
43432         this.combo.onRender(ct, position);
43433         if (typeof(this.combo.width) != 'undefined') {
43434             this.combo.onResize(this.combo.width,0);
43435         }
43436         
43437         this.combo.initEvents();
43438         
43439         // assigned so form know we need to do this..
43440         this.store          = this.combo.store;
43441         this.valueField     = this.combo.valueField;
43442         this.displayField   = this.combo.displayField ;
43443         
43444         
43445         this.combo.wrap.addClass('x-cbarray-grp');
43446         
43447         var cbwrap = this.combo.wrap.createChild(
43448             {tag: 'div', cls: 'x-cbarray-cb'},
43449             this.combo.el.dom
43450         );
43451         
43452              
43453         this.hiddenEl = this.combo.wrap.createChild({
43454             tag: 'input',  type:'hidden' , name: this.hiddenName, value : ''
43455         });
43456         this.el = this.combo.wrap.createChild({
43457             tag: 'input',  type:'hidden' , name: this.name, value : ''
43458         });
43459          //   this.el.dom.removeAttribute("name");
43460         
43461         
43462         this.outerWrap = this.combo.wrap;
43463         this.wrap = cbwrap;
43464         
43465         this.outerWrap.setWidth(this.width);
43466         this.outerWrap.dom.removeChild(this.el.dom);
43467         
43468         this.wrap.dom.appendChild(this.el.dom);
43469         this.outerWrap.dom.removeChild(this.combo.trigger.dom);
43470         this.combo.wrap.dom.appendChild(this.combo.trigger.dom);
43471         
43472         this.combo.trigger.setStyle('position','relative');
43473         this.combo.trigger.setStyle('left', '0px');
43474         this.combo.trigger.setStyle('top', '2px');
43475         
43476         this.combo.el.setStyle('vertical-align', 'text-bottom');
43477         
43478         //this.trigger.setStyle('vertical-align', 'top');
43479         
43480         // this should use the code from combo really... on('add' ....)
43481         if (this.adder) {
43482             
43483         
43484             this.adder = this.outerWrap.createChild(
43485                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-adder', style: 'margin-left:2px'});  
43486             var _t = this;
43487             this.adder.on('click', function(e) {
43488                 _t.fireEvent('adderclick', this, e);
43489             }, _t);
43490         }
43491         //var _t = this;
43492         //this.adder.on('click', this.onAddClick, _t);
43493         
43494         
43495         this.combo.on('select', function(cb, rec, ix) {
43496             this.addItem(rec.data);
43497             
43498             cb.setValue('');
43499             cb.el.dom.value = '';
43500             //cb.lastData = rec.data;
43501             // add to list
43502             
43503         }, this);
43504         
43505         
43506     },
43507     
43508     
43509     getName: function()
43510     {
43511         // returns hidden if it's set..
43512         if (!this.rendered) {return ''};
43513         return  this.hiddenName ? this.hiddenName : this.name;
43514         
43515     },
43516     
43517     
43518     onResize: function(w, h){
43519         
43520         return;
43521         // not sure if this is needed..
43522         //this.combo.onResize(w,h);
43523         
43524         if(typeof w != 'number'){
43525             // we do not handle it!?!?
43526             return;
43527         }
43528         var tw = this.combo.trigger.getWidth();
43529         tw += this.addicon ? this.addicon.getWidth() : 0;
43530         tw += this.editicon ? this.editicon.getWidth() : 0;
43531         var x = w - tw;
43532         this.combo.el.setWidth( this.combo.adjustWidth('input', x));
43533             
43534         this.combo.trigger.setStyle('left', '0px');
43535         
43536         if(this.list && this.listWidth === undefined){
43537             var lw = Math.max(x + this.combo.trigger.getWidth(), this.combo.minListWidth);
43538             this.list.setWidth(lw);
43539             this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
43540         }
43541         
43542     
43543         
43544     },
43545     
43546     addItem: function(rec)
43547     {
43548         var valueField = this.combo.valueField;
43549         var displayField = this.combo.displayField;
43550         
43551         if (this.items.indexOfKey(rec[valueField]) > -1) {
43552             //console.log("GOT " + rec.data.id);
43553             return;
43554         }
43555         
43556         var x = new Roo.form.ComboBoxArray.Item({
43557             //id : rec[this.idField],
43558             data : rec,
43559             displayField : displayField ,
43560             tipField : displayField ,
43561             cb : this
43562         });
43563         // use the 
43564         this.items.add(rec[valueField],x);
43565         // add it before the element..
43566         this.updateHiddenEl();
43567         x.render(this.outerWrap, this.wrap.dom);
43568         // add the image handler..
43569     },
43570     
43571     updateHiddenEl : function()
43572     {
43573         this.validate();
43574         if (!this.hiddenEl) {
43575             return;
43576         }
43577         var ar = [];
43578         var idField = this.combo.valueField;
43579         
43580         this.items.each(function(f) {
43581             ar.push(f.data[idField]);
43582         });
43583         this.hiddenEl.dom.value = ar.join(this.seperator);
43584         this.validate();
43585     },
43586     
43587     reset : function()
43588     {
43589         this.items.clear();
43590         
43591         Roo.each(this.outerWrap.select('.x-cbarray-item', true).elements, function(el){
43592            el.remove();
43593         });
43594         
43595         this.el.dom.value = '';
43596         if (this.hiddenEl) {
43597             this.hiddenEl.dom.value = '';
43598         }
43599         
43600     },
43601     getValue: function()
43602     {
43603         return this.hiddenEl ? this.hiddenEl.dom.value : '';
43604     },
43605     setValue: function(v) // not a valid action - must use addItems..
43606     {
43607         
43608         this.reset();
43609          
43610         if (this.store.isLocal && (typeof(v) == 'string')) {
43611             // then we can use the store to find the values..
43612             // comma seperated at present.. this needs to allow JSON based encoding..
43613             this.hiddenEl.value  = v;
43614             var v_ar = [];
43615             Roo.each(v.split(this.seperator), function(k) {
43616                 Roo.log("CHECK " + this.valueField + ',' + k);
43617                 var li = this.store.query(this.valueField, k);
43618                 if (!li.length) {
43619                     return;
43620                 }
43621                 var add = {};
43622                 add[this.valueField] = k;
43623                 add[this.displayField] = li.item(0).data[this.displayField];
43624                 
43625                 this.addItem(add);
43626             }, this) 
43627              
43628         }
43629         if (typeof(v) == 'object' ) {
43630             // then let's assume it's an array of objects..
43631             Roo.each(v, function(l) {
43632                 var add = l;
43633                 if (typeof(l) == 'string') {
43634                     add = {};
43635                     add[this.valueField] = l;
43636                     add[this.displayField] = l
43637                 }
43638                 this.addItem(add);
43639             }, this);
43640              
43641         }
43642         
43643         
43644     },
43645     setFromData: function(v)
43646     {
43647         // this recieves an object, if setValues is called.
43648         this.reset();
43649         this.el.dom.value = v[this.displayField];
43650         this.hiddenEl.dom.value = v[this.valueField];
43651         if (typeof(v[this.valueField]) != 'string' || !v[this.valueField].length) {
43652             return;
43653         }
43654         var kv = v[this.valueField];
43655         var dv = v[this.displayField];
43656         kv = typeof(kv) != 'string' ? '' : kv;
43657         dv = typeof(dv) != 'string' ? '' : dv;
43658         
43659         
43660         var keys = kv.split(this.seperator);
43661         var display = dv.split(this.seperator);
43662         for (var i = 0 ; i < keys.length; i++) {
43663             add = {};
43664             add[this.valueField] = keys[i];
43665             add[this.displayField] = display[i];
43666             this.addItem(add);
43667         }
43668       
43669         
43670     },
43671     
43672     /**
43673      * Validates the combox array value
43674      * @return {Boolean} True if the value is valid, else false
43675      */
43676     validate : function(){
43677         if(this.disabled || this.validateValue(this.processValue(this.getValue()))){
43678             this.clearInvalid();
43679             return true;
43680         }
43681         return false;
43682     },
43683     
43684     validateValue : function(value){
43685         return Roo.form.ComboBoxArray.superclass.validateValue.call(this, this.getValue());
43686         
43687     },
43688     
43689     /*@
43690      * overide
43691      * 
43692      */
43693     isDirty : function() {
43694         if(this.disabled) {
43695             return false;
43696         }
43697         
43698         try {
43699             var d = Roo.decode(String(this.originalValue));
43700         } catch (e) {
43701             return String(this.getValue()) !== String(this.originalValue);
43702         }
43703         
43704         var originalValue = [];
43705         
43706         for (var i = 0; i < d.length; i++){
43707             originalValue.push(d[i][this.valueField]);
43708         }
43709         
43710         return String(this.getValue()) !== String(originalValue.join(this.seperator));
43711         
43712     }
43713     
43714 });
43715
43716
43717
43718 /**
43719  * @class Roo.form.ComboBoxArray.Item
43720  * @extends Roo.BoxComponent
43721  * A selected item in the list
43722  *  Fred [x]  Brian [x]  [Pick another |v]
43723  * 
43724  * @constructor
43725  * Create a new item.
43726  * @param {Object} config Configuration options
43727  */
43728  
43729 Roo.form.ComboBoxArray.Item = function(config) {
43730     config.id = Roo.id();
43731     Roo.form.ComboBoxArray.Item.superclass.constructor.call(this, config);
43732 }
43733
43734 Roo.extend(Roo.form.ComboBoxArray.Item, Roo.BoxComponent, {
43735     data : {},
43736     cb: false,
43737     displayField : false,
43738     tipField : false,
43739     
43740     
43741     defaultAutoCreate : {
43742         tag: 'div',
43743         cls: 'x-cbarray-item',
43744         cn : [ 
43745             { tag: 'div' },
43746             {
43747                 tag: 'img',
43748                 width:16,
43749                 height : 16,
43750                 src : Roo.BLANK_IMAGE_URL ,
43751                 align: 'center'
43752             }
43753         ]
43754         
43755     },
43756     
43757  
43758     onRender : function(ct, position)
43759     {
43760         Roo.form.Field.superclass.onRender.call(this, ct, position);
43761         
43762         if(!this.el){
43763             var cfg = this.getAutoCreate();
43764             this.el = ct.createChild(cfg, position);
43765         }
43766         
43767         this.el.child('img').dom.setAttribute('src', Roo.BLANK_IMAGE_URL);
43768         
43769         this.el.child('div').dom.innerHTML = this.cb.renderer ? 
43770             this.cb.renderer(this.data) :
43771             String.format('{0}',this.data[this.displayField]);
43772         
43773             
43774         this.el.child('div').dom.setAttribute('qtip',
43775                         String.format('{0}',this.data[this.tipField])
43776         );
43777         
43778         this.el.child('img').on('click', this.remove, this);
43779         
43780     },
43781    
43782     remove : function()
43783     {
43784         if(this.cb.disabled){
43785             return;
43786         }
43787         
43788         if(false !== this.cb.fireEvent('beforeremove', this.cb, this)){
43789             this.cb.items.remove(this);
43790             this.el.child('img').un('click', this.remove, this);
43791             this.el.remove();
43792             this.cb.updateHiddenEl();
43793
43794             this.cb.fireEvent('remove', this.cb, this);
43795         }
43796         
43797     }
43798 });/*
43799  * RooJS Library 1.1.1
43800  * Copyright(c) 2008-2011  Alan Knowles
43801  *
43802  * License - LGPL
43803  */
43804  
43805
43806 /**
43807  * @class Roo.form.ComboNested
43808  * @extends Roo.form.ComboBox
43809  * A combobox for that allows selection of nested items in a list,
43810  * eg.
43811  *
43812  *  Book
43813  *    -> red
43814  *    -> green
43815  *  Table
43816  *    -> square
43817  *      ->red
43818  *      ->green
43819  *    -> rectangle
43820  *      ->green
43821  *      
43822  * 
43823  * @constructor
43824  * Create a new ComboNested
43825  * @param {Object} config Configuration options
43826  */
43827 Roo.form.ComboNested = function(config){
43828     Roo.form.ComboCheck.superclass.constructor.call(this, config);
43829     // should verify some data...
43830     // like
43831     // hiddenName = required..
43832     // displayField = required
43833     // valudField == required
43834     var req= [ 'hiddenName', 'displayField', 'valueField' ];
43835     var _t = this;
43836     Roo.each(req, function(e) {
43837         if ((typeof(_t[e]) == 'undefined' ) || !_t[e].length) {
43838             throw "Roo.form.ComboNested : missing value for: " + e;
43839         }
43840     });
43841      
43842     
43843 };
43844
43845 Roo.extend(Roo.form.ComboNested, Roo.form.ComboBox, {
43846    
43847     /*
43848      * @config {Number} max Number of columns to show
43849      */
43850     
43851     maxColumns : 3,
43852    
43853     list : null, // the outermost div..
43854     innerLists : null, // the
43855     views : null,
43856     stores : null,
43857     // private
43858     loadingChildren : false,
43859     
43860     onRender : function(ct, position)
43861     {
43862         Roo.form.ComboBox.superclass.onRender.call(this, ct, position); // skip parent call - got to above..
43863         
43864         if(this.hiddenName){
43865             this.hiddenField = this.el.insertSibling({tag:'input', type:'hidden', name: this.hiddenName, id:  (this.hiddenId||this.hiddenName)},
43866                     'before', true);
43867             this.hiddenField.value =
43868                 this.hiddenValue !== undefined ? this.hiddenValue :
43869                 this.value !== undefined ? this.value : '';
43870
43871             // prevent input submission
43872             this.el.dom.removeAttribute('name');
43873              
43874              
43875         }
43876         
43877         if(Roo.isGecko){
43878             this.el.dom.setAttribute('autocomplete', 'off');
43879         }
43880
43881         var cls = 'x-combo-list';
43882
43883         this.list = new Roo.Layer({
43884             shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
43885         });
43886
43887         var lw = this.listWidth || Math.max(this.wrap.getWidth(), this.minListWidth);
43888         this.list.setWidth(lw);
43889         this.list.swallowEvent('mousewheel');
43890         this.assetHeight = 0;
43891
43892         if(this.title){
43893             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
43894             this.assetHeight += this.header.getHeight();
43895         }
43896         this.innerLists = [];
43897         this.views = [];
43898         this.stores = [];
43899         for (var i =0 ; i < this.maxColumns; i++) {
43900             this.onRenderList( cls, i);
43901         }
43902         
43903         // always needs footer, as we are going to have an 'OK' button.
43904         this.footer = this.list.createChild({cls:cls+'-ft'});
43905         this.pageTb = new Roo.Toolbar(this.footer);  
43906         var _this = this;
43907         this.pageTb.add(  {
43908             
43909             text: 'Done',
43910             handler: function()
43911             {
43912                 _this.collapse();
43913             }
43914         });
43915         
43916         if ( this.allowBlank && !this.disableClear) {
43917             
43918             this.pageTb.add(new Roo.Toolbar.Fill(), {
43919                 cls: 'x-btn-icon x-btn-clear',
43920                 text: '&#160;',
43921                 handler: function()
43922                 {
43923                     _this.collapse();
43924                     _this.clearValue();
43925                     _this.onSelect(false, -1);
43926                 }
43927             });
43928         }
43929         if (this.footer) {
43930             this.assetHeight += this.footer.getHeight();
43931         }
43932         
43933     },
43934     onRenderList : function (  cls, i)
43935     {
43936         
43937         var lw = Math.floor(
43938                 ((this.listWidth * this.maxColumns || Math.max(this.wrap.getWidth(), this.minListWidth)) - this.list.getFrameWidth('lr')) / this.maxColumns
43939         );
43940         
43941         this.list.setWidth(lw); // default to '1'
43942
43943         var il = this.innerLists[i] = this.list.createChild({cls:cls+'-inner'});
43944         //il.on('mouseover', this.onViewOver, this, { list:  i });
43945         //il.on('mousemove', this.onViewMove, this, { list:  i });
43946         il.setWidth(lw);
43947         il.setStyle({ 'overflow-x' : 'hidden'});
43948
43949         if(!this.tpl){
43950             this.tpl = new Roo.Template({
43951                 html :  '<div class="'+cls+'-item '+cls+'-item-{cn:this.isEmpty}">{' + this.displayField + '}</div>',
43952                 isEmpty: function (value, allValues) {
43953                     //Roo.log(value);
43954                     var dl = typeof(value.data) != 'undefined' ? value.data.length : value.length; ///json is a nested response..
43955                     return dl ? 'has-children' : 'no-children'
43956                 }
43957             });
43958         }
43959         
43960         var store  = this.store;
43961         if (i > 0) {
43962             store  = new Roo.data.SimpleStore({
43963                 //fields : this.store.reader.meta.fields,
43964                 reader : this.store.reader,
43965                 data : [ ]
43966             });
43967         }
43968         this.stores[i]  = store;
43969                   
43970         var view = this.views[i] = new Roo.View(
43971             il,
43972             this.tpl,
43973             {
43974                 singleSelect:true,
43975                 store: store,
43976                 selectedClass: this.selectedClass
43977             }
43978         );
43979         view.getEl().setWidth(lw);
43980         view.getEl().setStyle({
43981             position: i < 1 ? 'relative' : 'absolute',
43982             top: 0,
43983             left: (i * lw ) + 'px',
43984             display : i > 0 ? 'none' : 'block'
43985         });
43986         view.on('selectionchange', this.onSelectChange.createDelegate(this, {list : i }, true));
43987         view.on('dblclick', this.onDoubleClick.createDelegate(this, {list : i }, true));
43988         //view.on('click', this.onViewClick, this, { list : i });
43989
43990         store.on('beforeload', this.onBeforeLoad, this);
43991         store.on('load',  this.onLoad, this, { list  : i});
43992         store.on('loadexception', this.onLoadException, this);
43993
43994         // hide the other vies..
43995         
43996         
43997         
43998     },
43999       
44000     restrictHeight : function()
44001     {
44002         var mh = 0;
44003         Roo.each(this.innerLists, function(il,i) {
44004             var el = this.views[i].getEl();
44005             el.dom.style.height = '';
44006             var inner = el.dom;
44007             var h = Math.max(il.clientHeight, il.offsetHeight, il.scrollHeight);
44008             // only adjust heights on other ones..
44009             mh = Math.max(h, mh);
44010             if (i < 1) {
44011                 
44012                 el.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
44013                 il.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
44014                
44015             }
44016             
44017             
44018         }, this);
44019         
44020         this.list.beginUpdate();
44021         this.list.setHeight(mh+this.list.getFrameWidth('tb')+this.assetHeight);
44022         this.list.alignTo(this.el, this.listAlign);
44023         this.list.endUpdate();
44024         
44025     },
44026      
44027     
44028     // -- store handlers..
44029     // private
44030     onBeforeLoad : function()
44031     {
44032         if(!this.hasFocus){
44033             return;
44034         }
44035         this.innerLists[0].update(this.loadingText ?
44036                '<div class="loading-indicator">'+this.loadingText+'</div>' : '');
44037         this.restrictHeight();
44038         this.selectedIndex = -1;
44039     },
44040     // private
44041     onLoad : function(a,b,c,d)
44042     {
44043         if (!this.loadingChildren) {
44044             // then we are loading the top level. - hide the children
44045             for (var i = 1;i < this.views.length; i++) {
44046                 this.views[i].getEl().setStyle({ display : 'none' });
44047             }
44048             var lw = Math.floor(
44049                 ((this.listWidth * this.maxColumns || Math.max(this.wrap.getWidth(), this.minListWidth)) - this.list.getFrameWidth('lr')) / this.maxColumns
44050             );
44051         
44052              this.list.setWidth(lw); // default to '1'
44053
44054             
44055         }
44056         if(!this.hasFocus){
44057             return;
44058         }
44059         
44060         if(this.store.getCount() > 0) {
44061             this.expand();
44062             this.restrictHeight();   
44063         } else {
44064             this.onEmptyResults();
44065         }
44066         
44067         if (!this.loadingChildren) {
44068             this.selectActive();
44069         }
44070         /*
44071         this.stores[1].loadData([]);
44072         this.stores[2].loadData([]);
44073         this.views
44074         */    
44075     
44076         //this.el.focus();
44077     },
44078     
44079     
44080     // private
44081     onLoadException : function()
44082     {
44083         this.collapse();
44084         Roo.log(this.store.reader.jsonData);
44085         if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
44086             Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
44087         }
44088         
44089         
44090     },
44091     // no cleaning of leading spaces on blur here.
44092     cleanLeadingSpace : function(e) { },
44093     
44094
44095     onSelectChange : function (view, sels, opts )
44096     {
44097         var ix = view.getSelectedIndexes();
44098          
44099         if (opts.list > this.maxColumns - 2) {
44100             if (view.store.getCount()<  1) {
44101                 this.views[opts.list ].getEl().setStyle({ display :   'none' });
44102
44103             } else  {
44104                 if (ix.length) {
44105                     // used to clear ?? but if we are loading unselected 
44106                     this.setFromData(view.store.getAt(ix[0]).data);
44107                 }
44108                 
44109             }
44110             
44111             return;
44112         }
44113         
44114         if (!ix.length) {
44115             // this get's fired when trigger opens..
44116            // this.setFromData({});
44117             var str = this.stores[opts.list+1];
44118             str.data.clear(); // removeall wihtout the fire events..
44119             return;
44120         }
44121         
44122         var rec = view.store.getAt(ix[0]);
44123          
44124         this.setFromData(rec.data);
44125         this.fireEvent('select', this, rec, ix[0]);
44126         
44127         var lw = Math.floor(
44128              (
44129                 (this.listWidth * this.maxColumns || Math.max(this.wrap.getWidth(), this.minListWidth)) - this.list.getFrameWidth('lr')
44130              ) / this.maxColumns
44131         );
44132         this.loadingChildren = true;
44133         this.stores[opts.list+1].loadDataFromChildren( rec );
44134         this.loadingChildren = false;
44135         var dl = this.stores[opts.list+1]. getTotalCount();
44136         
44137         this.views[opts.list+1].getEl().setHeight( this.innerLists[0].getHeight());
44138         
44139         this.views[opts.list+1].getEl().setStyle({ display : dl ? 'block' : 'none' });
44140         for (var i = opts.list+2; i < this.views.length;i++) {
44141             this.views[i].getEl().setStyle({ display : 'none' });
44142         }
44143         
44144         this.innerLists[opts.list+1].setHeight( this.innerLists[0].getHeight());
44145         this.list.setWidth(lw * (opts.list + (dl ? 2 : 1)));
44146         
44147         if (this.isLoading) {
44148            // this.selectActive(opts.list);
44149         }
44150          
44151     },
44152     
44153     
44154     
44155     
44156     onDoubleClick : function()
44157     {
44158         this.collapse(); //??
44159     },
44160     
44161      
44162     
44163     
44164     
44165     // private
44166     recordToStack : function(store, prop, value, stack)
44167     {
44168         var cstore = new Roo.data.SimpleStore({
44169             //fields : this.store.reader.meta.fields, // we need array reader.. for
44170             reader : this.store.reader,
44171             data : [ ]
44172         });
44173         var _this = this;
44174         var record  = false;
44175         var srec = false;
44176         if(store.getCount() < 1){
44177             return false;
44178         }
44179         store.each(function(r){
44180             if(r.data[prop] == value){
44181                 record = r;
44182             srec = r;
44183                 return false;
44184             }
44185             if (r.data.cn && r.data.cn.length) {
44186                 cstore.loadDataFromChildren( r);
44187                 var cret = _this.recordToStack(cstore, prop, value, stack);
44188                 if (cret !== false) {
44189                     record = cret;
44190                     srec = r;
44191                     return false;
44192                 }
44193             }
44194              
44195             return true;
44196         });
44197         if (record == false) {
44198             return false
44199         }
44200         stack.unshift(srec);
44201         return record;
44202     },
44203     
44204     /*
44205      * find the stack of stores that match our value.
44206      *
44207      * 
44208      */
44209     
44210     selectActive : function ()
44211     {
44212         // if store is not loaded, then we will need to wait for that to happen first.
44213         var stack = [];
44214         this.recordToStack(this.store, this.valueField, this.getValue(), stack);
44215         for (var i = 0; i < stack.length; i++ ) {
44216             this.views[i].select(stack[i].store.indexOf(stack[i]), false, false );
44217         }
44218         
44219     }
44220         
44221          
44222     
44223     
44224     
44225     
44226 });/*
44227  * Based on:
44228  * Ext JS Library 1.1.1
44229  * Copyright(c) 2006-2007, Ext JS, LLC.
44230  *
44231  * Originally Released Under LGPL - original licence link has changed is not relivant.
44232  *
44233  * Fork - LGPL
44234  * <script type="text/javascript">
44235  */
44236 /**
44237  * @class Roo.form.Checkbox
44238  * @extends Roo.form.Field
44239  * Single checkbox field.  Can be used as a direct replacement for traditional checkbox fields.
44240  * @constructor
44241  * Creates a new Checkbox
44242  * @param {Object} config Configuration options
44243  */
44244 Roo.form.Checkbox = function(config){
44245     Roo.form.Checkbox.superclass.constructor.call(this, config);
44246     this.addEvents({
44247         /**
44248          * @event check
44249          * Fires when the checkbox is checked or unchecked.
44250              * @param {Roo.form.Checkbox} this This checkbox
44251              * @param {Boolean} checked The new checked value
44252              */
44253         check : true
44254     });
44255 };
44256
44257 Roo.extend(Roo.form.Checkbox, Roo.form.Field,  {
44258     /**
44259      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
44260      */
44261     focusClass : undefined,
44262     /**
44263      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
44264      */
44265     fieldClass: "x-form-field",
44266     /**
44267      * @cfg {Boolean} checked True if the the checkbox should render already checked (defaults to false)
44268      */
44269     checked: false,
44270     /**
44271      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
44272      * {tag: "input", type: "checkbox", autocomplete: "off"})
44273      */
44274     defaultAutoCreate : { tag: "input", type: 'hidden', autocomplete: "off"},
44275     /**
44276      * @cfg {String} boxLabel The text that appears beside the checkbox
44277      */
44278     boxLabel : "",
44279     /**
44280      * @cfg {String} inputValue The value that should go into the generated input element's value attribute
44281      */  
44282     inputValue : '1',
44283     /**
44284      * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
44285      */
44286      valueOff: '0', // value when not checked..
44287
44288     actionMode : 'viewEl', 
44289     //
44290     // private
44291     itemCls : 'x-menu-check-item x-form-item',
44292     groupClass : 'x-menu-group-item',
44293     inputType : 'hidden',
44294     
44295     
44296     inSetChecked: false, // check that we are not calling self...
44297     
44298     inputElement: false, // real input element?
44299     basedOn: false, // ????
44300     
44301     isFormField: true, // not sure where this is needed!!!!
44302
44303     onResize : function(){
44304         Roo.form.Checkbox.superclass.onResize.apply(this, arguments);
44305         if(!this.boxLabel){
44306             this.el.alignTo(this.wrap, 'c-c');
44307         }
44308     },
44309
44310     initEvents : function(){
44311         Roo.form.Checkbox.superclass.initEvents.call(this);
44312         this.el.on("click", this.onClick,  this);
44313         this.el.on("change", this.onClick,  this);
44314     },
44315
44316
44317     getResizeEl : function(){
44318         return this.wrap;
44319     },
44320
44321     getPositionEl : function(){
44322         return this.wrap;
44323     },
44324
44325     // private
44326     onRender : function(ct, position){
44327         Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
44328         /*
44329         if(this.inputValue !== undefined){
44330             this.el.dom.value = this.inputValue;
44331         }
44332         */
44333         //this.wrap = this.el.wrap({cls: "x-form-check-wrap"});
44334         this.wrap = this.el.wrap({cls: 'x-menu-check-item '});
44335         var viewEl = this.wrap.createChild({ 
44336             tag: 'img', cls: 'x-menu-item-icon', style: 'margin: 0px;' ,src : Roo.BLANK_IMAGE_URL });
44337         this.viewEl = viewEl;   
44338         this.wrap.on('click', this.onClick,  this); 
44339         
44340         this.el.on('DOMAttrModified', this.setFromHidden,  this); //ff
44341         this.el.on('propertychange', this.setFromHidden,  this);  //ie
44342         
44343         
44344         
44345         if(this.boxLabel){
44346             this.wrap.createChild({tag: 'label', htmlFor: this.el.id, cls: 'x-form-cb-label', html: this.boxLabel});
44347         //    viewEl.on('click', this.onClick,  this); 
44348         }
44349         //if(this.checked){
44350             this.setChecked(this.checked);
44351         //}else{
44352             //this.checked = this.el.dom;
44353         //}
44354
44355     },
44356
44357     // private
44358     initValue : Roo.emptyFn,
44359
44360     /**
44361      * Returns the checked state of the checkbox.
44362      * @return {Boolean} True if checked, else false
44363      */
44364     getValue : function(){
44365         if(this.el){
44366             return String(this.el.dom.value) == String(this.inputValue ) ? this.inputValue : this.valueOff;
44367         }
44368         return this.valueOff;
44369         
44370     },
44371
44372         // private
44373     onClick : function(){ 
44374         if (this.disabled) {
44375             return;
44376         }
44377         this.setChecked(!this.checked);
44378
44379         //if(this.el.dom.checked != this.checked){
44380         //    this.setValue(this.el.dom.checked);
44381        // }
44382     },
44383
44384     /**
44385      * Sets the checked state of the checkbox.
44386      * On is always based on a string comparison between inputValue and the param.
44387      * @param {Boolean/String} value - the value to set 
44388      * @param {Boolean/String} suppressEvent - whether to suppress the checkchange event.
44389      */
44390     setValue : function(v,suppressEvent){
44391         
44392         
44393         //this.checked = (v === true || v === 'true' || v == '1' || String(v).toLowerCase() == 'on');
44394         //if(this.el && this.el.dom){
44395         //    this.el.dom.checked = this.checked;
44396         //    this.el.dom.defaultChecked = this.checked;
44397         //}
44398         this.setChecked(String(v) === String(this.inputValue), suppressEvent);
44399         //this.fireEvent("check", this, this.checked);
44400     },
44401     // private..
44402     setChecked : function(state,suppressEvent)
44403     {
44404         if (this.inSetChecked) {
44405             this.checked = state;
44406             return;
44407         }
44408         
44409     
44410         if(this.wrap){
44411             this.wrap[state ? 'addClass' : 'removeClass']('x-menu-item-checked');
44412         }
44413         this.checked = state;
44414         if(suppressEvent !== true){
44415             this.fireEvent('check', this, state);
44416         }
44417         this.inSetChecked = true;
44418         this.el.dom.value = state ? this.inputValue : this.valueOff;
44419         this.inSetChecked = false;
44420         
44421     },
44422     // handle setting of hidden value by some other method!!?!?
44423     setFromHidden: function()
44424     {
44425         if(!this.el){
44426             return;
44427         }
44428         //console.log("SET FROM HIDDEN");
44429         //alert('setFrom hidden');
44430         this.setValue(this.el.dom.value);
44431     },
44432     
44433     onDestroy : function()
44434     {
44435         if(this.viewEl){
44436             Roo.get(this.viewEl).remove();
44437         }
44438          
44439         Roo.form.Checkbox.superclass.onDestroy.call(this);
44440     },
44441     
44442     setBoxLabel : function(str)
44443     {
44444         this.wrap.select('.x-form-cb-label', true).first().dom.innerHTML = str;
44445     }
44446
44447 });/*
44448  * Based on:
44449  * Ext JS Library 1.1.1
44450  * Copyright(c) 2006-2007, Ext JS, LLC.
44451  *
44452  * Originally Released Under LGPL - original licence link has changed is not relivant.
44453  *
44454  * Fork - LGPL
44455  * <script type="text/javascript">
44456  */
44457  
44458 /**
44459  * @class Roo.form.Radio
44460  * @extends Roo.form.Checkbox
44461  * Single radio field.  Same as Checkbox, but provided as a convenience for automatically setting the input type.
44462  * Radio grouping is handled automatically by the browser if you give each radio in a group the same name.
44463  * @constructor
44464  * Creates a new Radio
44465  * @param {Object} config Configuration options
44466  */
44467 Roo.form.Radio = function(){
44468     Roo.form.Radio.superclass.constructor.apply(this, arguments);
44469 };
44470 Roo.extend(Roo.form.Radio, Roo.form.Checkbox, {
44471     inputType: 'radio',
44472
44473     /**
44474      * If this radio is part of a group, it will return the selected value
44475      * @return {String}
44476      */
44477     getGroupValue : function(){
44478         return this.el.up('form').child('input[name='+this.el.dom.name+']:checked', true).value;
44479     },
44480     
44481     
44482     onRender : function(ct, position){
44483         Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
44484         
44485         if(this.inputValue !== undefined){
44486             this.el.dom.value = this.inputValue;
44487         }
44488          
44489         this.wrap = this.el.wrap({cls: "x-form-check-wrap"});
44490         //this.wrap = this.el.wrap({cls: 'x-menu-check-item '});
44491         //var viewEl = this.wrap.createChild({ 
44492         //    tag: 'img', cls: 'x-menu-item-icon', style: 'margin: 0px;' ,src : Roo.BLANK_IMAGE_URL });
44493         //this.viewEl = viewEl;   
44494         //this.wrap.on('click', this.onClick,  this); 
44495         
44496         //this.el.on('DOMAttrModified', this.setFromHidden,  this); //ff
44497         //this.el.on('propertychange', this.setFromHidden,  this);  //ie
44498         
44499         
44500         
44501         if(this.boxLabel){
44502             this.wrap.createChild({tag: 'label', htmlFor: this.el.id, cls: 'x-form-cb-label', html: this.boxLabel});
44503         //    viewEl.on('click', this.onClick,  this); 
44504         }
44505          if(this.checked){
44506             this.el.dom.checked =   'checked' ;
44507         }
44508          
44509     } 
44510     
44511     
44512 });Roo.rtf = {}; // namespace
44513 Roo.rtf.Hex = function(hex)
44514 {
44515     this.hexstr = hex;
44516 };
44517 Roo.rtf.Paragraph = function(opts)
44518 {
44519     this.content = []; ///??? is that used?
44520 };Roo.rtf.Span = function(opts)
44521 {
44522     this.value = opts.value;
44523 };
44524
44525 Roo.rtf.Group = function(parent)
44526 {
44527     // we dont want to acutally store parent - it will make debug a nightmare..
44528     this.content = [];
44529     this.cn  = [];
44530      
44531        
44532     
44533 };
44534
44535 Roo.rtf.Group.prototype = {
44536     ignorable : false,
44537     content: false,
44538     cn: false,
44539     addContent : function(node) {
44540         // could set styles...
44541         this.content.push(node);
44542     },
44543     addChild : function(cn)
44544     {
44545         this.cn.push(cn);
44546     },
44547     // only for images really...
44548     toDataURL : function()
44549     {
44550         var mimetype = false;
44551         switch(true) {
44552             case this.content.filter(function(a) { return a.value == 'pngblip' } ).length > 0: 
44553                 mimetype = "image/png";
44554                 break;
44555              case this.content.filter(function(a) { return a.value == 'jpegblip' } ).length > 0:
44556                 mimetype = "image/jpeg";
44557                 break;
44558             default :
44559                 return 'about:blank'; // ?? error?
44560         }
44561         
44562         
44563         var hexstring = this.content[this.content.length-1].value;
44564         
44565         return 'data:' + mimetype + ';base64,' + btoa(hexstring.match(/\w{2}/g).map(function(a) {
44566             return String.fromCharCode(parseInt(a, 16));
44567         }).join(""));
44568     }
44569     
44570 };
44571 // this looks like it's normally the {rtf{ .... }}
44572 Roo.rtf.Document = function()
44573 {
44574     // we dont want to acutally store parent - it will make debug a nightmare..
44575     this.rtlch  = [];
44576     this.content = [];
44577     this.cn = [];
44578     
44579 };
44580 Roo.extend(Roo.rtf.Document, Roo.rtf.Group, { 
44581     addChild : function(cn)
44582     {
44583         this.cn.push(cn);
44584         switch(cn.type) {
44585             case 'rtlch': // most content seems to be inside this??
44586             case 'listtext':
44587             case 'shpinst':
44588                 this.rtlch.push(cn);
44589                 return;
44590             default:
44591                 this[cn.type] = cn;
44592         }
44593         
44594     },
44595     
44596     getElementsByType : function(type)
44597     {
44598         var ret =  [];
44599         this._getElementsByType(type, ret, this.cn, 'rtf');
44600         return ret;
44601     },
44602     _getElementsByType : function (type, ret, search_array, path)
44603     {
44604         search_array.forEach(function(n,i) {
44605             if (n.type == type) {
44606                 n.path = path + '/' + n.type + ':' + i;
44607                 ret.push(n);
44608             }
44609             if (n.cn.length > 0) {
44610                 this._getElementsByType(type, ret, n.cn, path + '/' + n.type+':'+i);
44611             }
44612         },this);
44613     }
44614     
44615 });
44616  
44617 Roo.rtf.Ctrl = function(opts)
44618 {
44619     this.value = opts.value;
44620     this.param = opts.param;
44621 };
44622 /**
44623  *
44624  *
44625  * based on this https://github.com/iarna/rtf-parser
44626  * it's really only designed to extract pict from pasted RTF 
44627  *
44628  * usage:
44629  *
44630  *  var images = new Roo.rtf.Parser().parse(a_string).filter(function(g) { return g.type == 'pict'; });
44631  *  
44632  *
44633  */
44634
44635  
44636
44637
44638
44639 Roo.rtf.Parser = function(text) {
44640     //super({objectMode: true})
44641     this.text = '';
44642     this.parserState = this.parseText;
44643     
44644     // these are for interpeter...
44645     this.doc = {};
44646     ///this.parserState = this.parseTop
44647     this.groupStack = [];
44648     this.hexStore = [];
44649     this.doc = false;
44650     
44651     this.groups = []; // where we put the return.
44652     
44653     for (var ii = 0; ii < text.length; ++ii) {
44654         ++this.cpos;
44655         
44656         if (text[ii] === '\n') {
44657             ++this.row;
44658             this.col = 1;
44659         } else {
44660             ++this.col;
44661         }
44662         this.parserState(text[ii]);
44663     }
44664     
44665     
44666     
44667 };
44668 Roo.rtf.Parser.prototype = {
44669     text : '', // string being parsed..
44670     controlWord : '',
44671     controlWordParam :  '',
44672     hexChar : '',
44673     doc : false,
44674     group: false,
44675     groupStack : false,
44676     hexStore : false,
44677     
44678     
44679     cpos : 0, 
44680     row : 1, // reportin?
44681     col : 1, //
44682
44683      
44684     push : function (el)
44685     {
44686         var m = 'cmd'+ el.type;
44687         if (typeof(this[m]) == 'undefined') {
44688             Roo.log('invalid cmd:' + el.type);
44689             return;
44690         }
44691         this[m](el);
44692         //Roo.log(el);
44693     },
44694     flushHexStore : function()
44695     {
44696         if (this.hexStore.length < 1) {
44697             return;
44698         }
44699         var hexstr = this.hexStore.map(
44700             function(cmd) {
44701                 return cmd.value;
44702         }).join('');
44703         
44704         this.group.addContent( new Roo.rtf.Hex( hexstr ));
44705               
44706             
44707         this.hexStore.splice(0)
44708         
44709     },
44710     
44711     cmdgroupstart : function()
44712     {
44713         this.flushHexStore();
44714         if (this.group) {
44715             this.groupStack.push(this.group);
44716         }
44717          // parent..
44718         if (this.doc === false) {
44719             this.group = this.doc = new Roo.rtf.Document();
44720             return;
44721             
44722         }
44723         this.group = new Roo.rtf.Group(this.group);
44724     },
44725     cmdignorable : function()
44726     {
44727         this.flushHexStore();
44728         this.group.ignorable = true;
44729     },
44730     cmdendparagraph : function()
44731     {
44732         this.flushHexStore();
44733         this.group.addContent(new Roo.rtf.Paragraph());
44734     },
44735     cmdgroupend : function ()
44736     {
44737         this.flushHexStore();
44738         var endingGroup = this.group;
44739         
44740         
44741         this.group = this.groupStack.pop();
44742         if (this.group) {
44743             this.group.addChild(endingGroup);
44744         }
44745         
44746         
44747         
44748         var doc = this.group || this.doc;
44749         //if (endingGroup instanceof FontTable) {
44750         //  doc.fonts = endingGroup.table
44751         //} else if (endingGroup instanceof ColorTable) {
44752         //  doc.colors = endingGroup.table
44753         //} else if (endingGroup !== this.doc && !endingGroup.get('ignorable')) {
44754         if (endingGroup.ignorable === false) {
44755             //code
44756             this.groups.push(endingGroup);
44757            // Roo.log( endingGroup );
44758         }
44759             //Roo.each(endingGroup.content, function(item)) {
44760             //    doc.addContent(item);
44761             //}
44762             //process.emit('debug', 'GROUP END', endingGroup.type, endingGroup.get('ignorable'))
44763         //}
44764     },
44765     cmdtext : function (cmd)
44766     {
44767         this.flushHexStore();
44768         if (!this.group) { // an RTF fragment, missing the {\rtf1 header
44769             //this.group = this.doc
44770         }
44771         this.group.addContent(new Roo.rtf.Span(cmd));
44772     },
44773     cmdcontrolword : function (cmd)
44774     {
44775         this.flushHexStore();
44776         if (!this.group.type) {
44777             this.group.type = cmd.value;
44778             return;
44779         }
44780         this.group.addContent(new Roo.rtf.Ctrl(cmd));
44781         // we actually don't care about ctrl words...
44782         return ;
44783         /*
44784         var method = 'ctrl$' + cmd.value.replace(/-(.)/g, (_, char) => char.toUpperCase())
44785         if (this[method]) {
44786             this[method](cmd.param)
44787         } else {
44788             if (!this.group.get('ignorable')) process.emit('debug', method, cmd.param)
44789         }
44790         */
44791     },
44792     cmdhexchar : function(cmd) {
44793         this.hexStore.push(cmd);
44794     },
44795     cmderror : function(cmd) {
44796         throw new Exception (cmd.value);
44797     },
44798     
44799     /*
44800       _flush (done) {
44801         if (this.text !== '\u0000') this.emitText()
44802         done()
44803       }
44804       */
44805       
44806       
44807     parseText : function(c)
44808     {
44809         if (c === '\\') {
44810             this.parserState = this.parseEscapes;
44811         } else if (c === '{') {
44812             this.emitStartGroup();
44813         } else if (c === '}') {
44814             this.emitEndGroup();
44815         } else if (c === '\x0A' || c === '\x0D') {
44816             // cr/lf are noise chars
44817         } else {
44818             this.text += c;
44819         }
44820     },
44821     
44822     parseEscapes: function (c)
44823     {
44824         if (c === '\\' || c === '{' || c === '}') {
44825             this.text += c;
44826             this.parserState = this.parseText;
44827         } else {
44828             this.parserState = this.parseControlSymbol;
44829             this.parseControlSymbol(c);
44830         }
44831     },
44832     parseControlSymbol: function(c)
44833     {
44834         if (c === '~') {
44835             this.text += '\u00a0'; // nbsp
44836             this.parserState = this.parseText
44837         } else if (c === '-') {
44838              this.text += '\u00ad'; // soft hyphen
44839         } else if (c === '_') {
44840             this.text += '\u2011'; // non-breaking hyphen
44841         } else if (c === '*') {
44842             this.emitIgnorable();
44843             this.parserState = this.parseText;
44844         } else if (c === "'") {
44845             this.parserState = this.parseHexChar;
44846         } else if (c === '|') { // formula cacter
44847             this.emitFormula();
44848             this.parserState = this.parseText;
44849         } else if (c === ':') { // subentry in an index entry
44850             this.emitIndexSubEntry();
44851             this.parserState = this.parseText;
44852         } else if (c === '\x0a') {
44853             this.emitEndParagraph();
44854             this.parserState = this.parseText;
44855         } else if (c === '\x0d') {
44856             this.emitEndParagraph();
44857             this.parserState = this.parseText;
44858         } else {
44859             this.parserState = this.parseControlWord;
44860             this.parseControlWord(c);
44861         }
44862     },
44863     parseHexChar: function (c)
44864     {
44865         if (/^[A-Fa-f0-9]$/.test(c)) {
44866             this.hexChar += c;
44867             if (this.hexChar.length >= 2) {
44868               this.emitHexChar();
44869               this.parserState = this.parseText;
44870             }
44871             return;
44872         }
44873         this.emitError("Invalid character \"" + c + "\" in hex literal.");
44874         this.parserState = this.parseText;
44875         
44876     },
44877     parseControlWord : function(c)
44878     {
44879         if (c === ' ') {
44880             this.emitControlWord();
44881             this.parserState = this.parseText;
44882         } else if (/^[-\d]$/.test(c)) {
44883             this.parserState = this.parseControlWordParam;
44884             this.controlWordParam += c;
44885         } else if (/^[A-Za-z]$/.test(c)) {
44886           this.controlWord += c;
44887         } else {
44888           this.emitControlWord();
44889           this.parserState = this.parseText;
44890           this.parseText(c);
44891         }
44892     },
44893     parseControlWordParam : function (c) {
44894         if (/^\d$/.test(c)) {
44895           this.controlWordParam += c;
44896         } else if (c === ' ') {
44897           this.emitControlWord();
44898           this.parserState = this.parseText;
44899         } else {
44900           this.emitControlWord();
44901           this.parserState = this.parseText;
44902           this.parseText(c);
44903         }
44904     },
44905     
44906     
44907     
44908     
44909     emitText : function () {
44910         if (this.text === '') {
44911             return;
44912         }
44913         this.push({
44914             type: 'text',
44915             value: this.text,
44916             pos: this.cpos,
44917             row: this.row,
44918             col: this.col
44919         });
44920         this.text = ''
44921     },
44922     emitControlWord : function ()
44923     {
44924         this.emitText();
44925         if (this.controlWord === '') {
44926             this.emitError('empty control word');
44927         } else {
44928             this.push({
44929                   type: 'controlword',
44930                   value: this.controlWord,
44931                   param: this.controlWordParam !== '' && Number(this.controlWordParam),
44932                   pos: this.cpos,
44933                   row: this.row,
44934                   col: this.col
44935             });
44936         }
44937         this.controlWord = '';
44938         this.controlWordParam = '';
44939     },
44940     emitStartGroup : function ()
44941     {
44942         this.emitText();
44943         this.push({
44944             type: 'groupstart',
44945             pos: this.cpos,
44946             row: this.row,
44947             col: this.col
44948         });
44949     },
44950     emitEndGroup : function ()
44951     {
44952         this.emitText();
44953         this.push({
44954             type: 'groupend',
44955             pos: this.cpos,
44956             row: this.row,
44957             col: this.col
44958         });
44959     },
44960     emitIgnorable : function ()
44961     {
44962         this.emitText();
44963         this.push({
44964             type: 'ignorable',
44965             pos: this.cpos,
44966             row: this.row,
44967             col: this.col
44968         });
44969     },
44970     emitHexChar : function ()
44971     {
44972         this.emitText();
44973         this.push({
44974             type: 'hexchar',
44975             value: this.hexChar,
44976             pos: this.cpos,
44977             row: this.row,
44978             col: this.col
44979         });
44980         this.hexChar = ''
44981     },
44982     emitError : function (message)
44983     {
44984       this.emitText();
44985       this.push({
44986             type: 'error',
44987             value: message,
44988             row: this.row,
44989             col: this.col,
44990             char: this.cpos //,
44991             //stack: new Error().stack
44992         });
44993     },
44994     emitEndParagraph : function () {
44995         this.emitText();
44996         this.push({
44997             type: 'endparagraph',
44998             pos: this.cpos,
44999             row: this.row,
45000             col: this.col
45001         });
45002     }
45003      
45004 } ;
45005 Roo.htmleditor = {};
45006  
45007 /**
45008  * @class Roo.htmleditor.Filter
45009  * Base Class for filtering htmleditor stuff. - do not use this directly - extend it.
45010  * @cfg {DomElement} node The node to iterate and filter
45011  * @cfg {boolean|String|Array} tag Tags to replace 
45012  * @constructor
45013  * Create a new Filter.
45014  * @param {Object} config Configuration options
45015  */
45016
45017
45018
45019 Roo.htmleditor.Filter = function(cfg) {
45020     Roo.apply(this.cfg);
45021     // this does not actually call walk as it's really just a abstract class
45022 }
45023
45024
45025 Roo.htmleditor.Filter.prototype = {
45026     
45027     node: false,
45028     
45029     tag: false,
45030
45031     // overrride to do replace comments.
45032     replaceComment : false,
45033     
45034     // overrride to do replace or do stuff with tags..
45035     replaceTag : false,
45036     
45037     walk : function(dom)
45038     {
45039         Roo.each( Array.from(dom.childNodes), function( e ) {
45040             switch(true) {
45041                 
45042                 case e.nodeType == 8 && typeof(this.replaceComment) != 'undefined': // comment
45043                     this.replaceComment(e);
45044                     return;
45045                 
45046                 case e.nodeType != 1: //not a node.
45047                     return;
45048                 
45049                 case this.tag === true: // everything
45050                 case typeof(this.tag) == 'object' && this.tag.indexOf(e.tagName) > -1: // array and it matches.
45051                 case typeof(this.tag) == 'string' && this.tag == e.tagName: // array and it matches.
45052                     if (this.replaceTag && false === this.replaceTag(e)) {
45053                         return;
45054                     }
45055                     if (e.hasChildNodes()) {
45056                         this.walk(e);
45057                     }
45058                     return;
45059                 
45060                 default:    // tags .. that do not match.
45061                     if (e.hasChildNodes()) {
45062                         this.walk(e);
45063                     }
45064             }
45065             
45066         }, this);
45067         
45068     }
45069 }; 
45070
45071 /**
45072  * @class Roo.htmleditor.FilterAttributes
45073  * clean attributes and  styles including http:// etc.. in attribute
45074  * @constructor
45075 * Run a new Attribute Filter
45076 * @param {Object} config Configuration options
45077  */
45078 Roo.htmleditor.FilterAttributes = function(cfg)
45079 {
45080     Roo.apply(this, cfg);
45081     this.attrib_black = this.attrib_black || [];
45082     this.attrib_white = this.attrib_white || [];
45083
45084     this.attrib_clean = this.attrib_clean || [];
45085     this.style_white = this.style_white || [];
45086     this.style_black = this.style_black || [];
45087     this.walk(cfg.node);
45088 }
45089
45090 Roo.extend(Roo.htmleditor.FilterAttributes, Roo.htmleditor.Filter,
45091 {
45092     tag: true, // all tags
45093     
45094     attrib_black : false, // array
45095     attrib_clean : false,
45096     attrib_white : false,
45097
45098     style_white : false,
45099     style_black : false,
45100      
45101      
45102     replaceTag : function(node)
45103     {
45104         if (!node.attributes || !node.attributes.length) {
45105             return true;
45106         }
45107         
45108         for (var i = node.attributes.length-1; i > -1 ; i--) {
45109             var a = node.attributes[i];
45110             //console.log(a);
45111             if (this.attrib_white.length && this.attrib_white.indexOf(a.name.toLowerCase()) < 0) {
45112                 node.removeAttribute(a.name);
45113                 continue;
45114             }
45115             
45116             
45117             
45118             if (a.name.toLowerCase().substr(0,2)=='on')  {
45119                 node.removeAttribute(a.name);
45120                 continue;
45121             }
45122             
45123             
45124             if (this.attrib_black.indexOf(a.name.toLowerCase()) > -1) {
45125                 node.removeAttribute(a.name);
45126                 continue;
45127             }
45128             if (this.attrib_clean.indexOf(a.name.toLowerCase()) > -1) {
45129                 this.cleanAttr(node,a.name,a.value); // fixme..
45130                 continue;
45131             }
45132             if (a.name == 'style') {
45133                 this.cleanStyle(node,a.name,a.value);
45134                 continue;
45135             }
45136             /// clean up MS crap..
45137             // tecnically this should be a list of valid class'es..
45138             
45139             
45140             if (a.name == 'class') {
45141                 if (a.value.match(/^Mso/)) {
45142                     node.removeAttribute('class');
45143                 }
45144                 
45145                 if (a.value.match(/^body$/)) {
45146                     node.removeAttribute('class');
45147                 }
45148                 continue;
45149             }
45150             
45151             
45152             // style cleanup!?
45153             // class cleanup?
45154             
45155         }
45156         return true; // clean children
45157     },
45158         
45159     cleanAttr: function(node, n,v)
45160     {
45161         
45162         if (v.match(/^\./) || v.match(/^\//)) {
45163             return;
45164         }
45165         if (v.match(/^(http|https):\/\//)
45166             || v.match(/^mailto:/) 
45167             || v.match(/^ftp:/)
45168             || v.match(/^data:/)
45169             ) {
45170             return;
45171         }
45172         if (v.match(/^#/)) {
45173             return;
45174         }
45175         if (v.match(/^\{/)) { // allow template editing.
45176             return;
45177         }
45178 //            Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
45179         node.removeAttribute(n);
45180         
45181     },
45182     cleanStyle : function(node,  n,v)
45183     {
45184         if (v.match(/expression/)) { //XSS?? should we even bother..
45185             node.removeAttribute(n);
45186             return;
45187         }
45188         
45189         var parts = v.split(/;/);
45190         var clean = [];
45191         
45192         Roo.each(parts, function(p) {
45193             p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
45194             if (!p.length) {
45195                 return true;
45196             }
45197             var l = p.split(':').shift().replace(/\s+/g,'');
45198             l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
45199             
45200             if ( this.style_black.length && (this.style_black.indexOf(l) > -1 || this.style_black.indexOf(l.toLowerCase()) > -1)) {
45201                 return true;
45202             }
45203             //Roo.log()
45204             // only allow 'c whitelisted system attributes'
45205             if ( this.style_white.length &&  style_white.indexOf(l) < 0 && style_white.indexOf(l.toLowerCase()) < 0 ) {
45206                 return true;
45207             }
45208             
45209             
45210             clean.push(p);
45211             return true;
45212         },this);
45213         if (clean.length) { 
45214             node.setAttribute(n, clean.join(';'));
45215         } else {
45216             node.removeAttribute(n);
45217         }
45218         
45219     }
45220         
45221         
45222         
45223     
45224 });/**
45225  * @class Roo.htmleditor.FilterBlack
45226  * remove blacklisted elements.
45227  * @constructor
45228  * Run a new Blacklisted Filter
45229  * @param {Object} config Configuration options
45230  */
45231
45232 Roo.htmleditor.FilterBlack = function(cfg)
45233 {
45234     Roo.apply(this, cfg);
45235     this.walk(cfg.node);
45236 }
45237
45238 Roo.extend(Roo.htmleditor.FilterBlack, Roo.htmleditor.Filter,
45239 {
45240     tag : true, // all elements.
45241    
45242     replace : function(n)
45243     {
45244         n.parentNode.removeChild(n);
45245     }
45246 });
45247 /**
45248  * @class Roo.htmleditor.FilterComment
45249  * remove comments.
45250  * @constructor
45251 * Run a new Comments Filter
45252 * @param {Object} config Configuration options
45253  */
45254 Roo.htmleditor.FilterComment = function(cfg)
45255 {
45256     this.walk(cfg.node);
45257 }
45258
45259 Roo.extend(Roo.htmleditor.FilterComment, Roo.htmleditor.Filter,
45260 {
45261   
45262     replaceComment : function(n)
45263     {
45264         n.parentNode.removeChild(n);
45265     }
45266 });/**
45267  * @class Roo.htmleditor.FilterKeepChildren
45268  * remove tags but keep children
45269  * @constructor
45270  * Run a new Keep Children Filter
45271  * @param {Object} config Configuration options
45272  */
45273
45274 Roo.htmleditor.FilterKeepChildren = function(cfg)
45275 {
45276     Roo.apply(this, cfg);
45277     if (this.tag === false) {
45278         return; // dont walk.. (you can use this to use this just to do a child removal on a single tag )
45279     }
45280     this.walk(cfg.node);
45281 }
45282
45283 Roo.extend(Roo.htmleditor.FilterKeepChildren, Roo.htmleditor.FilterBlack,
45284 {
45285     
45286   
45287     replaceTag : function(node)
45288     {
45289         // walk children...
45290         //Roo.log(node);
45291         var ar = Array.from(node.childNodes);
45292         //remove first..
45293         for (var i = 0; i < ar.length; i++) {
45294             if (ar[i].nodeType == 1) {
45295                 if (
45296                     (typeof(this.tag) == 'object' && this.tag.indexOf(ar[i].tagName) > -1)
45297                     || // array and it matches
45298                     (typeof(this.tag) == 'string' && this.tag == ar[i].tagName)
45299                 ) {
45300                     this.replaceTag(ar[i]); // child is blacklisted as well...
45301                     continue;
45302                 }
45303             }
45304         }  
45305         ar = Array.from(node.childNodes);
45306         for (var i = 0; i < ar.length; i++) {
45307          
45308             node.removeChild(ar[i]);
45309             // what if we need to walk these???
45310             node.parentNode.insertBefore(ar[i], node);
45311             if (this.tag !== false) {
45312                 this.walk(ar[i]);
45313                 
45314             }
45315         }
45316         node.parentNode.removeChild(node);
45317         return false; // don't walk children
45318         
45319         
45320     }
45321 });/**
45322  * @class Roo.htmleditor.FilterParagraph
45323  * paragraphs cause a nightmare for shared content - this filter is designed to be called ? at various points when editing
45324  * like on 'push' to remove the <p> tags and replace them with line breaks.
45325  * @constructor
45326  * Run a new Paragraph Filter
45327  * @param {Object} config Configuration options
45328  */
45329
45330 Roo.htmleditor.FilterParagraph = function(cfg)
45331 {
45332     // no need to apply config.
45333     this.walk(cfg.node);
45334 }
45335
45336 Roo.extend(Roo.htmleditor.FilterParagraph, Roo.htmleditor.Filter,
45337 {
45338     
45339      
45340     tag : 'P',
45341     
45342      
45343     replaceTag : function(node)
45344     {
45345         
45346         if (node.childNodes.length == 1 &&
45347             node.childNodes[0].nodeType == 3 &&
45348             node.childNodes[0].textContent.trim().length < 1
45349             ) {
45350             // remove and replace with '<BR>';
45351             node.parentNode.replaceChild(node.ownerDocument.createElement('BR'),node);
45352             return false; // no need to walk..
45353         }
45354         var ar = Array.from(node.childNodes);
45355         for (var i = 0; i < ar.length; i++) {
45356             node.removeChild(ar[i]);
45357             // what if we need to walk these???
45358             node.parentNode.insertBefore(ar[i], node);
45359         }
45360         // now what about this?
45361         // <p> &nbsp; </p>
45362         
45363         // double BR.
45364         node.parentNode.insertBefore(node.ownerDocument.createElement('BR'), node);
45365         node.parentNode.insertBefore(node.ownerDocument.createElement('BR'), node);
45366         node.parentNode.removeChild(node);
45367         
45368         return false;
45369
45370     }
45371     
45372 });/**
45373  * @class Roo.htmleditor.FilterSpan
45374  * filter span's with no attributes out..
45375  * @constructor
45376  * Run a new Span Filter
45377  * @param {Object} config Configuration options
45378  */
45379
45380 Roo.htmleditor.FilterSpan = function(cfg)
45381 {
45382     // no need to apply config.
45383     this.walk(cfg.node);
45384 }
45385
45386 Roo.extend(Roo.htmleditor.FilterSpan, Roo.htmleditor.FilterKeepChildren,
45387 {
45388      
45389     tag : 'SPAN',
45390      
45391  
45392     replaceTag : function(node)
45393     {
45394         if (node.attributes && node.attributes.length > 0) {
45395             return true; // walk if there are any.
45396         }
45397         Roo.htmleditor.FilterKeepChildren.prototype.replaceTag.call(this, node);
45398         return false;
45399      
45400     }
45401     
45402 });/**
45403  * @class Roo.htmleditor.FilterTableWidth
45404   try and remove table width data - as that frequently messes up other stuff.
45405  * 
45406  *      was cleanTableWidths.
45407  *
45408  * Quite often pasting from word etc.. results in tables with column and widths.
45409  * This does not work well on fluid HTML layouts - like emails. - so this code should hunt an destroy them..
45410  *
45411  * @constructor
45412  * Run a new Table Filter
45413  * @param {Object} config Configuration options
45414  */
45415
45416 Roo.htmleditor.FilterTableWidth = function(cfg)
45417 {
45418     // no need to apply config.
45419     this.tag = ['TABLE', 'TD', 'TR', 'TH', 'THEAD', 'TBODY' ];
45420     this.walk(cfg.node);
45421 }
45422
45423 Roo.extend(Roo.htmleditor.FilterTableWidth, Roo.htmleditor.Filter,
45424 {
45425      
45426      
45427     
45428     replaceTag: function(node) {
45429         
45430         
45431       
45432         if (node.hasAttribute('width')) {
45433             node.removeAttribute('width');
45434         }
45435         
45436          
45437         if (node.hasAttribute("style")) {
45438             // pretty basic...
45439             
45440             var styles = node.getAttribute("style").split(";");
45441             var nstyle = [];
45442             Roo.each(styles, function(s) {
45443                 if (!s.match(/:/)) {
45444                     return;
45445                 }
45446                 var kv = s.split(":");
45447                 if (kv[0].match(/^\s*(width|min-width)\s*$/)) {
45448                     return;
45449                 }
45450                 // what ever is left... we allow.
45451                 nstyle.push(s);
45452             });
45453             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
45454             if (!nstyle.length) {
45455                 node.removeAttribute('style');
45456             }
45457         }
45458         
45459         return true; // continue doing children..
45460     }
45461 });/**
45462  * @class Roo.htmleditor.FilterWord
45463  * try and clean up all the mess that Word generates.
45464  * 
45465  * This is the 'nice version' - see 'Heavy' that white lists a very short list of elements, and multi-filters 
45466  
45467  * @constructor
45468  * Run a new Span Filter
45469  * @param {Object} config Configuration options
45470  */
45471
45472 Roo.htmleditor.FilterWord = function(cfg)
45473 {
45474     // no need to apply config.
45475     this.walk(cfg.node);
45476 }
45477
45478 Roo.extend(Roo.htmleditor.FilterWord, Roo.htmleditor.Filter,
45479 {
45480     tag: true,
45481      
45482     
45483     /**
45484      * Clean up MS wordisms...
45485      */
45486     replaceTag : function(node)
45487     {
45488          
45489         // no idea what this does - span with text, replaceds with just text.
45490         if(
45491                 node.nodeName == 'SPAN' &&
45492                 !node.hasAttributes() &&
45493                 node.childNodes.length == 1 &&
45494                 node.firstChild.nodeName == "#text"  
45495         ) {
45496             var textNode = node.firstChild;
45497             node.removeChild(textNode);
45498             if (node.getAttribute('lang') != 'zh-CN') {   // do not space pad on chinese characters..
45499                 node.parentNode.insertBefore(node.ownerDocument.createTextNode(" "), node);
45500             }
45501             node.parentNode.insertBefore(textNode, node);
45502             if (node.getAttribute('lang') != 'zh-CN') {   // do not space pad on chinese characters..
45503                 node.parentNode.insertBefore(node.ownerDocument.createTextNode(" ") , node);
45504             }
45505             
45506             node.parentNode.removeChild(node);
45507             return false; // dont do chidren - we have remove our node - so no need to do chdhilren?
45508         }
45509         
45510    
45511         
45512         if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
45513             node.parentNode.removeChild(node);
45514             return false; // dont do chidlren
45515         }
45516         //Roo.log(node.tagName);
45517         // remove - but keep children..
45518         if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|v:|font)/)) {
45519             //Roo.log('-- removed');
45520             while (node.childNodes.length) {
45521                 var cn = node.childNodes[0];
45522                 node.removeChild(cn);
45523                 node.parentNode.insertBefore(cn, node);
45524                 // move node to parent - and clean it..
45525                 this.replaceTag(cn);
45526             }
45527             node.parentNode.removeChild(node);
45528             /// no need to iterate chidlren = it's got none..
45529             //this.iterateChildren(node, this.cleanWord);
45530             return false; // no need to iterate children.
45531         }
45532         // clean styles
45533         if (node.className.length) {
45534             
45535             var cn = node.className.split(/\W+/);
45536             var cna = [];
45537             Roo.each(cn, function(cls) {
45538                 if (cls.match(/Mso[a-zA-Z]+/)) {
45539                     return;
45540                 }
45541                 cna.push(cls);
45542             });
45543             node.className = cna.length ? cna.join(' ') : '';
45544             if (!cna.length) {
45545                 node.removeAttribute("class");
45546             }
45547         }
45548         
45549         if (node.hasAttribute("lang")) {
45550             node.removeAttribute("lang");
45551         }
45552         
45553         if (node.hasAttribute("style")) {
45554             
45555             var styles = node.getAttribute("style").split(";");
45556             var nstyle = [];
45557             Roo.each(styles, function(s) {
45558                 if (!s.match(/:/)) {
45559                     return;
45560                 }
45561                 var kv = s.split(":");
45562                 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
45563                     return;
45564                 }
45565                 // what ever is left... we allow.
45566                 nstyle.push(s);
45567             });
45568             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
45569             if (!nstyle.length) {
45570                 node.removeAttribute('style');
45571             }
45572         }
45573         return true; // do children
45574         
45575         
45576         
45577     }
45578 });
45579 /**
45580  * @class Roo.htmleditor.FilterStyleToTag
45581  * part of the word stuff... - certain 'styles' should be converted to tags.
45582  * eg.
45583  *   font-weight: bold -> bold
45584  *   ?? super / subscrit etc..
45585  * 
45586  * @constructor
45587 * Run a new style to tag filter.
45588 * @param {Object} config Configuration options
45589  */
45590 Roo.htmleditor.FilterStyleToTag = function(cfg)
45591 {
45592     
45593     this.tags = {
45594         B  : [ 'fontWeight' , 'bold'],
45595         I :  [ 'fontStyle' , 'italic'],
45596         //pre :  [ 'font-style' , 'italic'],
45597         // h1.. h6 ?? font-size?
45598         SUP : [ 'verticalAlign' , 'super' ],
45599         SUB : [ 'verticalAlign' , 'sub' ]
45600         
45601         
45602     };
45603     
45604     Roo.apply(this, cfg);
45605      
45606     
45607     this.walk(cfg.node);
45608     
45609     
45610     
45611 }
45612
45613
45614 Roo.extend(Roo.htmleditor.FilterStyleToTag, Roo.htmleditor.Filter,
45615 {
45616     tag: true, // all tags
45617     
45618     tags : false,
45619     
45620     
45621     replaceTag : function(node)
45622     {
45623         
45624         
45625         if (node.getAttribute("style") === null) {
45626             return true;
45627         }
45628         var inject = [];
45629         for (var k in this.tags) {
45630             if (node.style[this.tags[k][0]] == this.tags[k][1]) {
45631                 inject.push(k);
45632                 node.style.removeProperty(this.tags[k][0]);
45633             }
45634         }
45635         if (!inject.length) {
45636             return true; 
45637         }
45638         var cn = Array.from(node.childNodes);
45639         var nn = node;
45640         Roo.each(inject, function(t) {
45641             var nc = node.ownerDocument.createelement(t);
45642             nn.appendChild(nc);
45643             nn = nc;
45644         });
45645         for(var i = 0;i < cn.length;cn++) {
45646             node.removeChild(cn[i]);
45647             nn.appendChild(cn[i]);
45648         }
45649         return true /// iterate thru
45650     }
45651     
45652 })/**
45653  * @class Roo.htmleditor.FilterLongBr
45654  * BR/BR/BR - keep a maximum of 2...
45655  * @constructor
45656  * Run a new Long BR Filter
45657  * @param {Object} config Configuration options
45658  */
45659
45660 Roo.htmleditor.FilterLongBr = function(cfg)
45661 {
45662     // no need to apply config.
45663     this.walk(cfg.node);
45664 }
45665
45666 Roo.extend(Roo.htmleditor.FilterLongBr, Roo.htmleditor.Filter,
45667 {
45668     
45669      
45670     tag : 'BR',
45671     
45672      
45673     replaceTag : function(node)
45674     {
45675         
45676         var ps = node.nextSibling;
45677         while (ps && ps.nodeType == 3 && ps.nodeValue.trim().length < 1) {
45678             ps = ps.nextSibling;
45679         }
45680         
45681         if (!ps &&  [ 'TD', 'TH', 'LI', 'H1', 'H2', 'H3', 'H4', 'H5', 'H6' ].indexOf(node.parentNode.tagName) > -1) { 
45682             node.parentNode.removeChild(node); // remove last BR inside one fo these tags
45683             return false;
45684         }
45685         
45686         if (!ps || ps.nodeType != 1) {
45687             return false;
45688         }
45689         
45690         if (!ps || ps.tagName != 'BR') {
45691            
45692             return false;
45693         }
45694         
45695         
45696         
45697         
45698         
45699         if (!node.previousSibling) {
45700             return false;
45701         }
45702         var ps = node.previousSibling;
45703         
45704         while (ps && ps.nodeType == 3 && ps.nodeValue.trim().length < 1) {
45705             ps = ps.previousSibling;
45706         }
45707         if (!ps || ps.nodeType != 1) {
45708             return false;
45709         }
45710         // if header or BR before.. then it's a candidate for removal.. - as we only want '2' of these..
45711         if (!ps || [ 'BR', 'H1', 'H2', 'H3', 'H4', 'H5', 'H6' ].indexOf(ps.tagName) < 0) {
45712             return false;
45713         }
45714         
45715         node.parentNode.removeChild(node); // remove me...
45716         
45717         return false; // no need to do children
45718
45719     }
45720     
45721 });
45722 /**
45723  * @class Roo.htmleditor.Tidy
45724  * Tidy HTML 
45725  * @cfg {Roo.HtmlEditorCore} core the editor.
45726  * @constructor
45727  * Create a new Filter.
45728  * @param {Object} config Configuration options
45729  */
45730
45731
45732 Roo.htmleditor.Tidy = function(cfg) {
45733     Roo.apply(this, cfg);
45734     
45735     this.core.doc.body.innerHTML = this.tidy(this.core.doc.body, '');
45736      
45737 }
45738
45739 Roo.htmleditor.Tidy.toString = function(node)
45740 {
45741     return Roo.htmleditor.Tidy.prototype.tidy(node, '');
45742 }
45743
45744 Roo.htmleditor.Tidy.prototype = {
45745     
45746     
45747     wrap : function(s) {
45748         return s.replace(/\n/g, " ").replace(/(?![^\n]{1,80}$)([^\n]{1,80})\s/g, '$1\n');
45749     },
45750
45751     
45752     tidy : function(node, indent) {
45753      
45754         if  (node.nodeType == 3) {
45755             // text.
45756             
45757             
45758             return indent === false ? node.nodeValue : this.wrap(node.nodeValue.trim()).split("\n").join("\n" + indent);
45759                 
45760             
45761         }
45762         
45763         if  (node.nodeType != 1) {
45764             return '';
45765         }
45766         
45767         
45768         
45769         if (node.tagName == 'BODY') {
45770             
45771             return this.cn(node, '');
45772         }
45773              
45774              // Prints the node tagName, such as <A>, <IMG>, etc
45775         var ret = "<" + node.tagName +  this.attr(node) ;
45776         
45777         // elements with no children..
45778         if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(node.tagName) > -1) {
45779                 return ret + '/>';
45780         }
45781         ret += '>';
45782         
45783         
45784         var cindent = indent === false ? '' : (indent + '  ');
45785         // tags where we will not pad the children.. (inline text tags etc..)
45786         if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN', 'B', 'I', 'S'].indexOf(node.tagName) > -1) { // or code?
45787             cindent = false;
45788             
45789             
45790         }
45791         
45792         var cn = this.cn(node, cindent );
45793         
45794         return ret + cn  + '</' + node.tagName + '>';
45795         
45796     },
45797     cn: function(node, indent)
45798     {
45799         var ret = [];
45800         
45801         var ar = Array.from(node.childNodes);
45802         for (var i = 0 ; i < ar.length ; i++) {
45803             
45804             
45805             
45806             if (indent !== false   // indent==false preservies everything
45807                 && i > 0
45808                 && ar[i].nodeType == 3 
45809                 && ar[i].nodeValue.length > 0
45810                 && ar[i].nodeValue.match(/^\s+/)
45811             ) {
45812                 if (ret.length && ret[ret.length-1] == "\n" + indent) {
45813                     ret.pop(); // remove line break from last?
45814                 }
45815                 
45816                 ret.push(" "); // add a space if i'm a text item with a space at the front, as tidy will strip spaces.
45817             }
45818             if (indent !== false
45819                 && ar[i].nodeType == 1 // element - and indent is not set... 
45820             ) {
45821                 ret.push("\n" + indent); 
45822             }
45823             
45824             ret.push(this.tidy(ar[i], indent));
45825             // text + trailing indent 
45826             if (indent !== false
45827                 && ar[i].nodeType == 3
45828                 && ar[i].nodeValue.length > 0
45829                 && ar[i].nodeValue.match(/\s+$/)
45830             ){
45831                 ret.push("\n" + indent); 
45832             }
45833             
45834             
45835             
45836             
45837         }
45838         // what if all text?
45839         
45840         
45841         return ret.join('');
45842     },
45843     
45844          
45845         
45846     attr : function(node)
45847     {
45848         var attr = [];
45849         for(i = 0; i < node.attributes.length;i++) {
45850             
45851             // skip empty values?
45852             if (!node.attributes.item(i).value.length) {
45853                 continue;
45854             }
45855             attr.push(  node.attributes.item(i).name + '="' +
45856                     Roo.util.Format.htmlEncode(node.attributes.item(i).value) + '"'
45857             );
45858         }
45859         return attr.length ? (' ' + attr.join(' ') ) : '';
45860         
45861     }
45862     
45863     
45864     
45865 }
45866 /**
45867  * @class Roo.htmleditor.KeyEnter
45868  * Handle Enter press..
45869  * @cfg {Roo.HtmlEditorCore} core the editor.
45870  * @constructor
45871  * Create a new Filter.
45872  * @param {Object} config Configuration options
45873  */
45874
45875
45876
45877 Roo.htmleditor.KeyEnter = function(cfg) {
45878     Roo.apply(this, cfg);
45879     // this does not actually call walk as it's really just a abstract class
45880  
45881     Roo.get(this.core.doc.body).on('keypress', this.keypress, this);
45882 }
45883
45884
45885 Roo.htmleditor.KeyEnter.prototype = {
45886     
45887     core : false,
45888     
45889     keypress : function(e) {
45890         if (e.charCode != 13) {
45891             return true;
45892         }
45893         e.preventDefault();
45894         // https://stackoverflow.com/questions/18552336/prevent-contenteditable-adding-div-on-enter-chrome
45895         var doc = this.core.doc;
45896         
45897         var docFragment = doc.createDocumentFragment();
45898     
45899         //add a new line
45900         var newEle = doc.createTextNode('\n');
45901         docFragment.appendChild(newEle);
45902     
45903     
45904         var range = this.core.win.getSelection().getRangeAt(0);
45905         var n = range.commonAncestorContainer ;
45906         while (n && n.nodeType != 1) {
45907             n  = n.parentNode;
45908         }
45909         var li = false;
45910         if (n && n.tagName == 'UL') {
45911             li = doc.createElement('LI');
45912             n.appendChild(li);
45913             
45914         }
45915         if (n && n.tagName == 'LI') {
45916             li = doc.createElement('LI');
45917             if (n.nextSibling) {
45918                 n.parentNode.insertBefore(li, n.firstSibling);
45919                 
45920             } else {
45921                 n.parentNode.appendChild(li);
45922             }
45923         }
45924         if (li) {   
45925             range = doc.createRange();
45926             range.setStartAfter(li);
45927             range.collapse(true);
45928         
45929             //make the cursor there
45930             var sel = this.core.win.getSelection();
45931             sel.removeAllRanges();
45932             sel.addRange(range);
45933             return false;
45934             
45935             
45936         }
45937         //add the br, or p, or something else
45938         newEle = doc.createElement('br');
45939         docFragment.appendChild(newEle);
45940     
45941         //make the br replace selection
45942         
45943         range.deleteContents();
45944         
45945         range.insertNode(docFragment);
45946     
45947         //create a new range
45948         range = doc.createRange();
45949         range.setStartAfter(newEle);
45950         range.collapse(true);
45951     
45952         //make the cursor there
45953         var sel = this.core.win.getSelection();
45954         sel.removeAllRanges();
45955         sel.addRange(range);
45956     
45957         return false;
45958          
45959     }
45960 };
45961      
45962 /**
45963  * @class Roo.htmleditor.Block
45964  * Base class for html editor blocks - do not use it directly .. extend it..
45965  * @cfg {DomElement} node The node to apply stuff to.
45966  * @cfg {String} friendly_name the name that appears in the context bar about this block
45967  * @cfg {Object} Context menu - see Roo.form.HtmlEditor.ToolbarContext
45968  
45969  * @constructor
45970  * Create a new Filter.
45971  * @param {Object} config Configuration options
45972  */
45973
45974 Roo.htmleditor.Block  = function(cfg)
45975 {
45976     // do nothing .. should not be called really.
45977 }
45978
45979 Roo.htmleditor.Block.factory = function(node)
45980 {
45981     
45982     var id = Roo.get(node).id;
45983     if (typeof(Roo.htmleditor.Block.cache[id]) != 'undefined') {
45984         return Roo.htmleditor.Block.cache[id];
45985     }
45986     
45987     var cls = Roo.htmleditor['Block' + Roo.get(node).attr('data-block')];
45988     if (typeof(cls) == 'undefined') {
45989         Roo.log("OOps missing block : " + 'Block' + Roo.get(node).attr('data-block'));
45990         return false;
45991     }
45992     Roo.htmleditor.Block.cache[id] = new cls({ node: node });
45993     return Roo.htmleditor.Block.cache[id];  /// should trigger update element
45994 };
45995 // question goes here... do we need to clear out this cache sometimes?
45996 // or show we make it relivant to the htmleditor.
45997 Roo.htmleditor.Block.cache = {};
45998
45999 Roo.htmleditor.Block.prototype = {
46000     
46001      // used by context menu
46002     friendly_name : 'Image with caption',
46003     
46004     context : false,
46005     /**
46006      * Update a node with values from this object
46007      * @param {DomElement} node
46008      */
46009     updateElement : function(node)
46010     {
46011         Roo.DomHelper.update(node, this.toObject());
46012     },
46013      /**
46014      * convert to plain HTML for calling insertAtCursor..
46015      */
46016     toHTML : function()
46017     {
46018         return Roo.DomHelper.markup(this.toObject());
46019     },
46020     /**
46021      * used by readEleemnt to extract data from a node
46022      * may need improving as it's pretty basic
46023      
46024      * @param {DomElement} node
46025      * @param {String} tag - tag to find, eg. IMG ?? might be better to use DomQuery ?
46026      * @param {String} attribute (use html - for contents, or style for using next param as style)
46027      * @param {String} style the style property - eg. text-align
46028      */
46029     getVal : function(node, tag, attr, style)
46030     {
46031         var n = node;
46032         if (tag !== true && n.tagName != tag.toUpperCase()) {
46033             // in theory we could do figure[3] << 3rd figure? or some more complex search..?
46034             // but kiss for now.
46035             n = node.getElementsByTagName(tag).item(0);
46036         }
46037         if (attr == 'html') {
46038             return n.innerHTML;
46039         }
46040         if (attr == 'style') {
46041             return Roo.get(n).getStyle(style);
46042         }
46043         
46044         return Roo.get(n).attr(attr);
46045             
46046     },
46047     /**
46048      * create a DomHelper friendly object - for use with 
46049      * Roo.DomHelper.markup / overwrite / etc..
46050      * (override this)
46051      */
46052     toObject : function()
46053     {
46054         return {};
46055     },
46056       /**
46057      * Read a node that has a 'data-block' property - and extract the values from it.
46058      * @param {DomElement} node - the node
46059      */
46060     readElement : function(node)
46061     {
46062         
46063     } 
46064     
46065     
46066 };
46067
46068  
46069
46070 /**
46071  * @class Roo.htmleditor.BlockFigure
46072  * Block that has an image and a figcaption
46073  * @cfg {String} image_src the url for the image
46074  * @cfg {String} align (left|right) alignment for the block default left
46075  * @cfg {String} text_align (left|right) alignment for the text caption default left.
46076  * @cfg {String} caption the text to appear below  (and in the alt tag)
46077  * @cfg {String|number} image_width the width of the image number or %?
46078  * @cfg {String|number} image_height the height of the image number or %?
46079  * 
46080  * @constructor
46081  * Create a new Filter.
46082  * @param {Object} config Configuration options
46083  */
46084
46085 Roo.htmleditor.BlockFigure = function(cfg)
46086 {
46087     if (cfg.node) {
46088         this.readElement(cfg.node);
46089         this.updateElement(cfg.node);
46090     }
46091     Roo.apply(this, cfg);
46092 }
46093 Roo.extend(Roo.htmleditor.BlockFigure, Roo.htmleditor.Block, {
46094  
46095     
46096     // setable values.
46097     image_src: '',
46098     
46099     align: 'left',
46100     caption : '',
46101     text_align: 'left',
46102     
46103     width : '46%',
46104     margin: '2%',
46105     
46106     // used by context menu
46107     friendly_name : 'Image with caption',
46108     
46109     context : { // ?? static really
46110         width : {
46111             title: "Width",
46112             width: 40
46113             // ?? number
46114         },
46115         margin : {
46116             title: "Margin",
46117             width: 40
46118             // ?? number
46119         },
46120         align: {
46121             title: "Align",
46122             opts : [[ "left"],[ "right"]],
46123             width : 80
46124             
46125         },
46126         text_align: {
46127             title: "Caption Align",
46128             opts : [ [ "left"],[ "right"],[ "center"]],
46129             width : 80
46130         },
46131         
46132        
46133         image_src : {
46134             title: "Src",
46135             width: 220
46136         }
46137     },
46138     /**
46139      * create a DomHelper friendly object - for use with
46140      * Roo.DomHelper.markup / overwrite / etc..
46141      */
46142     toObject : function()
46143     {
46144         var d = document.createElement('div');
46145         d.innerHTML = this.caption;
46146         
46147         return {
46148             tag: 'figure',
46149             'data-block' : 'Figure',
46150             contenteditable : 'false',
46151             style : {
46152                 display: 'table',
46153                 float :  this.align ,
46154                 width :  this.width,
46155                 margin:  this.margin
46156             },
46157             cn : [
46158                 {
46159                     tag : 'img',
46160                     src : this.image_src,
46161                     alt : d.innerText.replace(/\n/g, " "), // removeHTML..
46162                     style: {
46163                         width: '100%'
46164                     }
46165                 },
46166                 {
46167                     tag: 'figcaption',
46168                     contenteditable : true,
46169                     style : {
46170                         'text-align': this.text_align
46171                     },
46172                     html : this.caption
46173                     
46174                 }
46175             ]
46176         };
46177     },
46178     
46179     readElement : function(node)
46180     {
46181         this.image_src = this.getVal(node, 'img', 'src');
46182         this.align = this.getVal(node, 'figure', 'style', 'float');
46183         this.caption = this.getVal(node, 'figcaption', 'html');
46184         this.text_align = this.getVal(node, 'figcaption', 'style','text-align');
46185         this.width = this.getVal(node, 'figure', 'style', 'width');
46186         this.margin = this.getVal(node, 'figure', 'style', 'margin');
46187         
46188     } 
46189     
46190   
46191    
46192      
46193     
46194     
46195     
46196     
46197 })
46198
46199 //<script type="text/javascript">
46200
46201 /*
46202  * Based  Ext JS Library 1.1.1
46203  * Copyright(c) 2006-2007, Ext JS, LLC.
46204  * LGPL
46205  *
46206  */
46207  
46208 /**
46209  * @class Roo.HtmlEditorCore
46210  * @extends Roo.Component
46211  * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
46212  *
46213  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
46214  */
46215
46216 Roo.HtmlEditorCore = function(config){
46217     
46218     
46219     Roo.HtmlEditorCore.superclass.constructor.call(this, config);
46220     
46221     
46222     this.addEvents({
46223         /**
46224          * @event initialize
46225          * Fires when the editor is fully initialized (including the iframe)
46226          * @param {Roo.HtmlEditorCore} this
46227          */
46228         initialize: true,
46229         /**
46230          * @event activate
46231          * Fires when the editor is first receives the focus. Any insertion must wait
46232          * until after this event.
46233          * @param {Roo.HtmlEditorCore} this
46234          */
46235         activate: true,
46236          /**
46237          * @event beforesync
46238          * Fires before the textarea is updated with content from the editor iframe. Return false
46239          * to cancel the sync.
46240          * @param {Roo.HtmlEditorCore} this
46241          * @param {String} html
46242          */
46243         beforesync: true,
46244          /**
46245          * @event beforepush
46246          * Fires before the iframe editor is updated with content from the textarea. Return false
46247          * to cancel the push.
46248          * @param {Roo.HtmlEditorCore} this
46249          * @param {String} html
46250          */
46251         beforepush: true,
46252          /**
46253          * @event sync
46254          * Fires when the textarea is updated with content from the editor iframe.
46255          * @param {Roo.HtmlEditorCore} this
46256          * @param {String} html
46257          */
46258         sync: true,
46259          /**
46260          * @event push
46261          * Fires when the iframe editor is updated with content from the textarea.
46262          * @param {Roo.HtmlEditorCore} this
46263          * @param {String} html
46264          */
46265         push: true,
46266         
46267         /**
46268          * @event editorevent
46269          * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
46270          * @param {Roo.HtmlEditorCore} this
46271          */
46272         editorevent: true
46273         
46274     });
46275     
46276     // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
46277     
46278     // defaults : white / black...
46279     this.applyBlacklists();
46280     
46281     
46282     
46283 };
46284
46285
46286 Roo.extend(Roo.HtmlEditorCore, Roo.Component,  {
46287
46288
46289      /**
46290      * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field 
46291      */
46292     
46293     owner : false,
46294     
46295      /**
46296      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
46297      *                        Roo.resizable.
46298      */
46299     resizable : false,
46300      /**
46301      * @cfg {Number} height (in pixels)
46302      */   
46303     height: 300,
46304    /**
46305      * @cfg {Number} width (in pixels)
46306      */   
46307     width: 500,
46308     
46309     /**
46310      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
46311      * 
46312      */
46313     stylesheets: false,
46314     
46315     /**
46316      * @cfg {boolean} allowComments - default false - allow comments in HTML source - by default they are stripped - if you are editing email you may need this.
46317      */
46318     allowComments: false,
46319     // id of frame..
46320     frameId: false,
46321     
46322     // private properties
46323     validationEvent : false,
46324     deferHeight: true,
46325     initialized : false,
46326     activated : false,
46327     sourceEditMode : false,
46328     onFocus : Roo.emptyFn,
46329     iframePad:3,
46330     hideMode:'offsets',
46331     
46332     clearUp: true,
46333     
46334     // blacklist + whitelisted elements..
46335     black: false,
46336     white: false,
46337      
46338     bodyCls : '',
46339
46340     /**
46341      * Protected method that will not generally be called directly. It
46342      * is called when the editor initializes the iframe with HTML contents. Override this method if you
46343      * want to change the initialization markup of the iframe (e.g. to add stylesheets).
46344      */
46345     getDocMarkup : function(){
46346         // body styles..
46347         var st = '';
46348         
46349         // inherit styels from page...?? 
46350         if (this.stylesheets === false) {
46351             
46352             Roo.get(document.head).select('style').each(function(node) {
46353                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
46354             });
46355             
46356             Roo.get(document.head).select('link').each(function(node) { 
46357                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
46358             });
46359             
46360         } else if (!this.stylesheets.length) {
46361                 // simple..
46362                 st = '<style type="text/css">' +
46363                     'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
46364                    '</style>';
46365         } else {
46366             for (var i in this.stylesheets) {
46367                 if (typeof(this.stylesheets[i]) != 'string') {
46368                     continue;
46369                 }
46370                 st += '<link rel="stylesheet" href="' + this.stylesheets[i] +'" type="text/css">';
46371             }
46372             
46373         }
46374         
46375         st +=  '<style type="text/css">' +
46376             'IMG { cursor: pointer } ' +
46377         '</style>';
46378
46379         var cls = 'roo-htmleditor-body';
46380         
46381         if(this.bodyCls.length){
46382             cls += ' ' + this.bodyCls;
46383         }
46384         
46385         return '<html><head>' + st  +
46386             //<style type="text/css">' +
46387             //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
46388             //'</style>' +
46389             ' </head><body contenteditable="true" data-enable-grammerly="true" class="' +  cls + '"></body></html>';
46390     },
46391
46392     // private
46393     onRender : function(ct, position)
46394     {
46395         var _t = this;
46396         //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
46397         this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
46398         
46399         
46400         this.el.dom.style.border = '0 none';
46401         this.el.dom.setAttribute('tabIndex', -1);
46402         this.el.addClass('x-hidden hide');
46403         
46404         
46405         
46406         if(Roo.isIE){ // fix IE 1px bogus margin
46407             this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
46408         }
46409        
46410         
46411         this.frameId = Roo.id();
46412         
46413          
46414         
46415         var iframe = this.owner.wrap.createChild({
46416             tag: 'iframe',
46417             cls: 'form-control', // bootstrap..
46418             id: this.frameId,
46419             name: this.frameId,
46420             frameBorder : 'no',
46421             'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL  :  "javascript:false"
46422         }, this.el
46423         );
46424         
46425         
46426         this.iframe = iframe.dom;
46427
46428         this.assignDocWin();
46429         
46430         this.doc.designMode = 'on';
46431        
46432         this.doc.open();
46433         this.doc.write(this.getDocMarkup());
46434         this.doc.close();
46435
46436         
46437         var task = { // must defer to wait for browser to be ready
46438             run : function(){
46439                 //console.log("run task?" + this.doc.readyState);
46440                 this.assignDocWin();
46441                 if(this.doc.body || this.doc.readyState == 'complete'){
46442                     try {
46443                         this.doc.designMode="on";
46444                     } catch (e) {
46445                         return;
46446                     }
46447                     Roo.TaskMgr.stop(task);
46448                     this.initEditor.defer(10, this);
46449                 }
46450             },
46451             interval : 10,
46452             duration: 10000,
46453             scope: this
46454         };
46455         Roo.TaskMgr.start(task);
46456
46457     },
46458
46459     // private
46460     onResize : function(w, h)
46461     {
46462          Roo.log('resize: ' +w + ',' + h );
46463         //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
46464         if(!this.iframe){
46465             return;
46466         }
46467         if(typeof w == 'number'){
46468             
46469             this.iframe.style.width = w + 'px';
46470         }
46471         if(typeof h == 'number'){
46472             
46473             this.iframe.style.height = h + 'px';
46474             if(this.doc){
46475                 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
46476             }
46477         }
46478         
46479     },
46480
46481     /**
46482      * Toggles the editor between standard and source edit mode.
46483      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
46484      */
46485     toggleSourceEdit : function(sourceEditMode){
46486         
46487         this.sourceEditMode = sourceEditMode === true;
46488         
46489         if(this.sourceEditMode){
46490  
46491             Roo.get(this.iframe).addClass(['x-hidden','hide', 'd-none']);     //FIXME - what's the BS styles for these
46492             
46493         }else{
46494             Roo.get(this.iframe).removeClass(['x-hidden','hide', 'd-none']);
46495             //this.iframe.className = '';
46496             this.deferFocus();
46497         }
46498         //this.setSize(this.owner.wrap.getSize());
46499         //this.fireEvent('editmodechange', this, this.sourceEditMode);
46500     },
46501
46502     
46503   
46504
46505     /**
46506      * Protected method that will not generally be called directly. If you need/want
46507      * custom HTML cleanup, this is the method you should override.
46508      * @param {String} html The HTML to be cleaned
46509      * return {String} The cleaned HTML
46510      */
46511     cleanHtml : function(html){
46512         html = String(html);
46513         if(html.length > 5){
46514             if(Roo.isSafari){ // strip safari nonsense
46515                 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
46516             }
46517         }
46518         if(html == '&nbsp;'){
46519             html = '';
46520         }
46521         return html;
46522     },
46523
46524     /**
46525      * HTML Editor -> Textarea
46526      * Protected method that will not generally be called directly. Syncs the contents
46527      * of the editor iframe with the textarea.
46528      */
46529     syncValue : function()
46530     {
46531         Roo.log("HtmlEditorCore:syncValue (EDITOR->TEXT)");
46532         if(this.initialized){
46533             var bd = (this.doc.body || this.doc.documentElement);
46534             //this.cleanUpPaste(); -- this is done else where and causes havoc..
46535             
46536             // not sure if this is really the place for this
46537             // the blocks are synced occasionaly - since we currently dont add listeners on the blocks
46538             // this has to update attributes that get duped.. like alt and caption..
46539             
46540             Roo.each(Roo.get(this.doc.body).query('*[data-block]'), function(e) {
46541                  Roo.htmleditor.Block.factory(e);
46542             },this);
46543             
46544             
46545             var div = document.createElement('div');
46546             div.innerHTML = bd.innerHTML;
46547             // remove content editable. (blocks)
46548             
46549            
46550             
46551             new Roo.htmleditor.FilterAttributes({node : div, attrib_black: [ 'contenteditable' ] });
46552             //?? tidy?
46553             var html = div.innerHTML;
46554             if(Roo.isSafari){
46555                 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
46556                 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
46557                 if(m && m[1]){
46558                     html = '<div style="'+m[0]+'">' + html + '</div>';
46559                 }
46560             }
46561             html = this.cleanHtml(html);
46562             // fix up the special chars.. normaly like back quotes in word...
46563             // however we do not want to do this with chinese..
46564             html = html.replace(/[\uD800-\uDBFF][\uDC00-\uDFFF]|[\u0080-\uFFFF]/g, function(match) {
46565                 
46566                 var cc = match.charCodeAt();
46567
46568                 // Get the character value, handling surrogate pairs
46569                 if (match.length == 2) {
46570                     // It's a surrogate pair, calculate the Unicode code point
46571                     var high = match.charCodeAt(0) - 0xD800;
46572                     var low  = match.charCodeAt(1) - 0xDC00;
46573                     cc = (high * 0x400) + low + 0x10000;
46574                 }  else if (
46575                     (cc >= 0x4E00 && cc < 0xA000 ) ||
46576                     (cc >= 0x3400 && cc < 0x4E00 ) ||
46577                     (cc >= 0xf900 && cc < 0xfb00 )
46578                 ) {
46579                         return match;
46580                 }  
46581          
46582                 // No, use a numeric entity. Here we brazenly (and possibly mistakenly)
46583                 return "&#" + cc + ";";
46584                 
46585                 
46586             });
46587             
46588             
46589              
46590             if(this.owner.fireEvent('beforesync', this, html) !== false){
46591                 this.el.dom.value = html;
46592                 this.owner.fireEvent('sync', this, html);
46593             }
46594         }
46595     },
46596
46597     /**
46598      * TEXTAREA -> EDITABLE
46599      * Protected method that will not generally be called directly. Pushes the value of the textarea
46600      * into the iframe editor.
46601      */
46602     pushValue : function()
46603     {
46604         Roo.log("HtmlEditorCore:pushValue (TEXT->EDITOR)");
46605         if(this.initialized){
46606             var v = this.el.dom.value.trim();
46607             
46608             
46609             if(this.owner.fireEvent('beforepush', this, v) !== false){
46610                 var d = (this.doc.body || this.doc.documentElement);
46611                 d.innerHTML = v;
46612                  
46613                 this.el.dom.value = d.innerHTML;
46614                 this.owner.fireEvent('push', this, v);
46615             }
46616             
46617             Roo.each(Roo.get(this.doc.body).query('*[data-block]'), function(e) {
46618                 
46619                 Roo.htmleditor.Block.factory(e);
46620                 
46621             },this);
46622             var lc = this.doc.body.lastChild;
46623             if (lc && lc.nodeType == 1 && lc.getAttribute("contenteditable") == "false") {
46624                 // add an extra line at the end.
46625                 this.doc.body.appendChild(this.doc.createElement('br'));
46626             }
46627             
46628             
46629         }
46630     },
46631
46632     // private
46633     deferFocus : function(){
46634         this.focus.defer(10, this);
46635     },
46636
46637     // doc'ed in Field
46638     focus : function(){
46639         if(this.win && !this.sourceEditMode){
46640             this.win.focus();
46641         }else{
46642             this.el.focus();
46643         }
46644     },
46645     
46646     assignDocWin: function()
46647     {
46648         var iframe = this.iframe;
46649         
46650          if(Roo.isIE){
46651             this.doc = iframe.contentWindow.document;
46652             this.win = iframe.contentWindow;
46653         } else {
46654 //            if (!Roo.get(this.frameId)) {
46655 //                return;
46656 //            }
46657 //            this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
46658 //            this.win = Roo.get(this.frameId).dom.contentWindow;
46659             
46660             if (!Roo.get(this.frameId) && !iframe.contentDocument) {
46661                 return;
46662             }
46663             
46664             this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
46665             this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
46666         }
46667     },
46668     
46669     // private
46670     initEditor : function(){
46671         //console.log("INIT EDITOR");
46672         this.assignDocWin();
46673         
46674         
46675         
46676         this.doc.designMode="on";
46677         this.doc.open();
46678         this.doc.write(this.getDocMarkup());
46679         this.doc.close();
46680         
46681         var dbody = (this.doc.body || this.doc.documentElement);
46682         //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
46683         // this copies styles from the containing element into thsi one..
46684         // not sure why we need all of this..
46685         //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
46686         
46687         //var ss = this.el.getStyles( 'background-image', 'background-repeat');
46688         //ss['background-attachment'] = 'fixed'; // w3c
46689         dbody.bgProperties = 'fixed'; // ie
46690         //Roo.DomHelper.applyStyles(dbody, ss);
46691         Roo.EventManager.on(this.doc, {
46692             //'mousedown': this.onEditorEvent,
46693             'mouseup': this.onEditorEvent,
46694             'dblclick': this.onEditorEvent,
46695             'click': this.onEditorEvent,
46696             'keyup': this.onEditorEvent,
46697             
46698             buffer:100,
46699             scope: this
46700         });
46701         Roo.EventManager.on(this.doc, {
46702             'paste': this.onPasteEvent,
46703             scope : this
46704         });
46705         if(Roo.isGecko){
46706             Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
46707         }
46708         if(Roo.isIE || Roo.isSafari || Roo.isOpera){
46709             Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
46710         }
46711         this.initialized = true;
46712
46713         
46714         // initialize special key events - enter
46715         new Roo.htmleditor.KeyEnter({core : this});
46716         
46717          
46718         
46719         this.owner.fireEvent('initialize', this);
46720         this.pushValue();
46721     },
46722     
46723     onPasteEvent : function(e,v)
46724     {
46725         // I think we better assume paste is going to be a dirty load of rubish from word..
46726         
46727         // even pasting into a 'email version' of this widget will have to clean up that mess.
46728         var cd = (e.browserEvent.clipboardData || window.clipboardData);
46729         
46730         // check what type of paste - if it's an image, then handle it differently.
46731         if (cd.files.length > 0) {
46732             // pasting images?
46733             var urlAPI = (window.createObjectURL && window) || 
46734                 (window.URL && URL.revokeObjectURL && URL) || 
46735                 (window.webkitURL && webkitURL);
46736     
46737             var url = urlAPI.createObjectURL( cd.files[0]);
46738             this.insertAtCursor('<img src=" + url + ">');
46739             return false;
46740         }
46741         
46742         var html = cd.getData('text/html'); // clipboard event
46743         var parser = new Roo.rtf.Parser(cd.getData('text/rtf'));
46744         var images = parser.doc.getElementsByType('pict');
46745         Roo.log(images);
46746         //Roo.log(imgs);
46747         // fixme..
46748         images = images.filter(function(g) { return !g.path.match(/^rtf\/(head|pgdsctbl|listtable)/); }) // ignore headers
46749                        .map(function(g) { return g.toDataURL(); });
46750         
46751         
46752         html = this.cleanWordChars(html);
46753         
46754         var d = (new DOMParser().parseFromString(html, 'text/html')).body;
46755         
46756         if (images.length > 0) {
46757             Roo.each(d.getElementsByTagName('img'), function(img, i) {
46758                 img.setAttribute('src', images[i]);
46759             });
46760         }
46761         
46762       
46763         new Roo.htmleditor.FilterStyleToTag({ node : d });
46764         new Roo.htmleditor.FilterAttributes({
46765             node : d,
46766             attrib_white : ['href', 'src', 'name', 'align'],
46767             attrib_clean : ['href', 'src' ] 
46768         });
46769         new Roo.htmleditor.FilterBlack({ node : d, tag : this.black});
46770         // should be fonts..
46771         new Roo.htmleditor.FilterKeepChildren({node : d, tag : [ 'FONT' ]} );
46772         new Roo.htmleditor.FilterParagraph({ node : d });
46773         new Roo.htmleditor.FilterSpan({ node : d });
46774         new Roo.htmleditor.FilterLongBr({ node : d });
46775         
46776         
46777         
46778         this.insertAtCursor(d.innerHTML);
46779         
46780         e.preventDefault();
46781         return false;
46782         // default behaveiour should be our local cleanup paste? (optional?)
46783         // for simple editor - we want to hammer the paste and get rid of everything... - so over-rideable..
46784         //this.owner.fireEvent('paste', e, v);
46785     },
46786     // private
46787     onDestroy : function(){
46788         
46789         
46790         
46791         if(this.rendered){
46792             
46793             //for (var i =0; i < this.toolbars.length;i++) {
46794             //    // fixme - ask toolbars for heights?
46795             //    this.toolbars[i].onDestroy();
46796            // }
46797             
46798             //this.wrap.dom.innerHTML = '';
46799             //this.wrap.remove();
46800         }
46801     },
46802
46803     // private
46804     onFirstFocus : function(){
46805         
46806         this.assignDocWin();
46807         
46808         
46809         this.activated = true;
46810          
46811     
46812         if(Roo.isGecko){ // prevent silly gecko errors
46813             this.win.focus();
46814             var s = this.win.getSelection();
46815             if(!s.focusNode || s.focusNode.nodeType != 3){
46816                 var r = s.getRangeAt(0);
46817                 r.selectNodeContents((this.doc.body || this.doc.documentElement));
46818                 r.collapse(true);
46819                 this.deferFocus();
46820             }
46821             try{
46822                 this.execCmd('useCSS', true);
46823                 this.execCmd('styleWithCSS', false);
46824             }catch(e){}
46825         }
46826         this.owner.fireEvent('activate', this);
46827     },
46828
46829     // private
46830     adjustFont: function(btn){
46831         var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
46832         //if(Roo.isSafari){ // safari
46833         //    adjust *= 2;
46834        // }
46835         var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
46836         if(Roo.isSafari){ // safari
46837             var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
46838             v =  (v < 10) ? 10 : v;
46839             v =  (v > 48) ? 48 : v;
46840             v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
46841             
46842         }
46843         
46844         
46845         v = Math.max(1, v+adjust);
46846         
46847         this.execCmd('FontSize', v  );
46848     },
46849
46850     onEditorEvent : function(e)
46851     {
46852         this.owner.fireEvent('editorevent', this, e);
46853       //  this.updateToolbar();
46854         this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
46855     },
46856
46857     insertTag : function(tg)
46858     {
46859         // could be a bit smarter... -> wrap the current selected tRoo..
46860         if (tg.toLowerCase() == 'span' ||
46861             tg.toLowerCase() == 'code' ||
46862             tg.toLowerCase() == 'sup' ||
46863             tg.toLowerCase() == 'sub' 
46864             ) {
46865             
46866             range = this.createRange(this.getSelection());
46867             var wrappingNode = this.doc.createElement(tg.toLowerCase());
46868             wrappingNode.appendChild(range.extractContents());
46869             range.insertNode(wrappingNode);
46870
46871             return;
46872             
46873             
46874             
46875         }
46876         this.execCmd("formatblock",   tg);
46877         
46878     },
46879     
46880     insertText : function(txt)
46881     {
46882         
46883         
46884         var range = this.createRange();
46885         range.deleteContents();
46886                //alert(Sender.getAttribute('label'));
46887                
46888         range.insertNode(this.doc.createTextNode(txt));
46889     } ,
46890     
46891      
46892
46893     /**
46894      * Executes a Midas editor command on the editor document and performs necessary focus and
46895      * toolbar updates. <b>This should only be called after the editor is initialized.</b>
46896      * @param {String} cmd The Midas command
46897      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
46898      */
46899     relayCmd : function(cmd, value){
46900         this.win.focus();
46901         this.execCmd(cmd, value);
46902         this.owner.fireEvent('editorevent', this);
46903         //this.updateToolbar();
46904         this.owner.deferFocus();
46905     },
46906
46907     /**
46908      * Executes a Midas editor command directly on the editor document.
46909      * For visual commands, you should use {@link #relayCmd} instead.
46910      * <b>This should only be called after the editor is initialized.</b>
46911      * @param {String} cmd The Midas command
46912      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
46913      */
46914     execCmd : function(cmd, value){
46915         this.doc.execCommand(cmd, false, value === undefined ? null : value);
46916         this.syncValue();
46917     },
46918  
46919  
46920    
46921     /**
46922      * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
46923      * to insert tRoo.
46924      * @param {String} text | dom node.. 
46925      */
46926     insertAtCursor : function(text)
46927     {
46928         
46929         if(!this.activated){
46930             return;
46931         }
46932          
46933         if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
46934             this.win.focus();
46935             
46936             
46937             // from jquery ui (MIT licenced)
46938             var range, node;
46939             var win = this.win;
46940             
46941             if (win.getSelection && win.getSelection().getRangeAt) {
46942                 
46943                 // delete the existing?
46944                 
46945                 this.createRange(this.getSelection()).deleteContents();
46946                 range = win.getSelection().getRangeAt(0);
46947                 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
46948                 range.insertNode(node);
46949             } else if (win.document.selection && win.document.selection.createRange) {
46950                 // no firefox support
46951                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
46952                 win.document.selection.createRange().pasteHTML(txt);
46953             } else {
46954                 // no firefox support
46955                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
46956                 this.execCmd('InsertHTML', txt);
46957             } 
46958             
46959             this.syncValue();
46960             
46961             this.deferFocus();
46962         }
46963     },
46964  // private
46965     mozKeyPress : function(e){
46966         if(e.ctrlKey){
46967             var c = e.getCharCode(), cmd;
46968           
46969             if(c > 0){
46970                 c = String.fromCharCode(c).toLowerCase();
46971                 switch(c){
46972                     case 'b':
46973                         cmd = 'bold';
46974                         break;
46975                     case 'i':
46976                         cmd = 'italic';
46977                         break;
46978                     
46979                     case 'u':
46980                         cmd = 'underline';
46981                         break;
46982                     
46983                     //case 'v':
46984                       //  this.cleanUpPaste.defer(100, this);
46985                       //  return;
46986                         
46987                 }
46988                 if(cmd){
46989                     this.win.focus();
46990                     this.execCmd(cmd);
46991                     this.deferFocus();
46992                     e.preventDefault();
46993                 }
46994                 
46995             }
46996         }
46997     },
46998
46999     // private
47000     fixKeys : function(){ // load time branching for fastest keydown performance
47001         if(Roo.isIE){
47002             return function(e){
47003                 var k = e.getKey(), r;
47004                 if(k == e.TAB){
47005                     e.stopEvent();
47006                     r = this.doc.selection.createRange();
47007                     if(r){
47008                         r.collapse(true);
47009                         r.pasteHTML('&#160;&#160;&#160;&#160;');
47010                         this.deferFocus();
47011                     }
47012                     return;
47013                 }
47014                 
47015                 if(k == e.ENTER){
47016                     r = this.doc.selection.createRange();
47017                     if(r){
47018                         var target = r.parentElement();
47019                         if(!target || target.tagName.toLowerCase() != 'li'){
47020                             e.stopEvent();
47021                             r.pasteHTML('<br/>');
47022                             r.collapse(false);
47023                             r.select();
47024                         }
47025                     }
47026                 }
47027                 //if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
47028                 //    this.cleanUpPaste.defer(100, this);
47029                 //    return;
47030                 //}
47031                 
47032                 
47033             };
47034         }else if(Roo.isOpera){
47035             return function(e){
47036                 var k = e.getKey();
47037                 if(k == e.TAB){
47038                     e.stopEvent();
47039                     this.win.focus();
47040                     this.execCmd('InsertHTML','&#160;&#160;&#160;&#160;');
47041                     this.deferFocus();
47042                 }
47043                 //if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
47044                 //    this.cleanUpPaste.defer(100, this);
47045                  //   return;
47046                 //}
47047                 
47048             };
47049         }else if(Roo.isSafari){
47050             return function(e){
47051                 var k = e.getKey();
47052                 
47053                 if(k == e.TAB){
47054                     e.stopEvent();
47055                     this.execCmd('InsertText','\t');
47056                     this.deferFocus();
47057                     return;
47058                 }
47059                //if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
47060                  //   this.cleanUpPaste.defer(100, this);
47061                  //   return;
47062                // }
47063                 
47064              };
47065         }
47066     }(),
47067     
47068     getAllAncestors: function()
47069     {
47070         var p = this.getSelectedNode();
47071         var a = [];
47072         if (!p) {
47073             a.push(p); // push blank onto stack..
47074             p = this.getParentElement();
47075         }
47076         
47077         
47078         while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
47079             a.push(p);
47080             p = p.parentNode;
47081         }
47082         a.push(this.doc.body);
47083         return a;
47084     },
47085     lastSel : false,
47086     lastSelNode : false,
47087     
47088     
47089     getSelection : function() 
47090     {
47091         this.assignDocWin();
47092         return Roo.isIE ? this.doc.selection : this.win.getSelection();
47093     },
47094     /**
47095      * Select a dom node
47096      * @param {DomElement} node the node to select
47097      */
47098     selectNode : function(node)
47099     {
47100         
47101             var nodeRange = node.ownerDocument.createRange();
47102             try {
47103                 nodeRange.selectNode(node);
47104             } catch (e) {
47105                 nodeRange.selectNodeContents(node);
47106             }
47107             //nodeRange.collapse(true);
47108             var s = this.win.getSelection();
47109             s.removeAllRanges();
47110             s.addRange(nodeRange);
47111     },
47112     
47113     getSelectedNode: function() 
47114     {
47115         // this may only work on Gecko!!!
47116         
47117         // should we cache this!!!!
47118         
47119         
47120         
47121          
47122         var range = this.createRange(this.getSelection()).cloneRange();
47123         
47124         if (Roo.isIE) {
47125             var parent = range.parentElement();
47126             while (true) {
47127                 var testRange = range.duplicate();
47128                 testRange.moveToElementText(parent);
47129                 if (testRange.inRange(range)) {
47130                     break;
47131                 }
47132                 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
47133                     break;
47134                 }
47135                 parent = parent.parentElement;
47136             }
47137             return parent;
47138         }
47139         
47140         // is ancestor a text element.
47141         var ac =  range.commonAncestorContainer;
47142         if (ac.nodeType == 3) {
47143             ac = ac.parentNode;
47144         }
47145         
47146         var ar = ac.childNodes;
47147          
47148         var nodes = [];
47149         var other_nodes = [];
47150         var has_other_nodes = false;
47151         for (var i=0;i<ar.length;i++) {
47152             if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ? 
47153                 continue;
47154             }
47155             // fullly contained node.
47156             
47157             if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
47158                 nodes.push(ar[i]);
47159                 continue;
47160             }
47161             
47162             // probably selected..
47163             if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
47164                 other_nodes.push(ar[i]);
47165                 continue;
47166             }
47167             // outer..
47168             if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0))  {
47169                 continue;
47170             }
47171             
47172             
47173             has_other_nodes = true;
47174         }
47175         if (!nodes.length && other_nodes.length) {
47176             nodes= other_nodes;
47177         }
47178         if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
47179             return false;
47180         }
47181         
47182         return nodes[0];
47183     },
47184     createRange: function(sel)
47185     {
47186         // this has strange effects when using with 
47187         // top toolbar - not sure if it's a great idea.
47188         //this.editor.contentWindow.focus();
47189         if (typeof sel != "undefined") {
47190             try {
47191                 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
47192             } catch(e) {
47193                 return this.doc.createRange();
47194             }
47195         } else {
47196             return this.doc.createRange();
47197         }
47198     },
47199     getParentElement: function()
47200     {
47201         
47202         this.assignDocWin();
47203         var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
47204         
47205         var range = this.createRange(sel);
47206          
47207         try {
47208             var p = range.commonAncestorContainer;
47209             while (p.nodeType == 3) { // text node
47210                 p = p.parentNode;
47211             }
47212             return p;
47213         } catch (e) {
47214             return null;
47215         }
47216     
47217     },
47218     /***
47219      *
47220      * Range intersection.. the hard stuff...
47221      *  '-1' = before
47222      *  '0' = hits..
47223      *  '1' = after.
47224      *         [ -- selected range --- ]
47225      *   [fail]                        [fail]
47226      *
47227      *    basically..
47228      *      if end is before start or  hits it. fail.
47229      *      if start is after end or hits it fail.
47230      *
47231      *   if either hits (but other is outside. - then it's not 
47232      *   
47233      *    
47234      **/
47235     
47236     
47237     // @see http://www.thismuchiknow.co.uk/?p=64.
47238     rangeIntersectsNode : function(range, node)
47239     {
47240         var nodeRange = node.ownerDocument.createRange();
47241         try {
47242             nodeRange.selectNode(node);
47243         } catch (e) {
47244             nodeRange.selectNodeContents(node);
47245         }
47246     
47247         var rangeStartRange = range.cloneRange();
47248         rangeStartRange.collapse(true);
47249     
47250         var rangeEndRange = range.cloneRange();
47251         rangeEndRange.collapse(false);
47252     
47253         var nodeStartRange = nodeRange.cloneRange();
47254         nodeStartRange.collapse(true);
47255     
47256         var nodeEndRange = nodeRange.cloneRange();
47257         nodeEndRange.collapse(false);
47258     
47259         return rangeStartRange.compareBoundaryPoints(
47260                  Range.START_TO_START, nodeEndRange) == -1 &&
47261                rangeEndRange.compareBoundaryPoints(
47262                  Range.START_TO_START, nodeStartRange) == 1;
47263         
47264          
47265     },
47266     rangeCompareNode : function(range, node)
47267     {
47268         var nodeRange = node.ownerDocument.createRange();
47269         try {
47270             nodeRange.selectNode(node);
47271         } catch (e) {
47272             nodeRange.selectNodeContents(node);
47273         }
47274         
47275         
47276         range.collapse(true);
47277     
47278         nodeRange.collapse(true);
47279      
47280         var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
47281         var ee = range.compareBoundaryPoints(  Range.END_TO_END, nodeRange);
47282          
47283         //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
47284         
47285         var nodeIsBefore   =  ss == 1;
47286         var nodeIsAfter    = ee == -1;
47287         
47288         if (nodeIsBefore && nodeIsAfter) {
47289             return 0; // outer
47290         }
47291         if (!nodeIsBefore && nodeIsAfter) {
47292             return 1; //right trailed.
47293         }
47294         
47295         if (nodeIsBefore && !nodeIsAfter) {
47296             return 2;  // left trailed.
47297         }
47298         // fully contined.
47299         return 3;
47300     },
47301  
47302     cleanWordChars : function(input) {// change the chars to hex code
47303         
47304        var swapCodes  = [ 
47305             [    8211, "&#8211;" ], 
47306             [    8212, "&#8212;" ], 
47307             [    8216,  "'" ],  
47308             [    8217, "'" ],  
47309             [    8220, '"' ],  
47310             [    8221, '"' ],  
47311             [    8226, "*" ],  
47312             [    8230, "..." ]
47313         ]; 
47314         var output = input;
47315         Roo.each(swapCodes, function(sw) { 
47316             var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
47317             
47318             output = output.replace(swapper, sw[1]);
47319         });
47320         
47321         return output;
47322     },
47323     
47324      
47325     
47326         
47327     
47328     cleanUpChild : function (node)
47329     {
47330         
47331         new Roo.htmleditor.FilterComment({node : node});
47332         new Roo.htmleditor.FilterAttributes({
47333                 node : node,
47334                 attrib_black : this.ablack,
47335                 attrib_clean : this.aclean,
47336                 style_white : this.cwhite,
47337                 style_black : this.cblack
47338         });
47339         new Roo.htmleditor.FilterBlack({ node : node, tag : this.black});
47340         new Roo.htmleditor.FilterKeepChildren({node : node, tag : this.tag_remove} );
47341          
47342         
47343     },
47344     
47345     /**
47346      * Clean up MS wordisms...
47347      * @deprecated - use filter directly
47348      */
47349     cleanWord : function(node)
47350     {
47351         new Roo.htmleditor.FilterWord({ node : node ? node : this.doc.body });
47352         
47353     },
47354    
47355     
47356     /**
47357
47358      * @deprecated - use filters
47359      */
47360     cleanTableWidths : function(node)
47361     {
47362         new Roo.htmleditor.FilterTableWidth({ node : node ? node : this.doc.body});
47363         
47364  
47365     },
47366     
47367      
47368         
47369     applyBlacklists : function()
47370     {
47371         var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white  : [];
47372         var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black :  [];
47373         
47374         this.aclean = typeof(this.owner.aclean) != 'undefined' && this.owner.aclean ? this.owner.aclean :  Roo.HtmlEditorCore.aclean;
47375         this.ablack = typeof(this.owner.ablack) != 'undefined' && this.owner.ablack ? this.owner.ablack :  Roo.HtmlEditorCore.ablack;
47376         this.tag_remove = typeof(this.owner.tag_remove) != 'undefined' && this.owner.tag_remove ? this.owner.tag_remove :  Roo.HtmlEditorCore.tag_remove;
47377         
47378         this.white = [];
47379         this.black = [];
47380         Roo.each(Roo.HtmlEditorCore.white, function(tag) {
47381             if (b.indexOf(tag) > -1) {
47382                 return;
47383             }
47384             this.white.push(tag);
47385             
47386         }, this);
47387         
47388         Roo.each(w, function(tag) {
47389             if (b.indexOf(tag) > -1) {
47390                 return;
47391             }
47392             if (this.white.indexOf(tag) > -1) {
47393                 return;
47394             }
47395             this.white.push(tag);
47396             
47397         }, this);
47398         
47399         
47400         Roo.each(Roo.HtmlEditorCore.black, function(tag) {
47401             if (w.indexOf(tag) > -1) {
47402                 return;
47403             }
47404             this.black.push(tag);
47405             
47406         }, this);
47407         
47408         Roo.each(b, function(tag) {
47409             if (w.indexOf(tag) > -1) {
47410                 return;
47411             }
47412             if (this.black.indexOf(tag) > -1) {
47413                 return;
47414             }
47415             this.black.push(tag);
47416             
47417         }, this);
47418         
47419         
47420         w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite  : [];
47421         b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack :  [];
47422         
47423         this.cwhite = [];
47424         this.cblack = [];
47425         Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
47426             if (b.indexOf(tag) > -1) {
47427                 return;
47428             }
47429             this.cwhite.push(tag);
47430             
47431         }, this);
47432         
47433         Roo.each(w, function(tag) {
47434             if (b.indexOf(tag) > -1) {
47435                 return;
47436             }
47437             if (this.cwhite.indexOf(tag) > -1) {
47438                 return;
47439             }
47440             this.cwhite.push(tag);
47441             
47442         }, this);
47443         
47444         
47445         Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
47446             if (w.indexOf(tag) > -1) {
47447                 return;
47448             }
47449             this.cblack.push(tag);
47450             
47451         }, this);
47452         
47453         Roo.each(b, function(tag) {
47454             if (w.indexOf(tag) > -1) {
47455                 return;
47456             }
47457             if (this.cblack.indexOf(tag) > -1) {
47458                 return;
47459             }
47460             this.cblack.push(tag);
47461             
47462         }, this);
47463     },
47464     
47465     setStylesheets : function(stylesheets)
47466     {
47467         if(typeof(stylesheets) == 'string'){
47468             Roo.get(this.iframe.contentDocument.head).createChild({
47469                 tag : 'link',
47470                 rel : 'stylesheet',
47471                 type : 'text/css',
47472                 href : stylesheets
47473             });
47474             
47475             return;
47476         }
47477         var _this = this;
47478      
47479         Roo.each(stylesheets, function(s) {
47480             if(!s.length){
47481                 return;
47482             }
47483             
47484             Roo.get(_this.iframe.contentDocument.head).createChild({
47485                 tag : 'link',
47486                 rel : 'stylesheet',
47487                 type : 'text/css',
47488                 href : s
47489             });
47490         });
47491
47492         
47493     },
47494     
47495     removeStylesheets : function()
47496     {
47497         var _this = this;
47498         
47499         Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
47500             s.remove();
47501         });
47502     },
47503     
47504     setStyle : function(style)
47505     {
47506         Roo.get(this.iframe.contentDocument.head).createChild({
47507             tag : 'style',
47508             type : 'text/css',
47509             html : style
47510         });
47511
47512         return;
47513     }
47514     
47515     // hide stuff that is not compatible
47516     /**
47517      * @event blur
47518      * @hide
47519      */
47520     /**
47521      * @event change
47522      * @hide
47523      */
47524     /**
47525      * @event focus
47526      * @hide
47527      */
47528     /**
47529      * @event specialkey
47530      * @hide
47531      */
47532     /**
47533      * @cfg {String} fieldClass @hide
47534      */
47535     /**
47536      * @cfg {String} focusClass @hide
47537      */
47538     /**
47539      * @cfg {String} autoCreate @hide
47540      */
47541     /**
47542      * @cfg {String} inputType @hide
47543      */
47544     /**
47545      * @cfg {String} invalidClass @hide
47546      */
47547     /**
47548      * @cfg {String} invalidText @hide
47549      */
47550     /**
47551      * @cfg {String} msgFx @hide
47552      */
47553     /**
47554      * @cfg {String} validateOnBlur @hide
47555      */
47556 });
47557
47558 Roo.HtmlEditorCore.white = [
47559         'AREA', 'BR', 'IMG', 'INPUT', 'HR', 'WBR',
47560         
47561        'ADDRESS', 'BLOCKQUOTE', 'CENTER', 'DD',      'DIR',       'DIV', 
47562        'DL',      'DT',         'H1',     'H2',      'H3',        'H4', 
47563        'H5',      'H6',         'HR',     'ISINDEX', 'LISTING',   'MARQUEE', 
47564        'MENU',    'MULTICOL',   'OL',     'P',       'PLAINTEXT', 'PRE', 
47565        'TABLE',   'UL',         'XMP', 
47566        
47567        'CAPTION', 'COL', 'COLGROUP', 'TBODY', 'TD', 'TFOOT', 'TH', 
47568       'THEAD',   'TR', 
47569      
47570       'DIR', 'MENU', 'OL', 'UL', 'DL',
47571        
47572       'EMBED',  'OBJECT'
47573 ];
47574
47575
47576 Roo.HtmlEditorCore.black = [
47577     //    'embed',  'object', // enable - backend responsiblity to clean thiese
47578         'APPLET', // 
47579         'BASE',   'BASEFONT', 'BGSOUND', 'BLINK',  'BODY', 
47580         'FRAME',  'FRAMESET', 'HEAD',    'HTML',   'ILAYER', 
47581         'IFRAME', 'LAYER',  'LINK',     'META',    'OBJECT',   
47582         'SCRIPT', 'STYLE' ,'TITLE',  'XML',
47583         //'FONT' // CLEAN LATER..
47584         'COLGROUP', 'COL'  // messy tables.
47585         
47586 ];
47587 Roo.HtmlEditorCore.clean = [ // ?? needed???
47588      'SCRIPT', 'STYLE', 'TITLE', 'XML'
47589 ];
47590 Roo.HtmlEditorCore.tag_remove = [
47591     'FONT', 'TBODY'  
47592 ];
47593 // attributes..
47594
47595 Roo.HtmlEditorCore.ablack = [
47596     'on'
47597 ];
47598     
47599 Roo.HtmlEditorCore.aclean = [ 
47600     'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc' 
47601 ];
47602
47603 // protocols..
47604 Roo.HtmlEditorCore.pwhite= [
47605         'http',  'https',  'mailto'
47606 ];
47607
47608 // white listed style attributes.
47609 Roo.HtmlEditorCore.cwhite= [
47610       //  'text-align', /// default is to allow most things..
47611       
47612          
47613 //        'font-size'//??
47614 ];
47615
47616 // black listed style attributes.
47617 Roo.HtmlEditorCore.cblack= [
47618       //  'font-size' -- this can be set by the project 
47619 ];
47620
47621
47622
47623
47624     //<script type="text/javascript">
47625
47626 /*
47627  * Ext JS Library 1.1.1
47628  * Copyright(c) 2006-2007, Ext JS, LLC.
47629  * Licence LGPL
47630  * 
47631  */
47632  
47633  
47634 Roo.form.HtmlEditor = function(config){
47635     
47636     
47637     
47638     Roo.form.HtmlEditor.superclass.constructor.call(this, config);
47639     
47640     if (!this.toolbars) {
47641         this.toolbars = [];
47642     }
47643     this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
47644     
47645     
47646 };
47647
47648 /**
47649  * @class Roo.form.HtmlEditor
47650  * @extends Roo.form.Field
47651  * Provides a lightweight HTML Editor component.
47652  *
47653  * This has been tested on Fireforx / Chrome.. IE may not be so great..
47654  * 
47655  * <br><br><b>Note: The focus/blur and validation marking functionality inherited from Ext.form.Field is NOT
47656  * supported by this editor.</b><br/><br/>
47657  * An Editor is a sensitive component that can't be used in all spots standard fields can be used. Putting an Editor within
47658  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
47659  */
47660 Roo.extend(Roo.form.HtmlEditor, Roo.form.Field, {
47661     /**
47662      * @cfg {Boolean} clearUp
47663      */
47664     clearUp : true,
47665       /**
47666      * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
47667      */
47668     toolbars : false,
47669    
47670      /**
47671      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
47672      *                        Roo.resizable.
47673      */
47674     resizable : false,
47675      /**
47676      * @cfg {Number} height (in pixels)
47677      */   
47678     height: 300,
47679    /**
47680      * @cfg {Number} width (in pixels)
47681      */   
47682     width: 500,
47683     
47684     /**
47685      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets - this is usally a good idea  rootURL + '/roojs1/css/undoreset.css',   .
47686      * 
47687      */
47688     stylesheets: false,
47689     
47690     
47691      /**
47692      * @cfg {Array} blacklist of css styles style attributes (blacklist overrides whitelist)
47693      * 
47694      */
47695     cblack: false,
47696     /**
47697      * @cfg {Array} whitelist of css styles style attributes (blacklist overrides whitelist)
47698      * 
47699      */
47700     cwhite: false,
47701     
47702      /**
47703      * @cfg {Array} blacklist of html tags - in addition to standard blacklist.
47704      * 
47705      */
47706     black: false,
47707     /**
47708      * @cfg {Array} whitelist of html tags - in addition to statndard whitelist
47709      * 
47710      */
47711     white: false,
47712     /**
47713      * @cfg {boolean} allowComments - default false - allow comments in HTML source - by default they are stripped - if you are editing email you may need this.
47714      */
47715     allowComments: false,
47716     /**
47717      * @cfg {string} bodyCls- default '' default classes to add to body of editable area - usually undoreset is a good start..
47718      */
47719     
47720     
47721      bodyCls : '',
47722     
47723     // id of frame..
47724     frameId: false,
47725     
47726     // private properties
47727     validationEvent : false,
47728     deferHeight: true,
47729     initialized : false,
47730     activated : false,
47731     
47732     onFocus : Roo.emptyFn,
47733     iframePad:3,
47734     hideMode:'offsets',
47735     
47736     actionMode : 'container', // defaults to hiding it...
47737     
47738     defaultAutoCreate : { // modified by initCompnoent..
47739         tag: "textarea",
47740         style:"width:500px;height:300px;",
47741         autocomplete: "new-password"
47742     },
47743
47744     // private
47745     initComponent : function(){
47746         this.addEvents({
47747             /**
47748              * @event initialize
47749              * Fires when the editor is fully initialized (including the iframe)
47750              * @param {HtmlEditor} this
47751              */
47752             initialize: true,
47753             /**
47754              * @event activate
47755              * Fires when the editor is first receives the focus. Any insertion must wait
47756              * until after this event.
47757              * @param {HtmlEditor} this
47758              */
47759             activate: true,
47760              /**
47761              * @event beforesync
47762              * Fires before the textarea is updated with content from the editor iframe. Return false
47763              * to cancel the sync.
47764              * @param {HtmlEditor} this
47765              * @param {String} html
47766              */
47767             beforesync: true,
47768              /**
47769              * @event beforepush
47770              * Fires before the iframe editor is updated with content from the textarea. Return false
47771              * to cancel the push.
47772              * @param {HtmlEditor} this
47773              * @param {String} html
47774              */
47775             beforepush: true,
47776              /**
47777              * @event sync
47778              * Fires when the textarea is updated with content from the editor iframe.
47779              * @param {HtmlEditor} this
47780              * @param {String} html
47781              */
47782             sync: true,
47783              /**
47784              * @event push
47785              * Fires when the iframe editor is updated with content from the textarea.
47786              * @param {HtmlEditor} this
47787              * @param {String} html
47788              */
47789             push: true,
47790              /**
47791              * @event editmodechange
47792              * Fires when the editor switches edit modes
47793              * @param {HtmlEditor} this
47794              * @param {Boolean} sourceEdit True if source edit, false if standard editing.
47795              */
47796             editmodechange: true,
47797             /**
47798              * @event editorevent
47799              * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
47800              * @param {HtmlEditor} this
47801              */
47802             editorevent: true,
47803             /**
47804              * @event firstfocus
47805              * Fires when on first focus - needed by toolbars..
47806              * @param {HtmlEditor} this
47807              */
47808             firstfocus: true,
47809             /**
47810              * @event autosave
47811              * Auto save the htmlEditor value as a file into Events
47812              * @param {HtmlEditor} this
47813              */
47814             autosave: true,
47815             /**
47816              * @event savedpreview
47817              * preview the saved version of htmlEditor
47818              * @param {HtmlEditor} this
47819              */
47820             savedpreview: true,
47821             
47822             /**
47823             * @event stylesheetsclick
47824             * Fires when press the Sytlesheets button
47825             * @param {Roo.HtmlEditorCore} this
47826             */
47827             stylesheetsclick: true,
47828             /**
47829             * @event paste
47830             * Fires when press user pastes into the editor
47831             * @param {Roo.HtmlEditorCore} this
47832             */
47833             paste: true 
47834         });
47835         this.defaultAutoCreate =  {
47836             tag: "textarea",
47837             style:'width: ' + this.width + 'px;height: ' + this.height + 'px;',
47838             autocomplete: "new-password"
47839         };
47840     },
47841
47842     /**
47843      * Protected method that will not generally be called directly. It
47844      * is called when the editor creates its toolbar. Override this method if you need to
47845      * add custom toolbar buttons.
47846      * @param {HtmlEditor} editor
47847      */
47848     createToolbar : function(editor){
47849         Roo.log("create toolbars");
47850         if (!editor.toolbars || !editor.toolbars.length) {
47851             editor.toolbars = [ new Roo.form.HtmlEditor.ToolbarStandard() ]; // can be empty?
47852         }
47853         
47854         for (var i =0 ; i < editor.toolbars.length;i++) {
47855             editor.toolbars[i] = Roo.factory(
47856                     typeof(editor.toolbars[i]) == 'string' ?
47857                         { xtype: editor.toolbars[i]} : editor.toolbars[i],
47858                 Roo.form.HtmlEditor);
47859             editor.toolbars[i].init(editor);
47860         }
47861          
47862         
47863     },
47864
47865      
47866     // private
47867     onRender : function(ct, position)
47868     {
47869         var _t = this;
47870         Roo.form.HtmlEditor.superclass.onRender.call(this, ct, position);
47871         
47872         this.wrap = this.el.wrap({
47873             cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
47874         });
47875         
47876         this.editorcore.onRender(ct, position);
47877          
47878         if (this.resizable) {
47879             this.resizeEl = new Roo.Resizable(this.wrap, {
47880                 pinned : true,
47881                 wrap: true,
47882                 dynamic : true,
47883                 minHeight : this.height,
47884                 height: this.height,
47885                 handles : this.resizable,
47886                 width: this.width,
47887                 listeners : {
47888                     resize : function(r, w, h) {
47889                         _t.onResize(w,h); // -something
47890                     }
47891                 }
47892             });
47893             
47894         }
47895         this.createToolbar(this);
47896        
47897         
47898         if(!this.width){
47899             this.setSize(this.wrap.getSize());
47900         }
47901         if (this.resizeEl) {
47902             this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
47903             // should trigger onReize..
47904         }
47905         
47906         this.keyNav = new Roo.KeyNav(this.el, {
47907             
47908             "tab" : function(e){
47909                 e.preventDefault();
47910                 
47911                 var value = this.getValue();
47912                 
47913                 var start = this.el.dom.selectionStart;
47914                 var end = this.el.dom.selectionEnd;
47915                 
47916                 if(!e.shiftKey){
47917                     
47918                     this.setValue(value.substring(0, start) + "\t" + value.substring(end));
47919                     this.el.dom.setSelectionRange(end + 1, end + 1);
47920                     return;
47921                 }
47922                 
47923                 var f = value.substring(0, start).split("\t");
47924                 
47925                 if(f.pop().length != 0){
47926                     return;
47927                 }
47928                 
47929                 this.setValue(f.join("\t") + value.substring(end));
47930                 this.el.dom.setSelectionRange(start - 1, start - 1);
47931                 
47932             },
47933             
47934             "home" : function(e){
47935                 e.preventDefault();
47936                 
47937                 var curr = this.el.dom.selectionStart;
47938                 var lines = this.getValue().split("\n");
47939                 
47940                 if(!lines.length){
47941                     return;
47942                 }
47943                 
47944                 if(e.ctrlKey){
47945                     this.el.dom.setSelectionRange(0, 0);
47946                     return;
47947                 }
47948                 
47949                 var pos = 0;
47950                 
47951                 for (var i = 0; i < lines.length;i++) {
47952                     pos += lines[i].length;
47953                     
47954                     if(i != 0){
47955                         pos += 1;
47956                     }
47957                     
47958                     if(pos < curr){
47959                         continue;
47960                     }
47961                     
47962                     pos -= lines[i].length;
47963                     
47964                     break;
47965                 }
47966                 
47967                 if(!e.shiftKey){
47968                     this.el.dom.setSelectionRange(pos, pos);
47969                     return;
47970                 }
47971                 
47972                 this.el.dom.selectionStart = pos;
47973                 this.el.dom.selectionEnd = curr;
47974             },
47975             
47976             "end" : function(e){
47977                 e.preventDefault();
47978                 
47979                 var curr = this.el.dom.selectionStart;
47980                 var lines = this.getValue().split("\n");
47981                 
47982                 if(!lines.length){
47983                     return;
47984                 }
47985                 
47986                 if(e.ctrlKey){
47987                     this.el.dom.setSelectionRange(this.getValue().length, this.getValue().length);
47988                     return;
47989                 }
47990                 
47991                 var pos = 0;
47992                 
47993                 for (var i = 0; i < lines.length;i++) {
47994                     
47995                     pos += lines[i].length;
47996                     
47997                     if(i != 0){
47998                         pos += 1;
47999                     }
48000                     
48001                     if(pos < curr){
48002                         continue;
48003                     }
48004                     
48005                     break;
48006                 }
48007                 
48008                 if(!e.shiftKey){
48009                     this.el.dom.setSelectionRange(pos, pos);
48010                     return;
48011                 }
48012                 
48013                 this.el.dom.selectionStart = curr;
48014                 this.el.dom.selectionEnd = pos;
48015             },
48016
48017             scope : this,
48018
48019             doRelay : function(foo, bar, hname){
48020                 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
48021             },
48022
48023             forceKeyDown: true
48024         });
48025         
48026 //        if(this.autosave && this.w){
48027 //            this.autoSaveFn = setInterval(this.autosave, 1000);
48028 //        }
48029     },
48030
48031     // private
48032     onResize : function(w, h)
48033     {
48034         Roo.form.HtmlEditor.superclass.onResize.apply(this, arguments);
48035         var ew = false;
48036         var eh = false;
48037         
48038         if(this.el ){
48039             if(typeof w == 'number'){
48040                 var aw = w - this.wrap.getFrameWidth('lr');
48041                 this.el.setWidth(this.adjustWidth('textarea', aw));
48042                 ew = aw;
48043             }
48044             if(typeof h == 'number'){
48045                 var tbh = 0;
48046                 for (var i =0; i < this.toolbars.length;i++) {
48047                     // fixme - ask toolbars for heights?
48048                     tbh += this.toolbars[i].tb.el.getHeight();
48049                     if (this.toolbars[i].footer) {
48050                         tbh += this.toolbars[i].footer.el.getHeight();
48051                     }
48052                 }
48053                 
48054                 
48055                 
48056                 
48057                 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
48058                 ah -= 5; // knock a few pixes off for look..
48059 //                Roo.log(ah);
48060                 this.el.setHeight(this.adjustWidth('textarea', ah));
48061                 var eh = ah;
48062             }
48063         }
48064         Roo.log('onResize:' + [w,h,ew,eh].join(',') );
48065         this.editorcore.onResize(ew,eh);
48066         
48067     },
48068
48069     /**
48070      * Toggles the editor between standard and source edit mode.
48071      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
48072      */
48073     toggleSourceEdit : function(sourceEditMode)
48074     {
48075         this.editorcore.toggleSourceEdit(sourceEditMode);
48076         
48077         if(this.editorcore.sourceEditMode){
48078             Roo.log('editor - showing textarea');
48079             
48080 //            Roo.log('in');
48081 //            Roo.log(this.syncValue());
48082             this.editorcore.syncValue();
48083             this.el.removeClass('x-hidden');
48084             this.el.dom.removeAttribute('tabIndex');
48085             this.el.focus();
48086             this.el.dom.scrollTop = 0;
48087             
48088             
48089             for (var i = 0; i < this.toolbars.length; i++) {
48090                 if(this.toolbars[i] instanceof Roo.form.HtmlEditor.ToolbarContext){
48091                     this.toolbars[i].tb.hide();
48092                     this.toolbars[i].footer.hide();
48093                 }
48094             }
48095             
48096         }else{
48097             Roo.log('editor - hiding textarea');
48098 //            Roo.log('out')
48099 //            Roo.log(this.pushValue()); 
48100             this.editorcore.pushValue();
48101             
48102             this.el.addClass('x-hidden');
48103             this.el.dom.setAttribute('tabIndex', -1);
48104             
48105             for (var i = 0; i < this.toolbars.length; i++) {
48106                 if(this.toolbars[i] instanceof Roo.form.HtmlEditor.ToolbarContext){
48107                     this.toolbars[i].tb.show();
48108                     this.toolbars[i].footer.show();
48109                 }
48110             }
48111             
48112             //this.deferFocus();
48113         }
48114         
48115         this.setSize(this.wrap.getSize());
48116         this.onResize(this.wrap.getSize().width, this.wrap.getSize().height);
48117         
48118         this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
48119     },
48120  
48121     // private (for BoxComponent)
48122     adjustSize : Roo.BoxComponent.prototype.adjustSize,
48123
48124     // private (for BoxComponent)
48125     getResizeEl : function(){
48126         return this.wrap;
48127     },
48128
48129     // private (for BoxComponent)
48130     getPositionEl : function(){
48131         return this.wrap;
48132     },
48133
48134     // private
48135     initEvents : function(){
48136         this.originalValue = this.getValue();
48137     },
48138
48139     /**
48140      * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
48141      * @method
48142      */
48143     markInvalid : Roo.emptyFn,
48144     /**
48145      * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
48146      * @method
48147      */
48148     clearInvalid : Roo.emptyFn,
48149
48150     setValue : function(v){
48151         Roo.form.HtmlEditor.superclass.setValue.call(this, v);
48152         this.editorcore.pushValue();
48153     },
48154
48155      
48156     // private
48157     deferFocus : function(){
48158         this.focus.defer(10, this);
48159     },
48160
48161     // doc'ed in Field
48162     focus : function(){
48163         this.editorcore.focus();
48164         
48165     },
48166       
48167
48168     // private
48169     onDestroy : function(){
48170         
48171         
48172         
48173         if(this.rendered){
48174             
48175             for (var i =0; i < this.toolbars.length;i++) {
48176                 // fixme - ask toolbars for heights?
48177                 this.toolbars[i].onDestroy();
48178             }
48179             
48180             this.wrap.dom.innerHTML = '';
48181             this.wrap.remove();
48182         }
48183     },
48184
48185     // private
48186     onFirstFocus : function(){
48187         //Roo.log("onFirstFocus");
48188         this.editorcore.onFirstFocus();
48189          for (var i =0; i < this.toolbars.length;i++) {
48190             this.toolbars[i].onFirstFocus();
48191         }
48192         
48193     },
48194     
48195     // private
48196     syncValue : function()
48197     {
48198         this.editorcore.syncValue();
48199     },
48200     
48201     pushValue : function()
48202     {
48203         this.editorcore.pushValue();
48204     },
48205     
48206     setStylesheets : function(stylesheets)
48207     {
48208         this.editorcore.setStylesheets(stylesheets);
48209     },
48210     
48211     removeStylesheets : function()
48212     {
48213         this.editorcore.removeStylesheets();
48214     }
48215      
48216     
48217     // hide stuff that is not compatible
48218     /**
48219      * @event blur
48220      * @hide
48221      */
48222     /**
48223      * @event change
48224      * @hide
48225      */
48226     /**
48227      * @event focus
48228      * @hide
48229      */
48230     /**
48231      * @event specialkey
48232      * @hide
48233      */
48234     /**
48235      * @cfg {String} fieldClass @hide
48236      */
48237     /**
48238      * @cfg {String} focusClass @hide
48239      */
48240     /**
48241      * @cfg {String} autoCreate @hide
48242      */
48243     /**
48244      * @cfg {String} inputType @hide
48245      */
48246     /**
48247      * @cfg {String} invalidClass @hide
48248      */
48249     /**
48250      * @cfg {String} invalidText @hide
48251      */
48252     /**
48253      * @cfg {String} msgFx @hide
48254      */
48255     /**
48256      * @cfg {String} validateOnBlur @hide
48257      */
48258 });
48259  
48260     // <script type="text/javascript">
48261 /*
48262  * Based on
48263  * Ext JS Library 1.1.1
48264  * Copyright(c) 2006-2007, Ext JS, LLC.
48265  *  
48266  
48267  */
48268
48269 /**
48270  * @class Roo.form.HtmlEditorToolbar1
48271  * Basic Toolbar
48272  * 
48273  * Usage:
48274  *
48275  new Roo.form.HtmlEditor({
48276     ....
48277     toolbars : [
48278         new Roo.form.HtmlEditorToolbar1({
48279             disable : { fonts: 1 , format: 1, ..., ... , ...],
48280             btns : [ .... ]
48281         })
48282     }
48283      
48284  * 
48285  * @cfg {Object} disable List of elements to disable..
48286  * @cfg {Array} btns List of additional buttons.
48287  * 
48288  * 
48289  * NEEDS Extra CSS? 
48290  * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
48291  */
48292  
48293 Roo.form.HtmlEditor.ToolbarStandard = function(config)
48294 {
48295     
48296     Roo.apply(this, config);
48297     
48298     // default disabled, based on 'good practice'..
48299     this.disable = this.disable || {};
48300     Roo.applyIf(this.disable, {
48301         fontSize : true,
48302         colors : true,
48303         specialElements : true
48304     });
48305     
48306     
48307     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
48308     // dont call parent... till later.
48309 }
48310
48311 Roo.apply(Roo.form.HtmlEditor.ToolbarStandard.prototype,  {
48312     
48313     tb: false,
48314     
48315     rendered: false,
48316     
48317     editor : false,
48318     editorcore : false,
48319     /**
48320      * @cfg {Object} disable  List of toolbar elements to disable
48321          
48322      */
48323     disable : false,
48324     
48325     
48326      /**
48327      * @cfg {String} createLinkText The default text for the create link prompt
48328      */
48329     createLinkText : 'Please enter the URL for the link:',
48330     /**
48331      * @cfg {String} defaultLinkValue The default value for the create link prompt (defaults to http:/ /)
48332      */
48333     defaultLinkValue : 'http:/'+'/',
48334    
48335     
48336       /**
48337      * @cfg {Array} fontFamilies An array of available font families
48338      */
48339     fontFamilies : [
48340         'Arial',
48341         'Courier New',
48342         'Tahoma',
48343         'Times New Roman',
48344         'Verdana'
48345     ],
48346     
48347     specialChars : [
48348            "&#169;",
48349           "&#174;",     
48350           "&#8482;",    
48351           "&#163;" ,    
48352          // "&#8212;",    
48353           "&#8230;",    
48354           "&#247;" ,    
48355         //  "&#225;" ,     ?? a acute?
48356            "&#8364;"    , //Euro
48357        //   "&#8220;"    ,
48358         //  "&#8221;"    ,
48359         //  "&#8226;"    ,
48360           "&#176;"  //   , // degrees
48361
48362          // "&#233;"     , // e ecute
48363          // "&#250;"     , // u ecute?
48364     ],
48365     
48366     specialElements : [
48367         {
48368             text: "Insert Table",
48369             xtype: 'MenuItem',
48370             xns : Roo.Menu,
48371             ihtml :  '<table><tr><td>Cell</td></tr></table>' 
48372                 
48373         },
48374         {    
48375             text: "Insert Image",
48376             xtype: 'MenuItem',
48377             xns : Roo.Menu,
48378             ihtml : '<img src="about:blank"/>'
48379             
48380         }
48381         
48382          
48383     ],
48384     
48385     
48386     inputElements : [ 
48387             "form", "input:text", "input:hidden", "input:checkbox", "input:radio", "input:password", 
48388             "input:submit", "input:button", "select", "textarea", "label" ],
48389     formats : [
48390         ["p"] ,  
48391         ["h1"],["h2"],["h3"],["h4"],["h5"],["h6"], 
48392         ["pre"],[ "code"], 
48393         ["abbr"],[ "acronym"],[ "address"],[ "cite"],[ "samp"],[ "var"],
48394         ['div'],['span'],
48395         ['sup'],['sub']
48396     ],
48397     
48398     cleanStyles : [
48399         "font-size"
48400     ],
48401      /**
48402      * @cfg {String} defaultFont default font to use.
48403      */
48404     defaultFont: 'tahoma',
48405    
48406     fontSelect : false,
48407     
48408     
48409     formatCombo : false,
48410     
48411     init : function(editor)
48412     {
48413         this.editor = editor;
48414         this.editorcore = editor.editorcore ? editor.editorcore : editor;
48415         var editorcore = this.editorcore;
48416         
48417         var _t = this;
48418         
48419         var fid = editorcore.frameId;
48420         var etb = this;
48421         function btn(id, toggle, handler){
48422             var xid = fid + '-'+ id ;
48423             return {
48424                 id : xid,
48425                 cmd : id,
48426                 cls : 'x-btn-icon x-edit-'+id,
48427                 enableToggle:toggle !== false,
48428                 scope: _t, // was editor...
48429                 handler:handler||_t.relayBtnCmd,
48430                 clickEvent:'mousedown',
48431                 tooltip: etb.buttonTips[id] || undefined, ///tips ???
48432                 tabIndex:-1
48433             };
48434         }
48435         
48436         
48437         
48438         var tb = new Roo.Toolbar(editor.wrap.dom.firstChild);
48439         this.tb = tb;
48440          // stop form submits
48441         tb.el.on('click', function(e){
48442             e.preventDefault(); // what does this do?
48443         });
48444
48445         if(!this.disable.font) { // && !Roo.isSafari){
48446             /* why no safari for fonts 
48447             editor.fontSelect = tb.el.createChild({
48448                 tag:'select',
48449                 tabIndex: -1,
48450                 cls:'x-font-select',
48451                 html: this.createFontOptions()
48452             });
48453             
48454             editor.fontSelect.on('change', function(){
48455                 var font = editor.fontSelect.dom.value;
48456                 editor.relayCmd('fontname', font);
48457                 editor.deferFocus();
48458             }, editor);
48459             
48460             tb.add(
48461                 editor.fontSelect.dom,
48462                 '-'
48463             );
48464             */
48465             
48466         };
48467         if(!this.disable.formats){
48468             this.formatCombo = new Roo.form.ComboBox({
48469                 store: new Roo.data.SimpleStore({
48470                     id : 'tag',
48471                     fields: ['tag'],
48472                     data : this.formats // from states.js
48473                 }),
48474                 blockFocus : true,
48475                 name : '',
48476                 //autoCreate : {tag: "div",  size: "20"},
48477                 displayField:'tag',
48478                 typeAhead: false,
48479                 mode: 'local',
48480                 editable : false,
48481                 triggerAction: 'all',
48482                 emptyText:'Add tag',
48483                 selectOnFocus:true,
48484                 width:135,
48485                 listeners : {
48486                     'select': function(c, r, i) {
48487                         editorcore.insertTag(r.get('tag'));
48488                         editor.focus();
48489                     }
48490                 }
48491
48492             });
48493             tb.addField(this.formatCombo);
48494             
48495         }
48496         
48497         if(!this.disable.format){
48498             tb.add(
48499                 btn('bold'),
48500                 btn('italic'),
48501                 btn('underline'),
48502                 btn('strikethrough')
48503             );
48504         };
48505         if(!this.disable.fontSize){
48506             tb.add(
48507                 '-',
48508                 
48509                 
48510                 btn('increasefontsize', false, editorcore.adjustFont),
48511                 btn('decreasefontsize', false, editorcore.adjustFont)
48512             );
48513         };
48514         
48515         
48516         if(!this.disable.colors){
48517             tb.add(
48518                 '-', {
48519                     id:editorcore.frameId +'-forecolor',
48520                     cls:'x-btn-icon x-edit-forecolor',
48521                     clickEvent:'mousedown',
48522                     tooltip: this.buttonTips['forecolor'] || undefined,
48523                     tabIndex:-1,
48524                     menu : new Roo.menu.ColorMenu({
48525                         allowReselect: true,
48526                         focus: Roo.emptyFn,
48527                         value:'000000',
48528                         plain:true,
48529                         selectHandler: function(cp, color){
48530                             editorcore.execCmd('forecolor', Roo.isSafari || Roo.isIE ? '#'+color : color);
48531                             editor.deferFocus();
48532                         },
48533                         scope: editorcore,
48534                         clickEvent:'mousedown'
48535                     })
48536                 }, {
48537                     id:editorcore.frameId +'backcolor',
48538                     cls:'x-btn-icon x-edit-backcolor',
48539                     clickEvent:'mousedown',
48540                     tooltip: this.buttonTips['backcolor'] || undefined,
48541                     tabIndex:-1,
48542                     menu : new Roo.menu.ColorMenu({
48543                         focus: Roo.emptyFn,
48544                         value:'FFFFFF',
48545                         plain:true,
48546                         allowReselect: true,
48547                         selectHandler: function(cp, color){
48548                             if(Roo.isGecko){
48549                                 editorcore.execCmd('useCSS', false);
48550                                 editorcore.execCmd('hilitecolor', color);
48551                                 editorcore.execCmd('useCSS', true);
48552                                 editor.deferFocus();
48553                             }else{
48554                                 editorcore.execCmd(Roo.isOpera ? 'hilitecolor' : 'backcolor', 
48555                                     Roo.isSafari || Roo.isIE ? '#'+color : color);
48556                                 editor.deferFocus();
48557                             }
48558                         },
48559                         scope:editorcore,
48560                         clickEvent:'mousedown'
48561                     })
48562                 }
48563             );
48564         };
48565         // now add all the items...
48566         
48567
48568         if(!this.disable.alignments){
48569             tb.add(
48570                 '-',
48571                 btn('justifyleft'),
48572                 btn('justifycenter'),
48573                 btn('justifyright')
48574             );
48575         };
48576
48577         //if(!Roo.isSafari){
48578             if(!this.disable.links){
48579                 tb.add(
48580                     '-',
48581                     btn('createlink', false, this.createLink)    /// MOVE TO HERE?!!?!?!?!
48582                 );
48583             };
48584
48585             if(!this.disable.lists){
48586                 tb.add(
48587                     '-',
48588                     btn('insertorderedlist'),
48589                     btn('insertunorderedlist')
48590                 );
48591             }
48592             if(!this.disable.sourceEdit){
48593                 tb.add(
48594                     '-',
48595                     btn('sourceedit', true, function(btn){
48596                         this.toggleSourceEdit(btn.pressed);
48597                     })
48598                 );
48599             }
48600         //}
48601         
48602         var smenu = { };
48603         // special menu.. - needs to be tidied up..
48604         if (!this.disable.special) {
48605             smenu = {
48606                 text: "&#169;",
48607                 cls: 'x-edit-none',
48608                 
48609                 menu : {
48610                     items : []
48611                 }
48612             };
48613             for (var i =0; i < this.specialChars.length; i++) {
48614                 smenu.menu.items.push({
48615                     
48616                     html: this.specialChars[i],
48617                     handler: function(a,b) {
48618                         editorcore.insertAtCursor(String.fromCharCode(a.html.replace('&#','').replace(';', '')));
48619                         //editor.insertAtCursor(a.html);
48620                         
48621                     },
48622                     tabIndex:-1
48623                 });
48624             }
48625             
48626             
48627             tb.add(smenu);
48628             
48629             
48630         }
48631         
48632         var cmenu = { };
48633         if (!this.disable.cleanStyles) {
48634             cmenu = {
48635                 cls: 'x-btn-icon x-btn-clear',
48636                 
48637                 menu : {
48638                     items : []
48639                 }
48640             };
48641             for (var i =0; i < this.cleanStyles.length; i++) {
48642                 cmenu.menu.items.push({
48643                     actiontype : this.cleanStyles[i],
48644                     html: 'Remove ' + this.cleanStyles[i],
48645                     handler: function(a,b) {
48646 //                        Roo.log(a);
48647 //                        Roo.log(b);
48648                         var c = Roo.get(editorcore.doc.body);
48649                         c.select('[style]').each(function(s) {
48650                             s.dom.style.removeProperty(a.actiontype);
48651                         });
48652                         editorcore.syncValue();
48653                     },
48654                     tabIndex:-1
48655                 });
48656             }
48657             cmenu.menu.items.push({
48658                 actiontype : 'tablewidths',
48659                 html: 'Remove Table Widths',
48660                 handler: function(a,b) {
48661                     editorcore.cleanTableWidths();
48662                     editorcore.syncValue();
48663                 },
48664                 tabIndex:-1
48665             });
48666             cmenu.menu.items.push({
48667                 actiontype : 'word',
48668                 html: 'Remove MS Word Formating',
48669                 handler: function(a,b) {
48670                     editorcore.cleanWord();
48671                     editorcore.syncValue();
48672                 },
48673                 tabIndex:-1
48674             });
48675             
48676             cmenu.menu.items.push({
48677                 actiontype : 'all',
48678                 html: 'Remove All Styles',
48679                 handler: function(a,b) {
48680                     
48681                     var c = Roo.get(editorcore.doc.body);
48682                     c.select('[style]').each(function(s) {
48683                         s.dom.removeAttribute('style');
48684                     });
48685                     editorcore.syncValue();
48686                 },
48687                 tabIndex:-1
48688             });
48689             
48690             cmenu.menu.items.push({
48691                 actiontype : 'all',
48692                 html: 'Remove All CSS Classes',
48693                 handler: function(a,b) {
48694                     
48695                     var c = Roo.get(editorcore.doc.body);
48696                     c.select('[class]').each(function(s) {
48697                         s.dom.removeAttribute('class');
48698                     });
48699                     editorcore.cleanWord();
48700                     editorcore.syncValue();
48701                 },
48702                 tabIndex:-1
48703             });
48704             
48705              cmenu.menu.items.push({
48706                 actiontype : 'tidy',
48707                 html: 'Tidy HTML Source',
48708                 handler: function(a,b) {
48709                     new Roo.htmleditor.Tidy(editorcore.doc.body);
48710                     editorcore.syncValue();
48711                 },
48712                 tabIndex:-1
48713             });
48714             
48715             
48716             tb.add(cmenu);
48717         }
48718          
48719         if (!this.disable.specialElements) {
48720             var semenu = {
48721                 text: "Other;",
48722                 cls: 'x-edit-none',
48723                 menu : {
48724                     items : []
48725                 }
48726             };
48727             for (var i =0; i < this.specialElements.length; i++) {
48728                 semenu.menu.items.push(
48729                     Roo.apply({ 
48730                         handler: function(a,b) {
48731                             editor.insertAtCursor(this.ihtml);
48732                         }
48733                     }, this.specialElements[i])
48734                 );
48735                     
48736             }
48737             
48738             tb.add(semenu);
48739             
48740             
48741         }
48742          
48743         
48744         if (this.btns) {
48745             for(var i =0; i< this.btns.length;i++) {
48746                 var b = Roo.factory(this.btns[i],this.btns[i].xns || Roo.form);
48747                 b.cls =  'x-edit-none';
48748                 
48749                 if(typeof(this.btns[i].cls) != 'undefined' && this.btns[i].cls.indexOf('x-init-enable') !== -1){
48750                     b.cls += ' x-init-enable';
48751                 }
48752                 
48753                 b.scope = editorcore;
48754                 tb.add(b);
48755             }
48756         
48757         }
48758         
48759         
48760         
48761         // disable everything...
48762         
48763         this.tb.items.each(function(item){
48764             
48765            if(
48766                 item.id != editorcore.frameId+ '-sourceedit' && 
48767                 (typeof(item.cls) != 'undefined' && item.cls.indexOf('x-init-enable') === -1)
48768             ){
48769                 
48770                 item.disable();
48771             }
48772         });
48773         this.rendered = true;
48774         
48775         // the all the btns;
48776         editor.on('editorevent', this.updateToolbar, this);
48777         // other toolbars need to implement this..
48778         //editor.on('editmodechange', this.updateToolbar, this);
48779     },
48780     
48781     
48782     relayBtnCmd : function(btn) {
48783         this.editorcore.relayCmd(btn.cmd);
48784     },
48785     // private used internally
48786     createLink : function(){
48787         Roo.log("create link?");
48788         var url = prompt(this.createLinkText, this.defaultLinkValue);
48789         if(url && url != 'http:/'+'/'){
48790             this.editorcore.relayCmd('createlink', url);
48791         }
48792     },
48793
48794     
48795     /**
48796      * Protected method that will not generally be called directly. It triggers
48797      * a toolbar update by reading the markup state of the current selection in the editor.
48798      */
48799     updateToolbar: function(){
48800
48801         if(!this.editorcore.activated){
48802             this.editor.onFirstFocus();
48803             return;
48804         }
48805
48806         var btns = this.tb.items.map, 
48807             doc = this.editorcore.doc,
48808             frameId = this.editorcore.frameId;
48809
48810         if(!this.disable.font && !Roo.isSafari){
48811             /*
48812             var name = (doc.queryCommandValue('FontName')||this.editor.defaultFont).toLowerCase();
48813             if(name != this.fontSelect.dom.value){
48814                 this.fontSelect.dom.value = name;
48815             }
48816             */
48817         }
48818         if(!this.disable.format){
48819             btns[frameId + '-bold'].toggle(doc.queryCommandState('bold'));
48820             btns[frameId + '-italic'].toggle(doc.queryCommandState('italic'));
48821             btns[frameId + '-underline'].toggle(doc.queryCommandState('underline'));
48822             btns[frameId + '-strikethrough'].toggle(doc.queryCommandState('strikethrough'));
48823         }
48824         if(!this.disable.alignments){
48825             btns[frameId + '-justifyleft'].toggle(doc.queryCommandState('justifyleft'));
48826             btns[frameId + '-justifycenter'].toggle(doc.queryCommandState('justifycenter'));
48827             btns[frameId + '-justifyright'].toggle(doc.queryCommandState('justifyright'));
48828         }
48829         if(!Roo.isSafari && !this.disable.lists){
48830             btns[frameId + '-insertorderedlist'].toggle(doc.queryCommandState('insertorderedlist'));
48831             btns[frameId + '-insertunorderedlist'].toggle(doc.queryCommandState('insertunorderedlist'));
48832         }
48833         
48834         var ans = this.editorcore.getAllAncestors();
48835         if (this.formatCombo) {
48836             
48837             
48838             var store = this.formatCombo.store;
48839             this.formatCombo.setValue("");
48840             for (var i =0; i < ans.length;i++) {
48841                 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
48842                     // select it..
48843                     this.formatCombo.setValue(ans[i].tagName.toLowerCase());
48844                     break;
48845                 }
48846             }
48847         }
48848         
48849         
48850         
48851         // hides menus... - so this cant be on a menu...
48852         Roo.menu.MenuMgr.hideAll();
48853
48854         //this.editorsyncValue();
48855     },
48856    
48857     
48858     createFontOptions : function(){
48859         var buf = [], fs = this.fontFamilies, ff, lc;
48860         
48861         
48862         
48863         for(var i = 0, len = fs.length; i< len; i++){
48864             ff = fs[i];
48865             lc = ff.toLowerCase();
48866             buf.push(
48867                 '<option value="',lc,'" style="font-family:',ff,';"',
48868                     (this.defaultFont == lc ? ' selected="true">' : '>'),
48869                     ff,
48870                 '</option>'
48871             );
48872         }
48873         return buf.join('');
48874     },
48875     
48876     toggleSourceEdit : function(sourceEditMode){
48877         
48878         Roo.log("toolbar toogle");
48879         if(sourceEditMode === undefined){
48880             sourceEditMode = !this.sourceEditMode;
48881         }
48882         this.sourceEditMode = sourceEditMode === true;
48883         var btn = this.tb.items.get(this.editorcore.frameId +'-sourceedit');
48884         // just toggle the button?
48885         if(btn.pressed !== this.sourceEditMode){
48886             btn.toggle(this.sourceEditMode);
48887             return;
48888         }
48889         
48890         if(sourceEditMode){
48891             Roo.log("disabling buttons");
48892             this.tb.items.each(function(item){
48893                 if(item.cmd != 'sourceedit' && (typeof(item.cls) != 'undefined' && item.cls.indexOf('x-init-enable') === -1)){
48894                     item.disable();
48895                 }
48896             });
48897           
48898         }else{
48899             Roo.log("enabling buttons");
48900             if(this.editorcore.initialized){
48901                 this.tb.items.each(function(item){
48902                     item.enable();
48903                 });
48904             }
48905             
48906         }
48907         Roo.log("calling toggole on editor");
48908         // tell the editor that it's been pressed..
48909         this.editor.toggleSourceEdit(sourceEditMode);
48910        
48911     },
48912      /**
48913      * Object collection of toolbar tooltips for the buttons in the editor. The key
48914      * is the command id associated with that button and the value is a valid QuickTips object.
48915      * For example:
48916 <pre><code>
48917 {
48918     bold : {
48919         title: 'Bold (Ctrl+B)',
48920         text: 'Make the selected text bold.',
48921         cls: 'x-html-editor-tip'
48922     },
48923     italic : {
48924         title: 'Italic (Ctrl+I)',
48925         text: 'Make the selected text italic.',
48926         cls: 'x-html-editor-tip'
48927     },
48928     ...
48929 </code></pre>
48930     * @type Object
48931      */
48932     buttonTips : {
48933         bold : {
48934             title: 'Bold (Ctrl+B)',
48935             text: 'Make the selected text bold.',
48936             cls: 'x-html-editor-tip'
48937         },
48938         italic : {
48939             title: 'Italic (Ctrl+I)',
48940             text: 'Make the selected text italic.',
48941             cls: 'x-html-editor-tip'
48942         },
48943         underline : {
48944             title: 'Underline (Ctrl+U)',
48945             text: 'Underline the selected text.',
48946             cls: 'x-html-editor-tip'
48947         },
48948         strikethrough : {
48949             title: 'Strikethrough',
48950             text: 'Strikethrough the selected text.',
48951             cls: 'x-html-editor-tip'
48952         },
48953         increasefontsize : {
48954             title: 'Grow Text',
48955             text: 'Increase the font size.',
48956             cls: 'x-html-editor-tip'
48957         },
48958         decreasefontsize : {
48959             title: 'Shrink Text',
48960             text: 'Decrease the font size.',
48961             cls: 'x-html-editor-tip'
48962         },
48963         backcolor : {
48964             title: 'Text Highlight Color',
48965             text: 'Change the background color of the selected text.',
48966             cls: 'x-html-editor-tip'
48967         },
48968         forecolor : {
48969             title: 'Font Color',
48970             text: 'Change the color of the selected text.',
48971             cls: 'x-html-editor-tip'
48972         },
48973         justifyleft : {
48974             title: 'Align Text Left',
48975             text: 'Align text to the left.',
48976             cls: 'x-html-editor-tip'
48977         },
48978         justifycenter : {
48979             title: 'Center Text',
48980             text: 'Center text in the editor.',
48981             cls: 'x-html-editor-tip'
48982         },
48983         justifyright : {
48984             title: 'Align Text Right',
48985             text: 'Align text to the right.',
48986             cls: 'x-html-editor-tip'
48987         },
48988         insertunorderedlist : {
48989             title: 'Bullet List',
48990             text: 'Start a bulleted list.',
48991             cls: 'x-html-editor-tip'
48992         },
48993         insertorderedlist : {
48994             title: 'Numbered List',
48995             text: 'Start a numbered list.',
48996             cls: 'x-html-editor-tip'
48997         },
48998         createlink : {
48999             title: 'Hyperlink',
49000             text: 'Make the selected text a hyperlink.',
49001             cls: 'x-html-editor-tip'
49002         },
49003         sourceedit : {
49004             title: 'Source Edit',
49005             text: 'Switch to source editing mode.',
49006             cls: 'x-html-editor-tip'
49007         }
49008     },
49009     // private
49010     onDestroy : function(){
49011         if(this.rendered){
49012             
49013             this.tb.items.each(function(item){
49014                 if(item.menu){
49015                     item.menu.removeAll();
49016                     if(item.menu.el){
49017                         item.menu.el.destroy();
49018                     }
49019                 }
49020                 item.destroy();
49021             });
49022              
49023         }
49024     },
49025     onFirstFocus: function() {
49026         this.tb.items.each(function(item){
49027            item.enable();
49028         });
49029     }
49030 });
49031
49032
49033
49034
49035 // <script type="text/javascript">
49036 /*
49037  * Based on
49038  * Ext JS Library 1.1.1
49039  * Copyright(c) 2006-2007, Ext JS, LLC.
49040  *  
49041  
49042  */
49043
49044  
49045 /**
49046  * @class Roo.form.HtmlEditor.ToolbarContext
49047  * Context Toolbar
49048  * 
49049  * Usage:
49050  *
49051  new Roo.form.HtmlEditor({
49052     ....
49053     toolbars : [
49054         { xtype: 'ToolbarStandard', styles : {} }
49055         { xtype: 'ToolbarContext', disable : {} }
49056     ]
49057 })
49058
49059      
49060  * 
49061  * @config : {Object} disable List of elements to disable.. (not done yet.)
49062  * @config : {Object} styles  Map of styles available.
49063  * 
49064  */
49065
49066 Roo.form.HtmlEditor.ToolbarContext = function(config)
49067 {
49068     
49069     Roo.apply(this, config);
49070     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
49071     // dont call parent... till later.
49072     this.styles = this.styles || {};
49073 }
49074
49075  
49076
49077 Roo.form.HtmlEditor.ToolbarContext.types = {
49078     'IMG' : {
49079         width : {
49080             title: "Width",
49081             width: 40
49082         },
49083         height:  {
49084             title: "Height",
49085             width: 40
49086         },
49087         align: {
49088             title: "Align",
49089             opts : [ [""],[ "left"],[ "right"],[ "center"],[ "top"]],
49090             width : 80
49091             
49092         },
49093         border: {
49094             title: "Border",
49095             width: 40
49096         },
49097         alt: {
49098             title: "Alt",
49099             width: 120
49100         },
49101         src : {
49102             title: "Src",
49103             width: 220
49104         }
49105         
49106     },
49107     
49108     'FIGURE' : {
49109          align: {
49110             title: "Align",
49111             opts : [ [""],[ "left"],[ "right"],[ "center"],[ "top"]],
49112             width : 80  
49113         }
49114     },
49115     'A' : {
49116         name : {
49117             title: "Name",
49118             width: 50
49119         },
49120         target:  {
49121             title: "Target",
49122             width: 120
49123         },
49124         href:  {
49125             title: "Href",
49126             width: 220
49127         } // border?
49128         
49129     },
49130     /*
49131     'TABLE' : {
49132         rows : {
49133             title: "Rows",
49134             width: 20
49135         },
49136         cols : {
49137             title: "Cols",
49138             width: 20
49139         },
49140         width : {
49141             title: "Width",
49142             width: 40
49143         },
49144         height : {
49145             title: "Height",
49146             width: 40
49147         },
49148         border : {
49149             title: "Border",
49150             width: 20
49151         }
49152     },
49153     'TD' : {
49154         width : {
49155             title: "Width",
49156             width: 40
49157         },
49158         height : {
49159             title: "Height",
49160             width: 40
49161         },   
49162         align: {
49163             title: "Align",
49164             opts : [[""],[ "left"],[ "center"],[ "right"],[ "justify"],[ "char"]],
49165             width: 80
49166         },
49167         valign: {
49168             title: "Valign",
49169             opts : [[""],[ "top"],[ "middle"],[ "bottom"],[ "baseline"]],
49170             width: 80
49171         },
49172         colspan: {
49173             title: "Colspan",
49174             width: 20
49175             
49176         },
49177          'font-family'  : {
49178             title : "Font",
49179             style : 'fontFamily',
49180             displayField: 'display',
49181             optname : 'font-family',
49182             width: 140
49183         }
49184     },
49185     */
49186     'INPUT' : {
49187         name : {
49188             title: "name",
49189             width: 120
49190         },
49191         value : {
49192             title: "Value",
49193             width: 120
49194         },
49195         width : {
49196             title: "Width",
49197             width: 40
49198         }
49199     },
49200     'LABEL' : {
49201         'for' : {
49202             title: "For",
49203             width: 120
49204         }
49205     },
49206     'TEXTAREA' : {
49207         name : {
49208             title: "name",
49209             width: 120
49210         },
49211         rows : {
49212             title: "Rows",
49213             width: 20
49214         },
49215         cols : {
49216             title: "Cols",
49217             width: 20
49218         }
49219     },
49220     'SELECT' : {
49221         name : {
49222             title: "name",
49223             width: 120
49224         },
49225         selectoptions : {
49226             title: "Options",
49227             width: 200
49228         }
49229     },
49230     
49231     // should we really allow this??
49232     // should this just be 
49233     'BODY' : {
49234         title : {
49235             title: "Title",
49236             width: 200,
49237             disabled : true
49238         }
49239     },
49240     /*
49241     'SPAN' : {
49242         'font-family'  : {
49243             title : "Font",
49244             style : 'fontFamily',
49245             displayField: 'display',
49246             optname : 'font-family',
49247             width: 140
49248         }
49249     },
49250     'DIV' : {
49251         'font-family'  : {
49252             title : "Font",
49253             style : 'fontFamily',
49254             displayField: 'display',
49255             optname : 'font-family',
49256             width: 140
49257         }
49258     },
49259      'P' : {
49260         'font-family'  : {
49261             title : "Font",
49262             style : 'fontFamily',
49263             displayField: 'display',
49264             optname : 'font-family',
49265             width: 140
49266         }
49267     },
49268     */
49269     '*' : {
49270         // empty..
49271     }
49272
49273 };
49274
49275 // this should be configurable.. - you can either set it up using stores, or modify options somehwere..
49276 Roo.form.HtmlEditor.ToolbarContext.stores = false;
49277
49278 Roo.form.HtmlEditor.ToolbarContext.options = {
49279         'font-family'  : [ 
49280                 [ 'Helvetica,Arial,sans-serif', 'Helvetica'],
49281                 [ 'Courier New', 'Courier New'],
49282                 [ 'Tahoma', 'Tahoma'],
49283                 [ 'Times New Roman,serif', 'Times'],
49284                 [ 'Verdana','Verdana' ]
49285         ]
49286 };
49287
49288 // fixme - these need to be configurable..
49289  
49290
49291 //Roo.form.HtmlEditor.ToolbarContext.types
49292
49293
49294 Roo.apply(Roo.form.HtmlEditor.ToolbarContext.prototype,  {
49295     
49296     tb: false,
49297     
49298     rendered: false,
49299     
49300     editor : false,
49301     editorcore : false,
49302     /**
49303      * @cfg {Object} disable  List of toolbar elements to disable
49304          
49305      */
49306     disable : false,
49307     /**
49308      * @cfg {Object} styles List of styles 
49309      *    eg. { '*' : [ 'headline' ] , 'TD' : [ 'underline', 'double-underline' ] } 
49310      *
49311      * These must be defined in the page, so they get rendered correctly..
49312      * .headline { }
49313      * TD.underline { }
49314      * 
49315      */
49316     styles : false,
49317     
49318     options: false,
49319     
49320     toolbars : false,
49321     
49322     init : function(editor)
49323     {
49324         this.editor = editor;
49325         this.editorcore = editor.editorcore ? editor.editorcore : editor;
49326         var editorcore = this.editorcore;
49327         
49328         var fid = editorcore.frameId;
49329         var etb = this;
49330         function btn(id, toggle, handler){
49331             var xid = fid + '-'+ id ;
49332             return {
49333                 id : xid,
49334                 cmd : id,
49335                 cls : 'x-btn-icon x-edit-'+id,
49336                 enableToggle:toggle !== false,
49337                 scope: editorcore, // was editor...
49338                 handler:handler||editorcore.relayBtnCmd,
49339                 clickEvent:'mousedown',
49340                 tooltip: etb.buttonTips[id] || undefined, ///tips ???
49341                 tabIndex:-1
49342             };
49343         }
49344         // create a new element.
49345         var wdiv = editor.wrap.createChild({
49346                 tag: 'div'
49347             }, editor.wrap.dom.firstChild.nextSibling, true);
49348         
49349         // can we do this more than once??
49350         
49351          // stop form submits
49352       
49353  
49354         // disable everything...
49355         var ty= Roo.form.HtmlEditor.ToolbarContext.types;
49356         this.toolbars = {};
49357            
49358         for (var i in  ty) {
49359           
49360             this.toolbars[i] = this.buildToolbar(ty[i],i);
49361         }
49362         this.tb = this.toolbars.BODY;
49363         this.tb.el.show();
49364         this.buildFooter();
49365         this.footer.show();
49366         editor.on('hide', function( ) { this.footer.hide() }, this);
49367         editor.on('show', function( ) { this.footer.show() }, this);
49368         
49369          
49370         this.rendered = true;
49371         
49372         // the all the btns;
49373         editor.on('editorevent', this.updateToolbar, this);
49374         // other toolbars need to implement this..
49375         //editor.on('editmodechange', this.updateToolbar, this);
49376     },
49377     
49378     
49379     
49380     /**
49381      * Protected method that will not generally be called directly. It triggers
49382      * a toolbar update by reading the markup state of the current selection in the editor.
49383      *
49384      * Note you can force an update by calling on('editorevent', scope, false)
49385      */
49386     updateToolbar: function(editor ,ev, sel){
49387
49388         //Roo.log(ev);
49389         // capture mouse up - this is handy for selecting images..
49390         // perhaps should go somewhere else...
49391         if(!this.editorcore.activated){
49392              this.editor.onFirstFocus();
49393             return;
49394         }
49395         
49396         
49397         
49398         // http://developer.yahoo.com/yui/docs/simple-editor.js.html
49399         // selectNode - might want to handle IE?
49400         
49401         
49402         
49403         if (ev &&
49404             (ev.type == 'mouseup' || ev.type == 'click' ) &&
49405             ev.target && ev.target.tagName == 'IMG') {
49406             // they have click on an image...
49407             // let's see if we can change the selection...
49408             sel = ev.target;
49409             
49410             
49411             this.editorcore.selectNode(sel);
49412              
49413         }  
49414         
49415       
49416         //var updateFooter = sel ? false : true; 
49417         
49418         
49419         var ans = this.editorcore.getAllAncestors();
49420         
49421         // pick
49422         var ty = Roo.form.HtmlEditor.ToolbarContext.types;
49423         
49424         if (!sel) { 
49425             sel = ans.length ? (ans[0] ?  ans[0]  : ans[1]) : this.editorcore.doc.body;
49426             sel = sel ? sel : this.editorcore.doc.body;
49427             sel = sel.tagName.length ? sel : this.editorcore.doc.body;
49428             
49429         }
49430         
49431         var tn = sel.tagName.toUpperCase();
49432         var lastSel = this.tb.selectedNode;
49433         this.tb.selectedNode = sel;
49434         var left_label = tn;
49435         
49436         // ok see if we are editing a block?
49437         
49438         var db = Roo.get(sel).findParent('[data-block]');
49439         var cepar = Roo.get(sel).findParent('[contenteditable=true]');
49440         if (db && cepar && cepar.tagName != 'BODY') {
49441             db = false; // we are inside an editable block.. = not sure how we are going to handle nested blocks!?
49442         }
49443         var block = false;
49444         if (db && !sel.hasAttribute('contenteditable') && sel.getAttribute('contenteditable') != 'true' ) {
49445             block = Roo.htmleditor.Block.factory(db);
49446             if (block) {
49447                 tn = 'BLOCK.' + db.getAttribute('data-block');
49448                 this.tb.selectedNode = db;
49449                 this.editorcore.selectNode(db);
49450                 if (typeof(this.toolbars[tn]) == 'undefined') {
49451                    this.toolbars[tn] = this.buildToolbar( block.context || block.contextMenu(this) ,tn ,block.friendly_name);
49452                 }
49453                 left_label = block.friendly_name;
49454                 ans = this.editorcore.getAllAncestors();
49455             }
49456             
49457                 
49458             
49459         }
49460         
49461         
49462         if (this.tb.name == tn && lastSel == this.tb.selectedNode && ev !== false) {
49463             return; // no change?
49464         }
49465         
49466         
49467           
49468         this.tb.el.hide();
49469         ///console.log("show: " + tn);
49470         this.tb =  typeof(this.toolbars[tn]) != 'undefined' ? this.toolbars[tn] : this.toolbars['*'];
49471         
49472         this.tb.el.show();
49473         // update name
49474         this.tb.items.first().el.innerHTML = left_label + ':&nbsp;';
49475         
49476         
49477         // update attributes
49478         if (block) {
49479              
49480             this.tb.fields.each(function(e) {
49481                 e.setValue(block[e.attrname]);
49482             });
49483             
49484             
49485         } else  if (this.tb.fields && this.tb.selectedNode) {
49486             this.tb.fields.each( function(e) {
49487                 if (e.stylename) {
49488                     e.setValue(this.tb.selectedNode.style[e.stylename]);
49489                     return;
49490                 } 
49491                 e.setValue(this.tb.selectedNode.getAttribute(e.attrname));
49492             }, this);
49493             this.updateToolbarStyles(this.tb.selectedNode);  
49494         }
49495         
49496         
49497        
49498         Roo.menu.MenuMgr.hideAll();
49499
49500         
49501         
49502     
49503         // update the footer
49504         //
49505         this.updateFooter(ans);
49506              
49507     },
49508     
49509     updateToolbarStyles : function(sel)
49510     {
49511         var hasStyles = false;
49512         for(var i in this.styles) {
49513             hasStyles = true;
49514             break;
49515         }
49516         
49517         // update styles
49518         if (hasStyles && this.tb.hasStyles) { 
49519             var st = this.tb.fields.item(0);
49520             
49521             st.store.removeAll();
49522             var cn = sel.className.split(/\s+/);
49523             
49524             var avs = [];
49525             if (this.styles['*']) {
49526                 
49527                 Roo.each(this.styles['*'], function(v) {
49528                     avs.push( [ v , cn.indexOf(v) > -1 ? 1 : 0 ] );         
49529                 });
49530             }
49531             if (this.styles[tn]) { 
49532                 Roo.each(this.styles[tn], function(v) {
49533                     avs.push( [ v , cn.indexOf(v) > -1 ? 1 : 0 ] );         
49534                 });
49535             }
49536             
49537             st.store.loadData(avs);
49538             st.collapse();
49539             st.setValue(cn);
49540         }
49541     },
49542     
49543      
49544     updateFooter : function(ans)
49545     {
49546         var html = '';
49547         if (ans === false) {
49548             this.footDisp.dom.innerHTML = '';
49549             return;
49550         }
49551         
49552         this.footerEls = ans.reverse();
49553         Roo.each(this.footerEls, function(a,i) {
49554             if (!a) { return; }
49555             html += html.length ? ' &gt; '  :  '';
49556             
49557             html += '<span class="x-ed-loc-' + i + '">' + a.tagName + '</span>';
49558             
49559         });
49560        
49561         // 
49562         var sz = this.footDisp.up('td').getSize();
49563         this.footDisp.dom.style.width = (sz.width -10) + 'px';
49564         this.footDisp.dom.style.marginLeft = '5px';
49565         
49566         this.footDisp.dom.style.overflow = 'hidden';
49567         
49568         this.footDisp.dom.innerHTML = html;
49569             
49570         
49571     },
49572    
49573        
49574     // private
49575     onDestroy : function(){
49576         if(this.rendered){
49577             
49578             this.tb.items.each(function(item){
49579                 if(item.menu){
49580                     item.menu.removeAll();
49581                     if(item.menu.el){
49582                         item.menu.el.destroy();
49583                     }
49584                 }
49585                 item.destroy();
49586             });
49587              
49588         }
49589     },
49590     onFirstFocus: function() {
49591         // need to do this for all the toolbars..
49592         this.tb.items.each(function(item){
49593            item.enable();
49594         });
49595     },
49596     buildToolbar: function(tlist, nm, friendly_name)
49597     {
49598         var editor = this.editor;
49599         var editorcore = this.editorcore;
49600          // create a new element.
49601         var wdiv = editor.wrap.createChild({
49602                 tag: 'div'
49603             }, editor.wrap.dom.firstChild.nextSibling, true);
49604         
49605        
49606         var tb = new Roo.Toolbar(wdiv);
49607         tb.hasStyles = false;
49608         tb.name = nm;
49609         
49610         tb.add((typeof(friendly_name) == 'undefined' ? nm : friendly_name) + ":&nbsp;");
49611         
49612         var styles = Array.from(this.styles);
49613         
49614         
49615         // styles...
49616         if (styles && styles.length) {
49617             tb.hasStyles = true;
49618             // this needs a multi-select checkbox...
49619             tb.addField( new Roo.form.ComboBox({
49620                 store: new Roo.data.SimpleStore({
49621                     id : 'val',
49622                     fields: ['val', 'selected'],
49623                     data : [] 
49624                 }),
49625                 name : '-roo-edit-className',
49626                 attrname : 'className',
49627                 displayField: 'val',
49628                 typeAhead: false,
49629                 mode: 'local',
49630                 editable : false,
49631                 triggerAction: 'all',
49632                 emptyText:'Select Style',
49633                 selectOnFocus:true,
49634                 width: 130,
49635                 listeners : {
49636                     'select': function(c, r, i) {
49637                         // initial support only for on class per el..
49638                         tb.selectedNode.className =  r ? r.get('val') : '';
49639                         editorcore.syncValue();
49640                     }
49641                 }
49642     
49643             }));
49644         }
49645         
49646         var tbc = Roo.form.HtmlEditor.ToolbarContext;
49647         
49648         
49649         for (var i in tlist) {
49650             
49651             // newer versions will use xtype cfg to create menus.
49652             if (typeof(tlist[i].xtype) != 'undefined') {
49653                 tb.addField(Roo.factory(tlist[i].xtype));
49654                 continue;
49655             }
49656             
49657             var item = tlist[i];
49658             tb.add(item.title + ":&nbsp;");
49659             
49660             
49661             //optname == used so you can configure the options available..
49662             var opts = item.opts ? item.opts : false;
49663             if (item.optname) { // use the b
49664                 opts = Roo.form.HtmlEditor.ToolbarContext.options[item.optname];
49665            
49666             }
49667             
49668             if (opts) {
49669                 // opts == pulldown..
49670                 tb.addField( new Roo.form.ComboBox({
49671                     store:   typeof(tbc.stores[i]) != 'undefined' ?  Roo.factory(tbc.stores[i],Roo.data) : new Roo.data.SimpleStore({
49672                         id : 'val',
49673                         fields: ['val', 'display'],
49674                         data : opts  
49675                     }),
49676                     name : '-roo-edit-' + i,
49677                     
49678                     attrname : i,
49679                     stylename : item.style ? item.style : false,
49680                     
49681                     displayField: item.displayField ? item.displayField : 'val',
49682                     valueField :  'val',
49683                     typeAhead: false,
49684                     mode: typeof(tbc.stores[i]) != 'undefined'  ? 'remote' : 'local',
49685                     editable : false,
49686                     triggerAction: 'all',
49687                     emptyText:'Select',
49688                     selectOnFocus:true,
49689                     width: item.width ? item.width  : 130,
49690                     listeners : {
49691                         'select': function(c, r, i) {
49692                             if (tb.selectedNode.hasAttribute('data-block')) {
49693                                 var b = Roo.htmleditor.Block.factory(tb.selectedNode);
49694                                 b[c.attrname] = r.get('val');
49695                                 b.updateElement(tb.selectedNode);
49696                                 editorcore.syncValue();
49697                                 return;
49698                             }
49699                             
49700                             if (c.stylename) {
49701                                 tb.selectedNode.style[c.stylename] =  r.get('val');
49702                                 editorcore.syncValue();
49703                                 return;
49704                             }
49705                             if (r === false) {
49706                                 tb.selectedNode.removeAttribute(c.attrname);
49707                                 editorcore.syncValue();
49708                                 return;
49709                             }
49710                             tb.selectedNode.setAttribute(c.attrname, r.get('val'));
49711                             editorcore.syncValue();
49712                         }
49713                     }
49714
49715                 }));
49716                 continue;
49717                     
49718                  
49719                 /*
49720                 tb.addField( new Roo.form.TextField({
49721                     name: i,
49722                     width: 100,
49723                     //allowBlank:false,
49724                     value: ''
49725                 }));
49726                 continue;
49727                 */
49728             }
49729             tb.addField( new Roo.form.TextField({
49730                 name: '-roo-edit-' + i,
49731                 attrname : i,
49732                 
49733                 width: item.width,
49734                 //allowBlank:true,
49735                 value: '',
49736                 listeners: {
49737                     'change' : function(f, nv, ov) {
49738                         
49739                         if (tb.selectedNode.hasAttribute('data-block')) {
49740                             var b = Roo.htmleditor.Block.factory(tb.selectedNode);
49741                             b[f.attrname] = nv;
49742                             b.updateElement(tb.selectedNode);
49743                             editorcore.syncValue();
49744                             return;
49745                         }
49746                         
49747                         tb.selectedNode.setAttribute(f.attrname, nv);
49748                         editorcore.syncValue();
49749                     }
49750                 }
49751             }));
49752              
49753         }
49754         
49755         var _this = this;
49756         
49757         if(nm == 'BODY'){
49758             tb.addSeparator();
49759         
49760             tb.addButton( {
49761                 text: 'Stylesheets',
49762
49763                 listeners : {
49764                     click : function ()
49765                     {
49766                         _this.editor.fireEvent('stylesheetsclick', _this.editor);
49767                     }
49768                 }
49769             });
49770         }
49771         
49772         tb.addFill();
49773         tb.addButton({
49774             text: 'Remove Block or Formating', // remove the tag, and puts the children outside...
49775     
49776             listeners : {
49777                 click : function ()
49778                 {
49779                     // remove
49780                     // undo does not work.
49781                     var sn = tb.selectedNode;
49782                     if (!sn) {
49783                         return;
49784                     }
49785                     var stn =  sn.childNodes[0] || sn.nextSibling || sn.previousSibling || sn.parentNode;
49786                     if (sn.hasAttribute('data-block')) {
49787                         stn =  sn.nextSibling || sn.previousSibling || sn.parentNode;
49788                         sn.parentNode.removeChild(sn);
49789                         
49790                     } else if (sn && sn.tagName != 'BODY') {
49791                         // remove and keep parents.
49792                         a = new Roo.htmleditor.FilterKeepChildren({tag : false});
49793                         a.removeTag(sn);
49794                     }
49795                     
49796                     
49797                     var range = editorcore.createRange();
49798         
49799                     range.setStart(stn,0);
49800                     range.setEnd(stn,0); 
49801                     var selection = editorcore.getSelection();
49802                     selection.removeAllRanges();
49803                     selection.addRange(range);
49804                     
49805                     
49806                     //_this.updateToolbar(null, null, pn);
49807                     _this.updateToolbar(null, null, null);
49808                     _this.updateFooter(false);
49809                     
49810                 }
49811             }
49812             
49813                     
49814                 
49815             
49816         });
49817         
49818         
49819         tb.el.on('click', function(e){
49820             e.preventDefault(); // what does this do?
49821         });
49822         tb.el.setVisibilityMode( Roo.Element.DISPLAY);
49823         tb.el.hide();
49824         
49825         // dont need to disable them... as they will get hidden
49826         return tb;
49827          
49828         
49829     },
49830     buildFooter : function()
49831     {
49832         
49833         var fel = this.editor.wrap.createChild();
49834         this.footer = new Roo.Toolbar(fel);
49835         // toolbar has scrolly on left / right?
49836         var footDisp= new Roo.Toolbar.Fill();
49837         var _t = this;
49838         this.footer.add(
49839             {
49840                 text : '&lt;',
49841                 xtype: 'Button',
49842                 handler : function() {
49843                     _t.footDisp.scrollTo('left',0,true)
49844                 }
49845             }
49846         );
49847         this.footer.add( footDisp );
49848         this.footer.add( 
49849             {
49850                 text : '&gt;',
49851                 xtype: 'Button',
49852                 handler : function() {
49853                     // no animation..
49854                     _t.footDisp.select('span').last().scrollIntoView(_t.footDisp,true);
49855                 }
49856             }
49857         );
49858         var fel = Roo.get(footDisp.el);
49859         fel.addClass('x-editor-context');
49860         this.footDispWrap = fel; 
49861         this.footDispWrap.overflow  = 'hidden';
49862         
49863         this.footDisp = fel.createChild();
49864         this.footDispWrap.on('click', this.onContextClick, this)
49865         
49866         
49867     },
49868     onContextClick : function (ev,dom)
49869     {
49870         ev.preventDefault();
49871         var  cn = dom.className;
49872         //Roo.log(cn);
49873         if (!cn.match(/x-ed-loc-/)) {
49874             return;
49875         }
49876         var n = cn.split('-').pop();
49877         var ans = this.footerEls;
49878         var sel = ans[n];
49879         
49880          // pick
49881         var range = this.editorcore.createRange();
49882         
49883         range.selectNodeContents(sel);
49884         //range.selectNode(sel);
49885         
49886         
49887         var selection = this.editorcore.getSelection();
49888         selection.removeAllRanges();
49889         selection.addRange(range);
49890         
49891         
49892         
49893         this.updateToolbar(null, null, sel);
49894         
49895         
49896     }
49897     
49898     
49899     
49900     
49901     
49902 });
49903
49904
49905
49906
49907
49908 /*
49909  * Based on:
49910  * Ext JS Library 1.1.1
49911  * Copyright(c) 2006-2007, Ext JS, LLC.
49912  *
49913  * Originally Released Under LGPL - original licence link has changed is not relivant.
49914  *
49915  * Fork - LGPL
49916  * <script type="text/javascript">
49917  */
49918  
49919 /**
49920  * @class Roo.form.BasicForm
49921  * @extends Roo.util.Observable
49922  * Supplies the functionality to do "actions" on forms and initialize Roo.form.Field types on existing markup.
49923  * @constructor
49924  * @param {String/HTMLElement/Roo.Element} el The form element or its id
49925  * @param {Object} config Configuration options
49926  */
49927 Roo.form.BasicForm = function(el, config){
49928     this.allItems = [];
49929     this.childForms = [];
49930     Roo.apply(this, config);
49931     /*
49932      * The Roo.form.Field items in this form.
49933      * @type MixedCollection
49934      */
49935      
49936      
49937     this.items = new Roo.util.MixedCollection(false, function(o){
49938         return o.id || (o.id = Roo.id());
49939     });
49940     this.addEvents({
49941         /**
49942          * @event beforeaction
49943          * Fires before any action is performed. Return false to cancel the action.
49944          * @param {Form} this
49945          * @param {Action} action The action to be performed
49946          */
49947         beforeaction: true,
49948         /**
49949          * @event actionfailed
49950          * Fires when an action fails.
49951          * @param {Form} this
49952          * @param {Action} action The action that failed
49953          */
49954         actionfailed : true,
49955         /**
49956          * @event actioncomplete
49957          * Fires when an action is completed.
49958          * @param {Form} this
49959          * @param {Action} action The action that completed
49960          */
49961         actioncomplete : true
49962     });
49963     if(el){
49964         this.initEl(el);
49965     }
49966     Roo.form.BasicForm.superclass.constructor.call(this);
49967     
49968     Roo.form.BasicForm.popover.apply();
49969 };
49970
49971 Roo.extend(Roo.form.BasicForm, Roo.util.Observable, {
49972     /**
49973      * @cfg {String} method
49974      * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
49975      */
49976     /**
49977      * @cfg {DataReader} reader
49978      * An Roo.data.DataReader (e.g. {@link Roo.data.XmlReader}) to be used to read data when executing "load" actions.
49979      * This is optional as there is built-in support for processing JSON.
49980      */
49981     /**
49982      * @cfg {DataReader} errorReader
49983      * An Roo.data.DataReader (e.g. {@link Roo.data.XmlReader}) to be used to read data when reading validation errors on "submit" actions.
49984      * This is completely optional as there is built-in support for processing JSON.
49985      */
49986     /**
49987      * @cfg {String} url
49988      * The URL to use for form actions if one isn't supplied in the action options.
49989      */
49990     /**
49991      * @cfg {Boolean} fileUpload
49992      * Set to true if this form is a file upload.
49993      */
49994      
49995     /**
49996      * @cfg {Object} baseParams
49997      * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
49998      */
49999      /**
50000      
50001     /**
50002      * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
50003      */
50004     timeout: 30,
50005
50006     // private
50007     activeAction : null,
50008
50009     /**
50010      * @cfg {Boolean} trackResetOnLoad If set to true, form.reset() resets to the last loaded
50011      * or setValues() data instead of when the form was first created.
50012      */
50013     trackResetOnLoad : false,
50014     
50015     
50016     /**
50017      * childForms - used for multi-tab forms
50018      * @type {Array}
50019      */
50020     childForms : false,
50021     
50022     /**
50023      * allItems - full list of fields.
50024      * @type {Array}
50025      */
50026     allItems : false,
50027     
50028     /**
50029      * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
50030      * element by passing it or its id or mask the form itself by passing in true.
50031      * @type Mixed
50032      */
50033     waitMsgTarget : false,
50034     
50035     /**
50036      * @type Boolean
50037      */
50038     disableMask : false,
50039     
50040     /**
50041      * @cfg {Boolean} errorMask (true|false) default false
50042      */
50043     errorMask : false,
50044     
50045     /**
50046      * @cfg {Number} maskOffset Default 100
50047      */
50048     maskOffset : 100,
50049
50050     // private
50051     initEl : function(el){
50052         this.el = Roo.get(el);
50053         this.id = this.el.id || Roo.id();
50054         this.el.on('submit', this.onSubmit, this);
50055         this.el.addClass('x-form');
50056     },
50057
50058     // private
50059     onSubmit : function(e){
50060         e.stopEvent();
50061     },
50062
50063     /**
50064      * Returns true if client-side validation on the form is successful.
50065      * @return Boolean
50066      */
50067     isValid : function(){
50068         var valid = true;
50069         var target = false;
50070         this.items.each(function(f){
50071             if(f.validate()){
50072                 return;
50073             }
50074             
50075             valid = false;
50076                 
50077             if(!target && f.el.isVisible(true)){
50078                 target = f;
50079             }
50080         });
50081         
50082         if(this.errorMask && !valid){
50083             Roo.form.BasicForm.popover.mask(this, target);
50084         }
50085         
50086         return valid;
50087     },
50088     /**
50089      * Returns array of invalid form fields.
50090      * @return Array
50091      */
50092     
50093     invalidFields : function()
50094     {
50095         var ret = [];
50096         this.items.each(function(f){
50097             if(f.validate()){
50098                 return;
50099             }
50100             ret.push(f);
50101             
50102         });
50103         
50104         return ret;
50105     },
50106     
50107     
50108     /**
50109      * DEPRICATED Returns true if any fields in this form have changed since their original load. 
50110      * @return Boolean
50111      */
50112     isDirty : function(){
50113         var dirty = false;
50114         this.items.each(function(f){
50115            if(f.isDirty()){
50116                dirty = true;
50117                return false;
50118            }
50119         });
50120         return dirty;
50121     },
50122     
50123     /**
50124      * Returns true if any fields in this form have changed since their original load. (New version)
50125      * @return Boolean
50126      */
50127     
50128     hasChanged : function()
50129     {
50130         var dirty = false;
50131         this.items.each(function(f){
50132            if(f.hasChanged()){
50133                dirty = true;
50134                return false;
50135            }
50136         });
50137         return dirty;
50138         
50139     },
50140     /**
50141      * Resets all hasChanged to 'false' -
50142      * The old 'isDirty' used 'original value..' however this breaks reset() and a few other things.
50143      * So hasChanged storage is only to be used for this purpose
50144      * @return Boolean
50145      */
50146     resetHasChanged : function()
50147     {
50148         this.items.each(function(f){
50149            f.resetHasChanged();
50150         });
50151         
50152     },
50153     
50154     
50155     /**
50156      * Performs a predefined action (submit or load) or custom actions you define on this form.
50157      * @param {String} actionName The name of the action type
50158      * @param {Object} options (optional) The options to pass to the action.  All of the config options listed
50159      * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
50160      * accept other config options):
50161      * <pre>
50162 Property          Type             Description
50163 ----------------  ---------------  ----------------------------------------------------------------------------------
50164 url               String           The url for the action (defaults to the form's url)
50165 method            String           The form method to use (defaults to the form's method, or POST if not defined)
50166 params            String/Object    The params to pass (defaults to the form's baseParams, or none if not defined)
50167 clientValidation  Boolean          Applies to submit only.  Pass true to call form.isValid() prior to posting to
50168                                    validate the form on the client (defaults to false)
50169      * </pre>
50170      * @return {BasicForm} this
50171      */
50172     doAction : function(action, options){
50173         if(typeof action == 'string'){
50174             action = new Roo.form.Action.ACTION_TYPES[action](this, options);
50175         }
50176         if(this.fireEvent('beforeaction', this, action) !== false){
50177             this.beforeAction(action);
50178             action.run.defer(100, action);
50179         }
50180         return this;
50181     },
50182
50183     /**
50184      * Shortcut to do a submit action.
50185      * @param {Object} options The options to pass to the action (see {@link #doAction} for details)
50186      * @return {BasicForm} this
50187      */
50188     submit : function(options){
50189         this.doAction('submit', options);
50190         return this;
50191     },
50192
50193     /**
50194      * Shortcut to do a load action.
50195      * @param {Object} options The options to pass to the action (see {@link #doAction} for details)
50196      * @return {BasicForm} this
50197      */
50198     load : function(options){
50199         this.doAction('load', options);
50200         return this;
50201     },
50202
50203     /**
50204      * Persists the values in this form into the passed Roo.data.Record object in a beginEdit/endEdit block.
50205      * @param {Record} record The record to edit
50206      * @return {BasicForm} this
50207      */
50208     updateRecord : function(record){
50209         record.beginEdit();
50210         var fs = record.fields;
50211         fs.each(function(f){
50212             var field = this.findField(f.name);
50213             if(field){
50214                 record.set(f.name, field.getValue());
50215             }
50216         }, this);
50217         record.endEdit();
50218         return this;
50219     },
50220
50221     /**
50222      * Loads an Roo.data.Record into this form.
50223      * @param {Record} record The record to load
50224      * @return {BasicForm} this
50225      */
50226     loadRecord : function(record){
50227         this.setValues(record.data);
50228         return this;
50229     },
50230
50231     // private
50232     beforeAction : function(action){
50233         var o = action.options;
50234         
50235         if(!this.disableMask) {
50236             if(this.waitMsgTarget === true){
50237                 this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
50238             }else if(this.waitMsgTarget){
50239                 this.waitMsgTarget = Roo.get(this.waitMsgTarget);
50240                 this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
50241             }else {
50242                 Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
50243             }
50244         }
50245         
50246          
50247     },
50248
50249     // private
50250     afterAction : function(action, success){
50251         this.activeAction = null;
50252         var o = action.options;
50253         
50254         if(!this.disableMask) {
50255             if(this.waitMsgTarget === true){
50256                 this.el.unmask();
50257             }else if(this.waitMsgTarget){
50258                 this.waitMsgTarget.unmask();
50259             }else{
50260                 Roo.MessageBox.updateProgress(1);
50261                 Roo.MessageBox.hide();
50262             }
50263         }
50264         
50265         if(success){
50266             if(o.reset){
50267                 this.reset();
50268             }
50269             Roo.callback(o.success, o.scope, [this, action]);
50270             this.fireEvent('actioncomplete', this, action);
50271             
50272         }else{
50273             
50274             // failure condition..
50275             // we have a scenario where updates need confirming.
50276             // eg. if a locking scenario exists..
50277             // we look for { errors : { needs_confirm : true }} in the response.
50278             if (
50279                 (typeof(action.result) != 'undefined')  &&
50280                 (typeof(action.result.errors) != 'undefined')  &&
50281                 (typeof(action.result.errors.needs_confirm) != 'undefined')
50282            ){
50283                 var _t = this;
50284                 Roo.MessageBox.confirm(
50285                     "Change requires confirmation",
50286                     action.result.errorMsg,
50287                     function(r) {
50288                         if (r != 'yes') {
50289                             return;
50290                         }
50291                         _t.doAction('submit', { params :  { _submit_confirmed : 1 } }  );
50292                     }
50293                     
50294                 );
50295                 
50296                 
50297                 
50298                 return;
50299             }
50300             
50301             Roo.callback(o.failure, o.scope, [this, action]);
50302             // show an error message if no failed handler is set..
50303             if (!this.hasListener('actionfailed')) {
50304                 Roo.MessageBox.alert("Error",
50305                     (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
50306                         action.result.errorMsg :
50307                         "Saving Failed, please check your entries or try again"
50308                 );
50309             }
50310             
50311             this.fireEvent('actionfailed', this, action);
50312         }
50313         
50314     },
50315
50316     /**
50317      * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
50318      * @param {String} id The value to search for
50319      * @return Field
50320      */
50321     findField : function(id){
50322         var field = this.items.get(id);
50323         if(!field){
50324             this.items.each(function(f){
50325                 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
50326                     field = f;
50327                     return false;
50328                 }
50329             });
50330         }
50331         return field || null;
50332     },
50333
50334     /**
50335      * Add a secondary form to this one, 
50336      * Used to provide tabbed forms. One form is primary, with hidden values 
50337      * which mirror the elements from the other forms.
50338      * 
50339      * @param {Roo.form.Form} form to add.
50340      * 
50341      */
50342     addForm : function(form)
50343     {
50344        
50345         if (this.childForms.indexOf(form) > -1) {
50346             // already added..
50347             return;
50348         }
50349         this.childForms.push(form);
50350         var n = '';
50351         Roo.each(form.allItems, function (fe) {
50352             
50353             n = typeof(fe.getName) == 'undefined' ? fe.name : fe.getName();
50354             if (this.findField(n)) { // already added..
50355                 return;
50356             }
50357             var add = new Roo.form.Hidden({
50358                 name : n
50359             });
50360             add.render(this.el);
50361             
50362             this.add( add );
50363         }, this);
50364         
50365     },
50366     /**
50367      * Mark fields in this form invalid in bulk.
50368      * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
50369      * @return {BasicForm} this
50370      */
50371     markInvalid : function(errors){
50372         if(errors instanceof Array){
50373             for(var i = 0, len = errors.length; i < len; i++){
50374                 var fieldError = errors[i];
50375                 var f = this.findField(fieldError.id);
50376                 if(f){
50377                     f.markInvalid(fieldError.msg);
50378                 }
50379             }
50380         }else{
50381             var field, id;
50382             for(id in errors){
50383                 if(typeof errors[id] != 'function' && (field = this.findField(id))){
50384                     field.markInvalid(errors[id]);
50385                 }
50386             }
50387         }
50388         Roo.each(this.childForms || [], function (f) {
50389             f.markInvalid(errors);
50390         });
50391         
50392         return this;
50393     },
50394
50395     /**
50396      * Set values for fields in this form in bulk.
50397      * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
50398      * @return {BasicForm} this
50399      */
50400     setValues : function(values){
50401         if(values instanceof Array){ // array of objects
50402             for(var i = 0, len = values.length; i < len; i++){
50403                 var v = values[i];
50404                 var f = this.findField(v.id);
50405                 if(f){
50406                     f.setValue(v.value);
50407                     if(this.trackResetOnLoad){
50408                         f.originalValue = f.getValue();
50409                     }
50410                 }
50411             }
50412         }else{ // object hash
50413             var field, id;
50414             for(id in values){
50415                 if(typeof values[id] != 'function' && (field = this.findField(id))){
50416                     
50417                     if (field.setFromData && 
50418                         field.valueField && 
50419                         field.displayField &&
50420                         // combos' with local stores can 
50421                         // be queried via setValue()
50422                         // to set their value..
50423                         (field.store && !field.store.isLocal)
50424                         ) {
50425                         // it's a combo
50426                         var sd = { };
50427                         sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
50428                         sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
50429                         field.setFromData(sd);
50430                         
50431                     } else {
50432                         field.setValue(values[id]);
50433                     }
50434                     
50435                     
50436                     if(this.trackResetOnLoad){
50437                         field.originalValue = field.getValue();
50438                     }
50439                 }
50440             }
50441         }
50442         this.resetHasChanged();
50443         
50444         
50445         Roo.each(this.childForms || [], function (f) {
50446             f.setValues(values);
50447             f.resetHasChanged();
50448         });
50449                 
50450         return this;
50451     },
50452  
50453     /**
50454      * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
50455      * they are returned as an array.
50456      * @param {Boolean} asString
50457      * @return {Object}
50458      */
50459     getValues : function(asString){
50460         if (this.childForms) {
50461             // copy values from the child forms
50462             Roo.each(this.childForms, function (f) {
50463                 this.setValues(f.getValues());
50464             }, this);
50465         }
50466         
50467         // use formdata
50468         if (typeof(FormData) != 'undefined' && asString !== true) {
50469             // this relies on a 'recent' version of chrome apparently...
50470             try {
50471                 var fd = (new FormData(this.el.dom)).entries();
50472                 var ret = {};
50473                 var ent = fd.next();
50474                 while (!ent.done) {
50475                     ret[ent.value[0]] = ent.value[1]; // not sure how this will handle duplicates..
50476                     ent = fd.next();
50477                 };
50478                 return ret;
50479             } catch(e) {
50480                 
50481             }
50482             
50483         }
50484         
50485         
50486         var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
50487         if(asString === true){
50488             return fs;
50489         }
50490         return Roo.urlDecode(fs);
50491     },
50492     
50493     /**
50494      * Returns the fields in this form as an object with key/value pairs. 
50495      * This differs from getValues as it calls getValue on each child item, rather than using dom data.
50496      * @return {Object}
50497      */
50498     getFieldValues : function(with_hidden)
50499     {
50500         if (this.childForms) {
50501             // copy values from the child forms
50502             // should this call getFieldValues - probably not as we do not currently copy
50503             // hidden fields when we generate..
50504             Roo.each(this.childForms, function (f) {
50505                 this.setValues(f.getValues());
50506             }, this);
50507         }
50508         
50509         var ret = {};
50510         this.items.each(function(f){
50511             if (!f.getName()) {
50512                 return;
50513             }
50514             var v = f.getValue();
50515             if (f.inputType =='radio') {
50516                 if (typeof(ret[f.getName()]) == 'undefined') {
50517                     ret[f.getName()] = ''; // empty..
50518                 }
50519                 
50520                 if (!f.el.dom.checked) {
50521                     return;
50522                     
50523                 }
50524                 v = f.el.dom.value;
50525                 
50526             }
50527             
50528             // not sure if this supported any more..
50529             if ((typeof(v) == 'object') && f.getRawValue) {
50530                 v = f.getRawValue() ; // dates..
50531             }
50532             // combo boxes where name != hiddenName...
50533             if (f.name != f.getName()) {
50534                 ret[f.name] = f.getRawValue();
50535             }
50536             ret[f.getName()] = v;
50537         });
50538         
50539         return ret;
50540     },
50541
50542     /**
50543      * Clears all invalid messages in this form.
50544      * @return {BasicForm} this
50545      */
50546     clearInvalid : function(){
50547         this.items.each(function(f){
50548            f.clearInvalid();
50549         });
50550         
50551         Roo.each(this.childForms || [], function (f) {
50552             f.clearInvalid();
50553         });
50554         
50555         
50556         return this;
50557     },
50558
50559     /**
50560      * Resets this form.
50561      * @return {BasicForm} this
50562      */
50563     reset : function(){
50564         this.items.each(function(f){
50565             f.reset();
50566         });
50567         
50568         Roo.each(this.childForms || [], function (f) {
50569             f.reset();
50570         });
50571         this.resetHasChanged();
50572         
50573         return this;
50574     },
50575
50576     /**
50577      * Add Roo.form components to this form.
50578      * @param {Field} field1
50579      * @param {Field} field2 (optional)
50580      * @param {Field} etc (optional)
50581      * @return {BasicForm} this
50582      */
50583     add : function(){
50584         this.items.addAll(Array.prototype.slice.call(arguments, 0));
50585         return this;
50586     },
50587
50588
50589     /**
50590      * Removes a field from the items collection (does NOT remove its markup).
50591      * @param {Field} field
50592      * @return {BasicForm} this
50593      */
50594     remove : function(field){
50595         this.items.remove(field);
50596         return this;
50597     },
50598
50599     /**
50600      * Looks at the fields in this form, checks them for an id attribute,
50601      * and calls applyTo on the existing dom element with that id.
50602      * @return {BasicForm} this
50603      */
50604     render : function(){
50605         this.items.each(function(f){
50606             if(f.isFormField && !f.rendered && document.getElementById(f.id)){ // if the element exists
50607                 f.applyTo(f.id);
50608             }
50609         });
50610         return this;
50611     },
50612
50613     /**
50614      * Calls {@link Ext#apply} for all fields in this form with the passed object.
50615      * @param {Object} values
50616      * @return {BasicForm} this
50617      */
50618     applyToFields : function(o){
50619         this.items.each(function(f){
50620            Roo.apply(f, o);
50621         });
50622         return this;
50623     },
50624
50625     /**
50626      * Calls {@link Ext#applyIf} for all field in this form with the passed object.
50627      * @param {Object} values
50628      * @return {BasicForm} this
50629      */
50630     applyIfToFields : function(o){
50631         this.items.each(function(f){
50632            Roo.applyIf(f, o);
50633         });
50634         return this;
50635     }
50636 });
50637
50638 // back compat
50639 Roo.BasicForm = Roo.form.BasicForm;
50640
50641 Roo.apply(Roo.form.BasicForm, {
50642     
50643     popover : {
50644         
50645         padding : 5,
50646         
50647         isApplied : false,
50648         
50649         isMasked : false,
50650         
50651         form : false,
50652         
50653         target : false,
50654         
50655         intervalID : false,
50656         
50657         maskEl : false,
50658         
50659         apply : function()
50660         {
50661             if(this.isApplied){
50662                 return;
50663             }
50664             
50665             this.maskEl = {
50666                 top : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-top-mask" }, true),
50667                 left : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-left-mask" }, true),
50668                 bottom : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-bottom-mask" }, true),
50669                 right : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-right-mask" }, true)
50670             };
50671             
50672             this.maskEl.top.enableDisplayMode("block");
50673             this.maskEl.left.enableDisplayMode("block");
50674             this.maskEl.bottom.enableDisplayMode("block");
50675             this.maskEl.right.enableDisplayMode("block");
50676             
50677             Roo.get(document.body).on('click', function(){
50678                 this.unmask();
50679             }, this);
50680             
50681             Roo.get(document.body).on('touchstart', function(){
50682                 this.unmask();
50683             }, this);
50684             
50685             this.isApplied = true
50686         },
50687         
50688         mask : function(form, target)
50689         {
50690             this.form = form;
50691             
50692             this.target = target;
50693             
50694             if(!this.form.errorMask || !target.el){
50695                 return;
50696             }
50697             
50698             var scrollable = this.target.el.findScrollableParent() || this.target.el.findParent('div.x-layout-active-content', 100, true) || Roo.get(document.body);
50699             
50700             var ot = this.target.el.calcOffsetsTo(scrollable);
50701             
50702             var scrollTo = ot[1] - this.form.maskOffset;
50703             
50704             scrollTo = Math.min(scrollTo, scrollable.dom.scrollHeight);
50705             
50706             scrollable.scrollTo('top', scrollTo);
50707             
50708             var el = this.target.wrap || this.target.el;
50709             
50710             var box = el.getBox();
50711             
50712             this.maskEl.top.setStyle('position', 'absolute');
50713             this.maskEl.top.setStyle('z-index', 10000);
50714             this.maskEl.top.setSize(Roo.lib.Dom.getDocumentWidth(), box.y - this.padding);
50715             this.maskEl.top.setLeft(0);
50716             this.maskEl.top.setTop(0);
50717             this.maskEl.top.show();
50718             
50719             this.maskEl.left.setStyle('position', 'absolute');
50720             this.maskEl.left.setStyle('z-index', 10000);
50721             this.maskEl.left.setSize(box.x - this.padding, box.height + this.padding * 2);
50722             this.maskEl.left.setLeft(0);
50723             this.maskEl.left.setTop(box.y - this.padding);
50724             this.maskEl.left.show();
50725
50726             this.maskEl.bottom.setStyle('position', 'absolute');
50727             this.maskEl.bottom.setStyle('z-index', 10000);
50728             this.maskEl.bottom.setSize(Roo.lib.Dom.getDocumentWidth(), Roo.lib.Dom.getDocumentHeight() - box.bottom - this.padding);
50729             this.maskEl.bottom.setLeft(0);
50730             this.maskEl.bottom.setTop(box.bottom + this.padding);
50731             this.maskEl.bottom.show();
50732
50733             this.maskEl.right.setStyle('position', 'absolute');
50734             this.maskEl.right.setStyle('z-index', 10000);
50735             this.maskEl.right.setSize(Roo.lib.Dom.getDocumentWidth() - box.right - this.padding, box.height + this.padding * 2);
50736             this.maskEl.right.setLeft(box.right + this.padding);
50737             this.maskEl.right.setTop(box.y - this.padding);
50738             this.maskEl.right.show();
50739
50740             this.intervalID = window.setInterval(function() {
50741                 Roo.form.BasicForm.popover.unmask();
50742             }, 10000);
50743
50744             window.onwheel = function(){ return false;};
50745             
50746             (function(){ this.isMasked = true; }).defer(500, this);
50747             
50748         },
50749         
50750         unmask : function()
50751         {
50752             if(!this.isApplied || !this.isMasked || !this.form || !this.target || !this.form.errorMask){
50753                 return;
50754             }
50755             
50756             this.maskEl.top.setStyle('position', 'absolute');
50757             this.maskEl.top.setSize(0, 0).setXY([0, 0]);
50758             this.maskEl.top.hide();
50759
50760             this.maskEl.left.setStyle('position', 'absolute');
50761             this.maskEl.left.setSize(0, 0).setXY([0, 0]);
50762             this.maskEl.left.hide();
50763
50764             this.maskEl.bottom.setStyle('position', 'absolute');
50765             this.maskEl.bottom.setSize(0, 0).setXY([0, 0]);
50766             this.maskEl.bottom.hide();
50767
50768             this.maskEl.right.setStyle('position', 'absolute');
50769             this.maskEl.right.setSize(0, 0).setXY([0, 0]);
50770             this.maskEl.right.hide();
50771             
50772             window.onwheel = function(){ return true;};
50773             
50774             if(this.intervalID){
50775                 window.clearInterval(this.intervalID);
50776                 this.intervalID = false;
50777             }
50778             
50779             this.isMasked = false;
50780             
50781         }
50782         
50783     }
50784     
50785 });/*
50786  * Based on:
50787  * Ext JS Library 1.1.1
50788  * Copyright(c) 2006-2007, Ext JS, LLC.
50789  *
50790  * Originally Released Under LGPL - original licence link has changed is not relivant.
50791  *
50792  * Fork - LGPL
50793  * <script type="text/javascript">
50794  */
50795
50796 /**
50797  * @class Roo.form.Form
50798  * @extends Roo.form.BasicForm
50799  * @children Roo.form.Column Roo.form.FieldSet Roo.form.Row Roo.form.Field Roo.Button Roo.form.TextItem
50800  * Adds the ability to dynamically render forms with JavaScript to {@link Roo.form.BasicForm}.
50801  * @constructor
50802  * @param {Object} config Configuration options
50803  */
50804 Roo.form.Form = function(config){
50805     var xitems =  [];
50806     if (config.items) {
50807         xitems = config.items;
50808         delete config.items;
50809     }
50810    
50811     
50812     Roo.form.Form.superclass.constructor.call(this, null, config);
50813     this.url = this.url || this.action;
50814     if(!this.root){
50815         this.root = new Roo.form.Layout(Roo.applyIf({
50816             id: Roo.id()
50817         }, config));
50818     }
50819     this.active = this.root;
50820     /**
50821      * Array of all the buttons that have been added to this form via {@link addButton}
50822      * @type Array
50823      */
50824     this.buttons = [];
50825     this.allItems = [];
50826     this.addEvents({
50827         /**
50828          * @event clientvalidation
50829          * If the monitorValid config option is true, this event fires repetitively to notify of valid state
50830          * @param {Form} this
50831          * @param {Boolean} valid true if the form has passed client-side validation
50832          */
50833         clientvalidation: true,
50834         /**
50835          * @event rendered
50836          * Fires when the form is rendered
50837          * @param {Roo.form.Form} form
50838          */
50839         rendered : true
50840     });
50841     
50842     if (this.progressUrl) {
50843             // push a hidden field onto the list of fields..
50844             this.addxtype( {
50845                     xns: Roo.form, 
50846                     xtype : 'Hidden', 
50847                     name : 'UPLOAD_IDENTIFIER' 
50848             });
50849         }
50850         
50851     
50852     Roo.each(xitems, this.addxtype, this);
50853     
50854 };
50855
50856 Roo.extend(Roo.form.Form, Roo.form.BasicForm, {
50857      /**
50858      * @cfg {Roo.Button} buttons[] buttons at bottom of form
50859      */
50860     
50861     /**
50862      * @cfg {Number} labelWidth The width of labels. This property cascades to child containers.
50863      */
50864     /**
50865      * @cfg {String} itemCls A css class to apply to the x-form-item of fields. This property cascades to child containers.
50866      */
50867     /**
50868      * @cfg {String} buttonAlign Valid values are "left," "center" and "right" (defaults to "center")
50869      */
50870     buttonAlign:'center',
50871
50872     /**
50873      * @cfg {Number} minButtonWidth Minimum width of all buttons in pixels (defaults to 75)
50874      */
50875     minButtonWidth:75,
50876
50877     /**
50878      * @cfg {String} labelAlign Valid values are "left," "top" and "right" (defaults to "left").
50879      * This property cascades to child containers if not set.
50880      */
50881     labelAlign:'left',
50882
50883     /**
50884      * @cfg {Boolean} monitorValid If true the form monitors its valid state <b>client-side</b> and
50885      * fires a looping event with that state. This is required to bind buttons to the valid
50886      * state using the config value formBind:true on the button.
50887      */
50888     monitorValid : false,
50889
50890     /**
50891      * @cfg {Number} monitorPoll The milliseconds to poll valid state, ignored if monitorValid is not true (defaults to 200)
50892      */
50893     monitorPoll : 200,
50894     
50895     /**
50896      * @cfg {String} progressUrl - Url to return progress data 
50897      */
50898     
50899     progressUrl : false,
50900     /**
50901      * @cfg {boolean|FormData} formData - true to use new 'FormData' post, or set to a new FormData({dom form}) Object, if
50902      * sending a formdata with extra parameters - eg uploaded elements.
50903      */
50904     
50905     formData : false,
50906     
50907     /**
50908      * Opens a new {@link Roo.form.Column} container in the layout stack. If fields are passed after the config, the
50909      * fields are added and the column is closed. If no fields are passed the column remains open
50910      * until end() is called.
50911      * @param {Object} config The config to pass to the column
50912      * @param {Field} field1 (optional)
50913      * @param {Field} field2 (optional)
50914      * @param {Field} etc (optional)
50915      * @return Column The column container object
50916      */
50917     column : function(c){
50918         var col = new Roo.form.Column(c);
50919         this.start(col);
50920         if(arguments.length > 1){ // duplicate code required because of Opera
50921             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
50922             this.end();
50923         }
50924         return col;
50925     },
50926
50927     /**
50928      * Opens a new {@link Roo.form.FieldSet} container in the layout stack. If fields are passed after the config, the
50929      * fields are added and the fieldset is closed. If no fields are passed the fieldset remains open
50930      * until end() is called.
50931      * @param {Object} config The config to pass to the fieldset
50932      * @param {Field} field1 (optional)
50933      * @param {Field} field2 (optional)
50934      * @param {Field} etc (optional)
50935      * @return FieldSet The fieldset container object
50936      */
50937     fieldset : function(c){
50938         var fs = new Roo.form.FieldSet(c);
50939         this.start(fs);
50940         if(arguments.length > 1){ // duplicate code required because of Opera
50941             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
50942             this.end();
50943         }
50944         return fs;
50945     },
50946
50947     /**
50948      * Opens a new {@link Roo.form.Layout} container in the layout stack. If fields are passed after the config, the
50949      * fields are added and the container is closed. If no fields are passed the container remains open
50950      * until end() is called.
50951      * @param {Object} config The config to pass to the Layout
50952      * @param {Field} field1 (optional)
50953      * @param {Field} field2 (optional)
50954      * @param {Field} etc (optional)
50955      * @return Layout The container object
50956      */
50957     container : function(c){
50958         var l = new Roo.form.Layout(c);
50959         this.start(l);
50960         if(arguments.length > 1){ // duplicate code required because of Opera
50961             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
50962             this.end();
50963         }
50964         return l;
50965     },
50966
50967     /**
50968      * Opens the passed container in the layout stack. The container can be any {@link Roo.form.Layout} or subclass.
50969      * @param {Object} container A Roo.form.Layout or subclass of Layout
50970      * @return {Form} this
50971      */
50972     start : function(c){
50973         // cascade label info
50974         Roo.applyIf(c, {'labelAlign': this.active.labelAlign, 'labelWidth': this.active.labelWidth, 'itemCls': this.active.itemCls});
50975         this.active.stack.push(c);
50976         c.ownerCt = this.active;
50977         this.active = c;
50978         return this;
50979     },
50980
50981     /**
50982      * Closes the current open container
50983      * @return {Form} this
50984      */
50985     end : function(){
50986         if(this.active == this.root){
50987             return this;
50988         }
50989         this.active = this.active.ownerCt;
50990         return this;
50991     },
50992
50993     /**
50994      * Add Roo.form components to the current open container (e.g. column, fieldset, etc.).  Fields added via this method
50995      * can also be passed with an additional property of fieldLabel, which if supplied, will provide the text to display
50996      * as the label of the field.
50997      * @param {Field} field1
50998      * @param {Field} field2 (optional)
50999      * @param {Field} etc. (optional)
51000      * @return {Form} this
51001      */
51002     add : function(){
51003         this.active.stack.push.apply(this.active.stack, arguments);
51004         this.allItems.push.apply(this.allItems,arguments);
51005         var r = [];
51006         for(var i = 0, a = arguments, len = a.length; i < len; i++) {
51007             if(a[i].isFormField){
51008                 r.push(a[i]);
51009             }
51010         }
51011         if(r.length > 0){
51012             Roo.form.Form.superclass.add.apply(this, r);
51013         }
51014         return this;
51015     },
51016     
51017
51018     
51019     
51020     
51021      /**
51022      * Find any element that has been added to a form, using it's ID or name
51023      * This can include framesets, columns etc. along with regular fields..
51024      * @param {String} id - id or name to find.
51025      
51026      * @return {Element} e - or false if nothing found.
51027      */
51028     findbyId : function(id)
51029     {
51030         var ret = false;
51031         if (!id) {
51032             return ret;
51033         }
51034         Roo.each(this.allItems, function(f){
51035             if (f.id == id || f.name == id ){
51036                 ret = f;
51037                 return false;
51038             }
51039         });
51040         return ret;
51041     },
51042
51043     
51044     
51045     /**
51046      * Render this form into the passed container. This should only be called once!
51047      * @param {String/HTMLElement/Element} container The element this component should be rendered into
51048      * @return {Form} this
51049      */
51050     render : function(ct)
51051     {
51052         
51053         
51054         
51055         ct = Roo.get(ct);
51056         var o = this.autoCreate || {
51057             tag: 'form',
51058             method : this.method || 'POST',
51059             id : this.id || Roo.id()
51060         };
51061         this.initEl(ct.createChild(o));
51062
51063         this.root.render(this.el);
51064         
51065        
51066              
51067         this.items.each(function(f){
51068             f.render('x-form-el-'+f.id);
51069         });
51070
51071         if(this.buttons.length > 0){
51072             // tables are required to maintain order and for correct IE layout
51073             var tb = this.el.createChild({cls:'x-form-btns-ct', cn: {
51074                 cls:"x-form-btns x-form-btns-"+this.buttonAlign,
51075                 html:'<table cellspacing="0"><tbody><tr></tr></tbody></table><div class="x-clear"></div>'
51076             }}, null, true);
51077             var tr = tb.getElementsByTagName('tr')[0];
51078             for(var i = 0, len = this.buttons.length; i < len; i++) {
51079                 var b = this.buttons[i];
51080                 var td = document.createElement('td');
51081                 td.className = 'x-form-btn-td';
51082                 b.render(tr.appendChild(td));
51083             }
51084         }
51085         if(this.monitorValid){ // initialize after render
51086             this.startMonitoring();
51087         }
51088         this.fireEvent('rendered', this);
51089         return this;
51090     },
51091
51092     /**
51093      * Adds a button to the footer of the form - this <b>must</b> be called before the form is rendered.
51094      * @param {String/Object} config A string becomes the button text, an object can either be a Button config
51095      * object or a valid Roo.DomHelper element config
51096      * @param {Function} handler The function called when the button is clicked
51097      * @param {Object} scope (optional) The scope of the handler function
51098      * @return {Roo.Button}
51099      */
51100     addButton : function(config, handler, scope){
51101         var bc = {
51102             handler: handler,
51103             scope: scope,
51104             minWidth: this.minButtonWidth,
51105             hideParent:true
51106         };
51107         if(typeof config == "string"){
51108             bc.text = config;
51109         }else{
51110             Roo.apply(bc, config);
51111         }
51112         var btn = new Roo.Button(null, bc);
51113         this.buttons.push(btn);
51114         return btn;
51115     },
51116
51117      /**
51118      * Adds a series of form elements (using the xtype property as the factory method.
51119      * Valid xtypes are:  TextField, TextArea .... Button, Layout, FieldSet, Column, (and 'end' to close a block)
51120      * @param {Object} config 
51121      */
51122     
51123     addxtype : function()
51124     {
51125         var ar = Array.prototype.slice.call(arguments, 0);
51126         var ret = false;
51127         for(var i = 0; i < ar.length; i++) {
51128             if (!ar[i]) {
51129                 continue; // skip -- if this happends something invalid got sent, we 
51130                 // should ignore it, as basically that interface element will not show up
51131                 // and that should be pretty obvious!!
51132             }
51133             
51134             if (Roo.form[ar[i].xtype]) {
51135                 ar[i].form = this;
51136                 var fe = Roo.factory(ar[i], Roo.form);
51137                 if (!ret) {
51138                     ret = fe;
51139                 }
51140                 fe.form = this;
51141                 if (fe.store) {
51142                     fe.store.form = this;
51143                 }
51144                 if (fe.isLayout) {  
51145                          
51146                     this.start(fe);
51147                     this.allItems.push(fe);
51148                     if (fe.items && fe.addxtype) {
51149                         fe.addxtype.apply(fe, fe.items);
51150                         delete fe.items;
51151                     }
51152                      this.end();
51153                     continue;
51154                 }
51155                 
51156                 
51157                  
51158                 this.add(fe);
51159               //  console.log('adding ' + ar[i].xtype);
51160             }
51161             if (ar[i].xtype == 'Button') {  
51162                 //console.log('adding button');
51163                 //console.log(ar[i]);
51164                 this.addButton(ar[i]);
51165                 this.allItems.push(fe);
51166                 continue;
51167             }
51168             
51169             if (ar[i].xtype == 'end') { // so we can add fieldsets... / layout etc.
51170                 alert('end is not supported on xtype any more, use items');
51171             //    this.end();
51172             //    //console.log('adding end');
51173             }
51174             
51175         }
51176         return ret;
51177     },
51178     
51179     /**
51180      * Starts monitoring of the valid state of this form. Usually this is done by passing the config
51181      * option "monitorValid"
51182      */
51183     startMonitoring : function(){
51184         if(!this.bound){
51185             this.bound = true;
51186             Roo.TaskMgr.start({
51187                 run : this.bindHandler,
51188                 interval : this.monitorPoll || 200,
51189                 scope: this
51190             });
51191         }
51192     },
51193
51194     /**
51195      * Stops monitoring of the valid state of this form
51196      */
51197     stopMonitoring : function(){
51198         this.bound = false;
51199     },
51200
51201     // private
51202     bindHandler : function(){
51203         if(!this.bound){
51204             return false; // stops binding
51205         }
51206         var valid = true;
51207         this.items.each(function(f){
51208             if(!f.isValid(true)){
51209                 valid = false;
51210                 return false;
51211             }
51212         });
51213         for(var i = 0, len = this.buttons.length; i < len; i++){
51214             var btn = this.buttons[i];
51215             if(btn.formBind === true && btn.disabled === valid){
51216                 btn.setDisabled(!valid);
51217             }
51218         }
51219         this.fireEvent('clientvalidation', this, valid);
51220     }
51221     
51222     
51223     
51224     
51225     
51226     
51227     
51228     
51229 });
51230
51231
51232 // back compat
51233 Roo.Form = Roo.form.Form;
51234 /*
51235  * Based on:
51236  * Ext JS Library 1.1.1
51237  * Copyright(c) 2006-2007, Ext JS, LLC.
51238  *
51239  * Originally Released Under LGPL - original licence link has changed is not relivant.
51240  *
51241  * Fork - LGPL
51242  * <script type="text/javascript">
51243  */
51244
51245 // as we use this in bootstrap.
51246 Roo.namespace('Roo.form');
51247  /**
51248  * @class Roo.form.Action
51249  * Internal Class used to handle form actions
51250  * @constructor
51251  * @param {Roo.form.BasicForm} el The form element or its id
51252  * @param {Object} config Configuration options
51253  */
51254
51255  
51256  
51257 // define the action interface
51258 Roo.form.Action = function(form, options){
51259     this.form = form;
51260     this.options = options || {};
51261 };
51262 /**
51263  * Client Validation Failed
51264  * @const 
51265  */
51266 Roo.form.Action.CLIENT_INVALID = 'client';
51267 /**
51268  * Server Validation Failed
51269  * @const 
51270  */
51271 Roo.form.Action.SERVER_INVALID = 'server';
51272  /**
51273  * Connect to Server Failed
51274  * @const 
51275  */
51276 Roo.form.Action.CONNECT_FAILURE = 'connect';
51277 /**
51278  * Reading Data from Server Failed
51279  * @const 
51280  */
51281 Roo.form.Action.LOAD_FAILURE = 'load';
51282
51283 Roo.form.Action.prototype = {
51284     type : 'default',
51285     failureType : undefined,
51286     response : undefined,
51287     result : undefined,
51288
51289     // interface method
51290     run : function(options){
51291
51292     },
51293
51294     // interface method
51295     success : function(response){
51296
51297     },
51298
51299     // interface method
51300     handleResponse : function(response){
51301
51302     },
51303
51304     // default connection failure
51305     failure : function(response){
51306         
51307         this.response = response;
51308         this.failureType = Roo.form.Action.CONNECT_FAILURE;
51309         this.form.afterAction(this, false);
51310     },
51311
51312     processResponse : function(response){
51313         this.response = response;
51314         if(!response.responseText){
51315             return true;
51316         }
51317         this.result = this.handleResponse(response);
51318         return this.result;
51319     },
51320
51321     // utility functions used internally
51322     getUrl : function(appendParams){
51323         var url = this.options.url || this.form.url || this.form.el.dom.action;
51324         if(appendParams){
51325             var p = this.getParams();
51326             if(p){
51327                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
51328             }
51329         }
51330         return url;
51331     },
51332
51333     getMethod : function(){
51334         return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
51335     },
51336
51337     getParams : function(){
51338         var bp = this.form.baseParams;
51339         var p = this.options.params;
51340         if(p){
51341             if(typeof p == "object"){
51342                 p = Roo.urlEncode(Roo.applyIf(p, bp));
51343             }else if(typeof p == 'string' && bp){
51344                 p += '&' + Roo.urlEncode(bp);
51345             }
51346         }else if(bp){
51347             p = Roo.urlEncode(bp);
51348         }
51349         return p;
51350     },
51351
51352     createCallback : function(){
51353         return {
51354             success: this.success,
51355             failure: this.failure,
51356             scope: this,
51357             timeout: (this.form.timeout*1000),
51358             upload: this.form.fileUpload ? this.success : undefined
51359         };
51360     }
51361 };
51362
51363 Roo.form.Action.Submit = function(form, options){
51364     Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
51365 };
51366
51367 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
51368     type : 'submit',
51369
51370     haveProgress : false,
51371     uploadComplete : false,
51372     
51373     // uploadProgress indicator.
51374     uploadProgress : function()
51375     {
51376         if (!this.form.progressUrl) {
51377             return;
51378         }
51379         
51380         if (!this.haveProgress) {
51381             Roo.MessageBox.progress("Uploading", "Uploading");
51382         }
51383         if (this.uploadComplete) {
51384            Roo.MessageBox.hide();
51385            return;
51386         }
51387         
51388         this.haveProgress = true;
51389    
51390         var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
51391         
51392         var c = new Roo.data.Connection();
51393         c.request({
51394             url : this.form.progressUrl,
51395             params: {
51396                 id : uid
51397             },
51398             method: 'GET',
51399             success : function(req){
51400                //console.log(data);
51401                 var rdata = false;
51402                 var edata;
51403                 try  {
51404                    rdata = Roo.decode(req.responseText)
51405                 } catch (e) {
51406                     Roo.log("Invalid data from server..");
51407                     Roo.log(edata);
51408                     return;
51409                 }
51410                 if (!rdata || !rdata.success) {
51411                     Roo.log(rdata);
51412                     Roo.MessageBox.alert(Roo.encode(rdata));
51413                     return;
51414                 }
51415                 var data = rdata.data;
51416                 
51417                 if (this.uploadComplete) {
51418                    Roo.MessageBox.hide();
51419                    return;
51420                 }
51421                    
51422                 if (data){
51423                     Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
51424                        Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
51425                     );
51426                 }
51427                 this.uploadProgress.defer(2000,this);
51428             },
51429        
51430             failure: function(data) {
51431                 Roo.log('progress url failed ');
51432                 Roo.log(data);
51433             },
51434             scope : this
51435         });
51436            
51437     },
51438     
51439     
51440     run : function()
51441     {
51442         // run get Values on the form, so it syncs any secondary forms.
51443         this.form.getValues();
51444         
51445         var o = this.options;
51446         var method = this.getMethod();
51447         var isPost = method == 'POST';
51448         if(o.clientValidation === false || this.form.isValid()){
51449             
51450             if (this.form.progressUrl) {
51451                 this.form.findField('UPLOAD_IDENTIFIER').setValue(
51452                     (new Date() * 1) + '' + Math.random());
51453                     
51454             } 
51455             
51456             
51457             Roo.Ajax.request(Roo.apply(this.createCallback(), {
51458                 form:this.form.el.dom,
51459                 url:this.getUrl(!isPost),
51460                 method: method,
51461                 params:isPost ? this.getParams() : null,
51462                 isUpload: this.form.fileUpload,
51463                 formData : this.form.formData
51464             }));
51465             
51466             this.uploadProgress();
51467
51468         }else if (o.clientValidation !== false){ // client validation failed
51469             this.failureType = Roo.form.Action.CLIENT_INVALID;
51470             this.form.afterAction(this, false);
51471         }
51472     },
51473
51474     success : function(response)
51475     {
51476         this.uploadComplete= true;
51477         if (this.haveProgress) {
51478             Roo.MessageBox.hide();
51479         }
51480         
51481         
51482         var result = this.processResponse(response);
51483         if(result === true || result.success){
51484             this.form.afterAction(this, true);
51485             return;
51486         }
51487         if(result.errors){
51488             this.form.markInvalid(result.errors);
51489             this.failureType = Roo.form.Action.SERVER_INVALID;
51490         }
51491         this.form.afterAction(this, false);
51492     },
51493     failure : function(response)
51494     {
51495         this.uploadComplete= true;
51496         if (this.haveProgress) {
51497             Roo.MessageBox.hide();
51498         }
51499         
51500         this.response = response;
51501         this.failureType = Roo.form.Action.CONNECT_FAILURE;
51502         this.form.afterAction(this, false);
51503     },
51504     
51505     handleResponse : function(response){
51506         if(this.form.errorReader){
51507             var rs = this.form.errorReader.read(response);
51508             var errors = [];
51509             if(rs.records){
51510                 for(var i = 0, len = rs.records.length; i < len; i++) {
51511                     var r = rs.records[i];
51512                     errors[i] = r.data;
51513                 }
51514             }
51515             if(errors.length < 1){
51516                 errors = null;
51517             }
51518             return {
51519                 success : rs.success,
51520                 errors : errors
51521             };
51522         }
51523         var ret = false;
51524         try {
51525             ret = Roo.decode(response.responseText);
51526         } catch (e) {
51527             ret = {
51528                 success: false,
51529                 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
51530                 errors : []
51531             };
51532         }
51533         return ret;
51534         
51535     }
51536 });
51537
51538
51539 Roo.form.Action.Load = function(form, options){
51540     Roo.form.Action.Load.superclass.constructor.call(this, form, options);
51541     this.reader = this.form.reader;
51542 };
51543
51544 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
51545     type : 'load',
51546
51547     run : function(){
51548         
51549         Roo.Ajax.request(Roo.apply(
51550                 this.createCallback(), {
51551                     method:this.getMethod(),
51552                     url:this.getUrl(false),
51553                     params:this.getParams()
51554         }));
51555     },
51556
51557     success : function(response){
51558         
51559         var result = this.processResponse(response);
51560         if(result === true || !result.success || !result.data){
51561             this.failureType = Roo.form.Action.LOAD_FAILURE;
51562             this.form.afterAction(this, false);
51563             return;
51564         }
51565         this.form.clearInvalid();
51566         this.form.setValues(result.data);
51567         this.form.afterAction(this, true);
51568     },
51569
51570     handleResponse : function(response){
51571         if(this.form.reader){
51572             var rs = this.form.reader.read(response);
51573             var data = rs.records && rs.records[0] ? rs.records[0].data : null;
51574             return {
51575                 success : rs.success,
51576                 data : data
51577             };
51578         }
51579         return Roo.decode(response.responseText);
51580     }
51581 });
51582
51583 Roo.form.Action.ACTION_TYPES = {
51584     'load' : Roo.form.Action.Load,
51585     'submit' : Roo.form.Action.Submit
51586 };/*
51587  * Based on:
51588  * Ext JS Library 1.1.1
51589  * Copyright(c) 2006-2007, Ext JS, LLC.
51590  *
51591  * Originally Released Under LGPL - original licence link has changed is not relivant.
51592  *
51593  * Fork - LGPL
51594  * <script type="text/javascript">
51595  */
51596  
51597 /**
51598  * @class Roo.form.Layout
51599  * @extends Roo.Component
51600  * @children Roo.form.Column Roo.form.Row Roo.form.Field Roo.Button Roo.form.TextItem
51601  * Creates a container for layout and rendering of fields in an {@link Roo.form.Form}.
51602  * @constructor
51603  * @param {Object} config Configuration options
51604  */
51605 Roo.form.Layout = function(config){
51606     var xitems = [];
51607     if (config.items) {
51608         xitems = config.items;
51609         delete config.items;
51610     }
51611     Roo.form.Layout.superclass.constructor.call(this, config);
51612     this.stack = [];
51613     Roo.each(xitems, this.addxtype, this);
51614      
51615 };
51616
51617 Roo.extend(Roo.form.Layout, Roo.Component, {
51618     /**
51619      * @cfg {String/Object} autoCreate
51620      * A DomHelper element spec used to autocreate the layout (defaults to {tag: 'div', cls: 'x-form-ct'})
51621      */
51622     /**
51623      * @cfg {String/Object/Function} style
51624      * A style specification string, e.g. "width:100px", or object in the form {width:"100px"}, or
51625      * a function which returns such a specification.
51626      */
51627     /**
51628      * @cfg {String} labelAlign
51629      * Valid values are "left," "top" and "right" (defaults to "left")
51630      */
51631     /**
51632      * @cfg {Number} labelWidth
51633      * Fixed width in pixels of all field labels (defaults to undefined)
51634      */
51635     /**
51636      * @cfg {Boolean} clear
51637      * True to add a clearing element at the end of this layout, equivalent to CSS clear: both (defaults to true)
51638      */
51639     clear : true,
51640     /**
51641      * @cfg {String} labelSeparator
51642      * The separator to use after field labels (defaults to ':')
51643      */
51644     labelSeparator : ':',
51645     /**
51646      * @cfg {Boolean} hideLabels
51647      * True to suppress the display of field labels in this layout (defaults to false)
51648      */
51649     hideLabels : false,
51650
51651     // private
51652     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct'},
51653     
51654     isLayout : true,
51655     
51656     // private
51657     onRender : function(ct, position){
51658         if(this.el){ // from markup
51659             this.el = Roo.get(this.el);
51660         }else {  // generate
51661             var cfg = this.getAutoCreate();
51662             this.el = ct.createChild(cfg, position);
51663         }
51664         if(this.style){
51665             this.el.applyStyles(this.style);
51666         }
51667         if(this.labelAlign){
51668             this.el.addClass('x-form-label-'+this.labelAlign);
51669         }
51670         if(this.hideLabels){
51671             this.labelStyle = "display:none";
51672             this.elementStyle = "padding-left:0;";
51673         }else{
51674             if(typeof this.labelWidth == 'number'){
51675                 this.labelStyle = "width:"+this.labelWidth+"px;";
51676                 this.elementStyle = "padding-left:"+((this.labelWidth+(typeof this.labelPad == 'number' ? this.labelPad : 5))+'px')+";";
51677             }
51678             if(this.labelAlign == 'top'){
51679                 this.labelStyle = "width:auto;";
51680                 this.elementStyle = "padding-left:0;";
51681             }
51682         }
51683         var stack = this.stack;
51684         var slen = stack.length;
51685         if(slen > 0){
51686             if(!this.fieldTpl){
51687                 var t = new Roo.Template(
51688                     '<div class="x-form-item {5}">',
51689                         '<label for="{0}" style="{2}">{1}{4}</label>',
51690                         '<div class="x-form-element" id="x-form-el-{0}" style="{3}">',
51691                         '</div>',
51692                     '</div><div class="x-form-clear-left"></div>'
51693                 );
51694                 t.disableFormats = true;
51695                 t.compile();
51696                 Roo.form.Layout.prototype.fieldTpl = t;
51697             }
51698             for(var i = 0; i < slen; i++) {
51699                 if(stack[i].isFormField){
51700                     this.renderField(stack[i]);
51701                 }else{
51702                     this.renderComponent(stack[i]);
51703                 }
51704             }
51705         }
51706         if(this.clear){
51707             this.el.createChild({cls:'x-form-clear'});
51708         }
51709     },
51710
51711     // private
51712     renderField : function(f){
51713         f.fieldEl = Roo.get(this.fieldTpl.append(this.el, [
51714                f.id, //0
51715                f.fieldLabel, //1
51716                f.labelStyle||this.labelStyle||'', //2
51717                this.elementStyle||'', //3
51718                typeof f.labelSeparator == 'undefined' ? this.labelSeparator : f.labelSeparator, //4
51719                f.itemCls||this.itemCls||''  //5
51720        ], true).getPrevSibling());
51721     },
51722
51723     // private
51724     renderComponent : function(c){
51725         c.render(c.isLayout ? this.el : this.el.createChild());    
51726     },
51727     /**
51728      * Adds a object form elements (using the xtype property as the factory method.)
51729      * Valid xtypes are:  TextField, TextArea .... Button, Layout, FieldSet, Column
51730      * @param {Object} config 
51731      */
51732     addxtype : function(o)
51733     {
51734         // create the lement.
51735         o.form = this.form;
51736         var fe = Roo.factory(o, Roo.form);
51737         this.form.allItems.push(fe);
51738         this.stack.push(fe);
51739         
51740         if (fe.isFormField) {
51741             this.form.items.add(fe);
51742         }
51743          
51744         return fe;
51745     }
51746 });
51747
51748 /**
51749  * @class Roo.form.Column
51750  * @extends Roo.form.Layout
51751  * @children Roo.form.Row Roo.form.Field Roo.Button Roo.form.TextItem
51752  * Creates a column container for layout and rendering of fields in an {@link Roo.form.Form}.
51753  * @constructor
51754  * @param {Object} config Configuration options
51755  */
51756 Roo.form.Column = function(config){
51757     Roo.form.Column.superclass.constructor.call(this, config);
51758 };
51759
51760 Roo.extend(Roo.form.Column, Roo.form.Layout, {
51761     /**
51762      * @cfg {Number/String} width
51763      * The fixed width of the column in pixels or CSS value (defaults to "auto")
51764      */
51765     /**
51766      * @cfg {String/Object} autoCreate
51767      * A DomHelper element spec used to autocreate the column (defaults to {tag: 'div', cls: 'x-form-ct x-form-column'})
51768      */
51769
51770     // private
51771     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct x-form-column'},
51772
51773     // private
51774     onRender : function(ct, position){
51775         Roo.form.Column.superclass.onRender.call(this, ct, position);
51776         if(this.width){
51777             this.el.setWidth(this.width);
51778         }
51779     }
51780 });
51781
51782
51783 /**
51784  * @class Roo.form.Row
51785  * @extends Roo.form.Layout
51786  * @children Roo.form.Column Roo.form.Row Roo.form.Field Roo.Button Roo.form.TextItem
51787  * Creates a row container for layout and rendering of fields in an {@link Roo.form.Form}.
51788  * @constructor
51789  * @param {Object} config Configuration options
51790  */
51791
51792  
51793 Roo.form.Row = function(config){
51794     Roo.form.Row.superclass.constructor.call(this, config);
51795 };
51796  
51797 Roo.extend(Roo.form.Row, Roo.form.Layout, {
51798       /**
51799      * @cfg {Number/String} width
51800      * The fixed width of the column in pixels or CSS value (defaults to "auto")
51801      */
51802     /**
51803      * @cfg {Number/String} height
51804      * The fixed height of the column in pixels or CSS value (defaults to "auto")
51805      */
51806     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct x-form-row'},
51807     
51808     padWidth : 20,
51809     // private
51810     onRender : function(ct, position){
51811         //console.log('row render');
51812         if(!this.rowTpl){
51813             var t = new Roo.Template(
51814                 '<div class="x-form-item {5}" style="float:left;width:{6}px">',
51815                     '<label for="{0}" style="{2}">{1}{4}</label>',
51816                     '<div class="x-form-element" id="x-form-el-{0}" style="{3}">',
51817                     '</div>',
51818                 '</div>'
51819             );
51820             t.disableFormats = true;
51821             t.compile();
51822             Roo.form.Layout.prototype.rowTpl = t;
51823         }
51824         this.fieldTpl = this.rowTpl;
51825         
51826         //console.log('lw' + this.labelWidth +', la:' + this.labelAlign);
51827         var labelWidth = 100;
51828         
51829         if ((this.labelAlign != 'top')) {
51830             if (typeof this.labelWidth == 'number') {
51831                 labelWidth = this.labelWidth
51832             }
51833             this.padWidth =  20 + labelWidth;
51834             
51835         }
51836         
51837         Roo.form.Column.superclass.onRender.call(this, ct, position);
51838         if(this.width){
51839             this.el.setWidth(this.width);
51840         }
51841         if(this.height){
51842             this.el.setHeight(this.height);
51843         }
51844     },
51845     
51846     // private
51847     renderField : function(f){
51848         f.fieldEl = this.fieldTpl.append(this.el, [
51849                f.id, f.fieldLabel,
51850                f.labelStyle||this.labelStyle||'',
51851                this.elementStyle||'',
51852                typeof f.labelSeparator == 'undefined' ? this.labelSeparator : f.labelSeparator,
51853                f.itemCls||this.itemCls||'',
51854                f.width ? f.width + this.padWidth : 160 + this.padWidth
51855        ],true);
51856     }
51857 });
51858  
51859
51860 /**
51861  * @class Roo.form.FieldSet
51862  * @extends Roo.form.Layout
51863  * @children Roo.form.Column Roo.form.Row Roo.form.Field Roo.Button Roo.form.TextItem
51864  * Creates a fieldset container for layout and rendering of fields in an {@link Roo.form.Form}.
51865  * @constructor
51866  * @param {Object} config Configuration options
51867  */
51868 Roo.form.FieldSet = function(config){
51869     Roo.form.FieldSet.superclass.constructor.call(this, config);
51870 };
51871
51872 Roo.extend(Roo.form.FieldSet, Roo.form.Layout, {
51873     /**
51874      * @cfg {String} legend
51875      * The text to display as the legend for the FieldSet (defaults to '')
51876      */
51877     /**
51878      * @cfg {String/Object} autoCreate
51879      * A DomHelper element spec used to autocreate the fieldset (defaults to {tag: 'fieldset', cn: {tag:'legend'}})
51880      */
51881
51882     // private
51883     defaultAutoCreate : {tag: 'fieldset', cn: {tag:'legend'}},
51884
51885     // private
51886     onRender : function(ct, position){
51887         Roo.form.FieldSet.superclass.onRender.call(this, ct, position);
51888         if(this.legend){
51889             this.setLegend(this.legend);
51890         }
51891     },
51892
51893     // private
51894     setLegend : function(text){
51895         if(this.rendered){
51896             this.el.child('legend').update(text);
51897         }
51898     }
51899 });/*
51900  * Based on:
51901  * Ext JS Library 1.1.1
51902  * Copyright(c) 2006-2007, Ext JS, LLC.
51903  *
51904  * Originally Released Under LGPL - original licence link has changed is not relivant.
51905  *
51906  * Fork - LGPL
51907  * <script type="text/javascript">
51908  */
51909 /**
51910  * @class Roo.form.VTypes
51911  * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
51912  * @static
51913  */
51914 Roo.form.VTypes = function(){
51915     // closure these in so they are only created once.
51916     var alpha = /^[a-zA-Z_]+$/;
51917     var alphanum = /^[a-zA-Z0-9_]+$/;
51918     var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,24}$/;
51919     var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
51920
51921     // All these messages and functions are configurable
51922     return {
51923         /**
51924          * The function used to validate email addresses
51925          * @param {String} value The email address
51926          */
51927         'email' : function(v){
51928             return email.test(v);
51929         },
51930         /**
51931          * The error text to display when the email validation function returns false
51932          * @type String
51933          */
51934         'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
51935         /**
51936          * The keystroke filter mask to be applied on email input
51937          * @type RegExp
51938          */
51939         'emailMask' : /[a-z0-9_\.\-@]/i,
51940
51941         /**
51942          * The function used to validate URLs
51943          * @param {String} value The URL
51944          */
51945         'url' : function(v){
51946             return url.test(v);
51947         },
51948         /**
51949          * The error text to display when the url validation function returns false
51950          * @type String
51951          */
51952         'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
51953         
51954         /**
51955          * The function used to validate alpha values
51956          * @param {String} value The value
51957          */
51958         'alpha' : function(v){
51959             return alpha.test(v);
51960         },
51961         /**
51962          * The error text to display when the alpha validation function returns false
51963          * @type String
51964          */
51965         'alphaText' : 'This field should only contain letters and _',
51966         /**
51967          * The keystroke filter mask to be applied on alpha input
51968          * @type RegExp
51969          */
51970         'alphaMask' : /[a-z_]/i,
51971
51972         /**
51973          * The function used to validate alphanumeric values
51974          * @param {String} value The value
51975          */
51976         'alphanum' : function(v){
51977             return alphanum.test(v);
51978         },
51979         /**
51980          * The error text to display when the alphanumeric validation function returns false
51981          * @type String
51982          */
51983         'alphanumText' : 'This field should only contain letters, numbers and _',
51984         /**
51985          * The keystroke filter mask to be applied on alphanumeric input
51986          * @type RegExp
51987          */
51988         'alphanumMask' : /[a-z0-9_]/i
51989     };
51990 }();//<script type="text/javascript">
51991
51992 /**
51993  * @class Roo.form.FCKeditor
51994  * @extends Roo.form.TextArea
51995  * Wrapper around the FCKEditor http://www.fckeditor.net
51996  * @constructor
51997  * Creates a new FCKeditor
51998  * @param {Object} config Configuration options
51999  */
52000 Roo.form.FCKeditor = function(config){
52001     Roo.form.FCKeditor.superclass.constructor.call(this, config);
52002     this.addEvents({
52003          /**
52004          * @event editorinit
52005          * Fired when the editor is initialized - you can add extra handlers here..
52006          * @param {FCKeditor} this
52007          * @param {Object} the FCK object.
52008          */
52009         editorinit : true
52010     });
52011     
52012     
52013 };
52014 Roo.form.FCKeditor.editors = { };
52015 Roo.extend(Roo.form.FCKeditor, Roo.form.TextArea,
52016 {
52017     //defaultAutoCreate : {
52018     //    tag : "textarea",style   : "width:100px;height:60px;" ,autocomplete    : "off"
52019     //},
52020     // private
52021     /**
52022      * @cfg {Object} fck options - see fck manual for details.
52023      */
52024     fckconfig : false,
52025     
52026     /**
52027      * @cfg {Object} fck toolbar set (Basic or Default)
52028      */
52029     toolbarSet : 'Basic',
52030     /**
52031      * @cfg {Object} fck BasePath
52032      */ 
52033     basePath : '/fckeditor/',
52034     
52035     
52036     frame : false,
52037     
52038     value : '',
52039     
52040    
52041     onRender : function(ct, position)
52042     {
52043         if(!this.el){
52044             this.defaultAutoCreate = {
52045                 tag: "textarea",
52046                 style:"width:300px;height:60px;",
52047                 autocomplete: "new-password"
52048             };
52049         }
52050         Roo.form.FCKeditor.superclass.onRender.call(this, ct, position);
52051         /*
52052         if(this.grow){
52053             this.textSizeEl = Roo.DomHelper.append(document.body, {tag: "pre", cls: "x-form-grow-sizer"});
52054             if(this.preventScrollbars){
52055                 this.el.setStyle("overflow", "hidden");
52056             }
52057             this.el.setHeight(this.growMin);
52058         }
52059         */
52060         //console.log('onrender' + this.getId() );
52061         Roo.form.FCKeditor.editors[this.getId()] = this;
52062          
52063
52064         this.replaceTextarea() ;
52065         
52066     },
52067     
52068     getEditor : function() {
52069         return this.fckEditor;
52070     },
52071     /**
52072      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
52073      * @param {Mixed} value The value to set
52074      */
52075     
52076     
52077     setValue : function(value)
52078     {
52079         //console.log('setValue: ' + value);
52080         
52081         if(typeof(value) == 'undefined') { // not sure why this is happending...
52082             return;
52083         }
52084         Roo.form.FCKeditor.superclass.setValue.apply(this,[value]);
52085         
52086         //if(!this.el || !this.getEditor()) {
52087         //    this.value = value;
52088             //this.setValue.defer(100,this,[value]);    
52089         //    return;
52090         //} 
52091         
52092         if(!this.getEditor()) {
52093             return;
52094         }
52095         
52096         this.getEditor().SetData(value);
52097         
52098         //
52099
52100     },
52101
52102     /**
52103      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
52104      * @return {Mixed} value The field value
52105      */
52106     getValue : function()
52107     {
52108         
52109         if (this.frame && this.frame.dom.style.display == 'none') {
52110             return Roo.form.FCKeditor.superclass.getValue.call(this);
52111         }
52112         
52113         if(!this.el || !this.getEditor()) {
52114            
52115            // this.getValue.defer(100,this); 
52116             return this.value;
52117         }
52118        
52119         
52120         var value=this.getEditor().GetData();
52121         Roo.form.FCKeditor.superclass.setValue.apply(this,[value]);
52122         return Roo.form.FCKeditor.superclass.getValue.call(this);
52123         
52124
52125     },
52126
52127     /**
52128      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
52129      * @return {Mixed} value The field value
52130      */
52131     getRawValue : function()
52132     {
52133         if (this.frame && this.frame.dom.style.display == 'none') {
52134             return Roo.form.FCKeditor.superclass.getRawValue.call(this);
52135         }
52136         
52137         if(!this.el || !this.getEditor()) {
52138             //this.getRawValue.defer(100,this); 
52139             return this.value;
52140             return;
52141         }
52142         
52143         
52144         
52145         var value=this.getEditor().GetData();
52146         Roo.form.FCKeditor.superclass.setRawValue.apply(this,[value]);
52147         return Roo.form.FCKeditor.superclass.getRawValue.call(this);
52148          
52149     },
52150     
52151     setSize : function(w,h) {
52152         
52153         
52154         
52155         //if (this.frame && this.frame.dom.style.display == 'none') {
52156         //    Roo.form.FCKeditor.superclass.setSize.apply(this, [w, h]);
52157         //    return;
52158         //}
52159         //if(!this.el || !this.getEditor()) {
52160         //    this.setSize.defer(100,this, [w,h]); 
52161         //    return;
52162         //}
52163         
52164         
52165         
52166         Roo.form.FCKeditor.superclass.setSize.apply(this, [w, h]);
52167         
52168         this.frame.dom.setAttribute('width', w);
52169         this.frame.dom.setAttribute('height', h);
52170         this.frame.setSize(w,h);
52171         
52172     },
52173     
52174     toggleSourceEdit : function(value) {
52175         
52176       
52177          
52178         this.el.dom.style.display = value ? '' : 'none';
52179         this.frame.dom.style.display = value ?  'none' : '';
52180         
52181     },
52182     
52183     
52184     focus: function(tag)
52185     {
52186         if (this.frame.dom.style.display == 'none') {
52187             return Roo.form.FCKeditor.superclass.focus.call(this);
52188         }
52189         if(!this.el || !this.getEditor()) {
52190             this.focus.defer(100,this, [tag]); 
52191             return;
52192         }
52193         
52194         
52195         
52196         
52197         var tgs = this.getEditor().EditorDocument.getElementsByTagName(tag);
52198         this.getEditor().Focus();
52199         if (tgs.length) {
52200             if (!this.getEditor().Selection.GetSelection()) {
52201                 this.focus.defer(100,this, [tag]); 
52202                 return;
52203             }
52204             
52205             
52206             var r = this.getEditor().EditorDocument.createRange();
52207             r.setStart(tgs[0],0);
52208             r.setEnd(tgs[0],0);
52209             this.getEditor().Selection.GetSelection().removeAllRanges();
52210             this.getEditor().Selection.GetSelection().addRange(r);
52211             this.getEditor().Focus();
52212         }
52213         
52214     },
52215     
52216     
52217     
52218     replaceTextarea : function()
52219     {
52220         if ( document.getElementById( this.getId() + '___Frame' ) ) {
52221             return ;
52222         }
52223         //if ( !this.checkBrowser || this._isCompatibleBrowser() )
52224         //{
52225             // We must check the elements firstly using the Id and then the name.
52226         var oTextarea = document.getElementById( this.getId() );
52227         
52228         var colElementsByName = document.getElementsByName( this.getId() ) ;
52229          
52230         oTextarea.style.display = 'none' ;
52231
52232         if ( oTextarea.tabIndex ) {            
52233             this.TabIndex = oTextarea.tabIndex ;
52234         }
52235         
52236         this._insertHtmlBefore( this._getConfigHtml(), oTextarea ) ;
52237         this._insertHtmlBefore( this._getIFrameHtml(), oTextarea ) ;
52238         this.frame = Roo.get(this.getId() + '___Frame')
52239     },
52240     
52241     _getConfigHtml : function()
52242     {
52243         var sConfig = '' ;
52244
52245         for ( var o in this.fckconfig ) {
52246             sConfig += sConfig.length > 0  ? '&amp;' : '';
52247             sConfig += encodeURIComponent( o ) + '=' + encodeURIComponent( this.fckconfig[o] ) ;
52248         }
52249
52250         return '<input type="hidden" id="' + this.getId() + '___Config" value="' + sConfig + '" style="display:none" />' ;
52251     },
52252     
52253     
52254     _getIFrameHtml : function()
52255     {
52256         var sFile = 'fckeditor.html' ;
52257         /* no idea what this is about..
52258         try
52259         {
52260             if ( (/fcksource=true/i).test( window.top.location.search ) )
52261                 sFile = 'fckeditor.original.html' ;
52262         }
52263         catch (e) { 
52264         */
52265
52266         var sLink = this.basePath + 'editor/' + sFile + '?InstanceName=' + encodeURIComponent( this.getId() ) ;
52267         sLink += this.toolbarSet ? ( '&amp;Toolbar=' + this.toolbarSet)  : '';
52268         
52269         
52270         var html = '<iframe id="' + this.getId() +
52271             '___Frame" src="' + sLink +
52272             '" width="' + this.width +
52273             '" height="' + this.height + '"' +
52274             (this.tabIndex ?  ' tabindex="' + this.tabIndex + '"' :'' ) +
52275             ' frameborder="0" scrolling="no"></iframe>' ;
52276
52277         return html ;
52278     },
52279     
52280     _insertHtmlBefore : function( html, element )
52281     {
52282         if ( element.insertAdjacentHTML )       {
52283             // IE
52284             element.insertAdjacentHTML( 'beforeBegin', html ) ;
52285         } else { // Gecko
52286             var oRange = document.createRange() ;
52287             oRange.setStartBefore( element ) ;
52288             var oFragment = oRange.createContextualFragment( html );
52289             element.parentNode.insertBefore( oFragment, element ) ;
52290         }
52291     }
52292     
52293     
52294   
52295     
52296     
52297     
52298     
52299
52300 });
52301
52302 //Roo.reg('fckeditor', Roo.form.FCKeditor);
52303
52304 function FCKeditor_OnComplete(editorInstance){
52305     var f = Roo.form.FCKeditor.editors[editorInstance.Name];
52306     f.fckEditor = editorInstance;
52307     //console.log("loaded");
52308     f.fireEvent('editorinit', f, editorInstance);
52309
52310   
52311
52312  
52313
52314
52315
52316
52317
52318
52319
52320
52321
52322
52323
52324
52325
52326
52327
52328 //<script type="text/javascript">
52329 /**
52330  * @class Roo.form.GridField
52331  * @extends Roo.form.Field
52332  * Embed a grid (or editable grid into a form)
52333  * STATUS ALPHA
52334  * 
52335  * This embeds a grid in a form, the value of the field should be the json encoded array of rows
52336  * it needs 
52337  * xgrid.store = Roo.data.Store
52338  * xgrid.store.proxy = Roo.data.MemoryProxy (data = [] )
52339  * xgrid.store.reader = Roo.data.JsonReader 
52340  * 
52341  * 
52342  * @constructor
52343  * Creates a new GridField
52344  * @param {Object} config Configuration options
52345  */
52346 Roo.form.GridField = function(config){
52347     Roo.form.GridField.superclass.constructor.call(this, config);
52348      
52349 };
52350
52351 Roo.extend(Roo.form.GridField, Roo.form.Field,  {
52352     /**
52353      * @cfg {Number} width  - used to restrict width of grid..
52354      */
52355     width : 100,
52356     /**
52357      * @cfg {Number} height - used to restrict height of grid..
52358      */
52359     height : 50,
52360      /**
52361      * @cfg {Object} xgrid (xtype'd description of grid) { xtype : 'Grid', dataSource: .... }
52362          * 
52363          *}
52364      */
52365     xgrid : false, 
52366     /**
52367      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
52368      * {tag: "input", type: "checkbox", autocomplete: "off"})
52369      */
52370    // defaultAutoCreate : { tag: 'div' },
52371     defaultAutoCreate : { tag: 'input', type: 'hidden', autocomplete: 'new-password'},
52372     /**
52373      * @cfg {String} addTitle Text to include for adding a title.
52374      */
52375     addTitle : false,
52376     //
52377     onResize : function(){
52378         Roo.form.Field.superclass.onResize.apply(this, arguments);
52379     },
52380
52381     initEvents : function(){
52382         // Roo.form.Checkbox.superclass.initEvents.call(this);
52383         // has no events...
52384        
52385     },
52386
52387
52388     getResizeEl : function(){
52389         return this.wrap;
52390     },
52391
52392     getPositionEl : function(){
52393         return this.wrap;
52394     },
52395
52396     // private
52397     onRender : function(ct, position){
52398         
52399         this.style = this.style || 'overflow: hidden; border:1px solid #c3daf9;';
52400         var style = this.style;
52401         delete this.style;
52402         
52403         Roo.form.GridField.superclass.onRender.call(this, ct, position);
52404         this.wrap = this.el.wrap({cls: ''}); // not sure why ive done thsi...
52405         this.viewEl = this.wrap.createChild({ tag: 'div' });
52406         if (style) {
52407             this.viewEl.applyStyles(style);
52408         }
52409         if (this.width) {
52410             this.viewEl.setWidth(this.width);
52411         }
52412         if (this.height) {
52413             this.viewEl.setHeight(this.height);
52414         }
52415         //if(this.inputValue !== undefined){
52416         //this.setValue(this.value);
52417         
52418         
52419         this.grid = new Roo.grid[this.xgrid.xtype](this.viewEl, this.xgrid);
52420         
52421         
52422         this.grid.render();
52423         this.grid.getDataSource().on('remove', this.refreshValue, this);
52424         this.grid.getDataSource().on('update', this.refreshValue, this);
52425         this.grid.on('afteredit', this.refreshValue, this);
52426  
52427     },
52428      
52429     
52430     /**
52431      * Sets the value of the item. 
52432      * @param {String} either an object  or a string..
52433      */
52434     setValue : function(v){
52435         //this.value = v;
52436         v = v || []; // empty set..
52437         // this does not seem smart - it really only affects memoryproxy grids..
52438         if (this.grid && this.grid.getDataSource() && typeof(v) != 'undefined') {
52439             var ds = this.grid.getDataSource();
52440             // assumes a json reader..
52441             var data = {}
52442             data[ds.reader.meta.root ] =  typeof(v) == 'string' ? Roo.decode(v) : v;
52443             ds.loadData( data);
52444         }
52445         // clear selection so it does not get stale.
52446         if (this.grid.sm) { 
52447             this.grid.sm.clearSelections();
52448         }
52449         
52450         Roo.form.GridField.superclass.setValue.call(this, v);
52451         this.refreshValue();
52452         // should load data in the grid really....
52453     },
52454     
52455     // private
52456     refreshValue: function() {
52457          var val = [];
52458         this.grid.getDataSource().each(function(r) {
52459             val.push(r.data);
52460         });
52461         this.el.dom.value = Roo.encode(val);
52462     }
52463     
52464      
52465     
52466     
52467 });/*
52468  * Based on:
52469  * Ext JS Library 1.1.1
52470  * Copyright(c) 2006-2007, Ext JS, LLC.
52471  *
52472  * Originally Released Under LGPL - original licence link has changed is not relivant.
52473  *
52474  * Fork - LGPL
52475  * <script type="text/javascript">
52476  */
52477 /**
52478  * @class Roo.form.DisplayField
52479  * @extends Roo.form.Field
52480  * A generic Field to display non-editable data.
52481  * @cfg {Boolean} closable (true|false) default false
52482  * @constructor
52483  * Creates a new Display Field item.
52484  * @param {Object} config Configuration options
52485  */
52486 Roo.form.DisplayField = function(config){
52487     Roo.form.DisplayField.superclass.constructor.call(this, config);
52488     
52489     this.addEvents({
52490         /**
52491          * @event close
52492          * Fires after the click the close btn
52493              * @param {Roo.form.DisplayField} this
52494              */
52495         close : true
52496     });
52497 };
52498
52499 Roo.extend(Roo.form.DisplayField, Roo.form.TextField,  {
52500     inputType:      'hidden',
52501     allowBlank:     true,
52502     readOnly:         true,
52503     
52504  
52505     /**
52506      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
52507      */
52508     focusClass : undefined,
52509     /**
52510      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
52511      */
52512     fieldClass: 'x-form-field',
52513     
52514      /**
52515      * @cfg {Function} valueRenderer The renderer for the field (so you can reformat output). should return raw HTML
52516      */
52517     valueRenderer: undefined,
52518     
52519     width: 100,
52520     /**
52521      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
52522      * {tag: "input", type: "checkbox", autocomplete: "off"})
52523      */
52524      
52525  //   defaultAutoCreate : { tag: 'input', type: 'hidden', autocomplete: 'off'},
52526  
52527     closable : false,
52528     
52529     onResize : function(){
52530         Roo.form.DisplayField.superclass.onResize.apply(this, arguments);
52531         
52532     },
52533
52534     initEvents : function(){
52535         // Roo.form.Checkbox.superclass.initEvents.call(this);
52536         // has no events...
52537         
52538         if(this.closable){
52539             this.closeEl.on('click', this.onClose, this);
52540         }
52541        
52542     },
52543
52544
52545     getResizeEl : function(){
52546         return this.wrap;
52547     },
52548
52549     getPositionEl : function(){
52550         return this.wrap;
52551     },
52552
52553     // private
52554     onRender : function(ct, position){
52555         
52556         Roo.form.DisplayField.superclass.onRender.call(this, ct, position);
52557         //if(this.inputValue !== undefined){
52558         this.wrap = this.el.wrap();
52559         
52560         this.viewEl = this.wrap.createChild({ tag: 'div', cls: 'x-form-displayfield'});
52561         
52562         if(this.closable){
52563             this.closeEl = this.wrap.createChild({ tag: 'div', cls: 'x-dlg-close'});
52564         }
52565         
52566         if (this.bodyStyle) {
52567             this.viewEl.applyStyles(this.bodyStyle);
52568         }
52569         //this.viewEl.setStyle('padding', '2px');
52570         
52571         this.setValue(this.value);
52572         
52573     },
52574 /*
52575     // private
52576     initValue : Roo.emptyFn,
52577
52578   */
52579
52580         // private
52581     onClick : function(){
52582         
52583     },
52584
52585     /**
52586      * Sets the checked state of the checkbox.
52587      * @param {Boolean/String} checked True, 'true', '1', or 'on' to check the checkbox, any other value will uncheck it.
52588      */
52589     setValue : function(v){
52590         this.value = v;
52591         var html = this.valueRenderer ?  this.valueRenderer(v) : String.format('{0}', v);
52592         // this might be called before we have a dom element..
52593         if (!this.viewEl) {
52594             return;
52595         }
52596         this.viewEl.dom.innerHTML = html;
52597         Roo.form.DisplayField.superclass.setValue.call(this, v);
52598
52599     },
52600     
52601     onClose : function(e)
52602     {
52603         e.preventDefault();
52604         
52605         this.fireEvent('close', this);
52606     }
52607 });/*
52608  * 
52609  * Licence- LGPL
52610  * 
52611  */
52612
52613 /**
52614  * @class Roo.form.DayPicker
52615  * @extends Roo.form.Field
52616  * A Day picker show [M] [T] [W] ....
52617  * @constructor
52618  * Creates a new Day Picker
52619  * @param {Object} config Configuration options
52620  */
52621 Roo.form.DayPicker= function(config){
52622     Roo.form.DayPicker.superclass.constructor.call(this, config);
52623      
52624 };
52625
52626 Roo.extend(Roo.form.DayPicker, Roo.form.Field,  {
52627     /**
52628      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
52629      */
52630     focusClass : undefined,
52631     /**
52632      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
52633      */
52634     fieldClass: "x-form-field",
52635    
52636     /**
52637      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
52638      * {tag: "input", type: "checkbox", autocomplete: "off"})
52639      */
52640     defaultAutoCreate : { tag: "input", type: 'hidden', autocomplete: "new-password"},
52641     
52642    
52643     actionMode : 'viewEl', 
52644     //
52645     // private
52646  
52647     inputType : 'hidden',
52648     
52649      
52650     inputElement: false, // real input element?
52651     basedOn: false, // ????
52652     
52653     isFormField: true, // not sure where this is needed!!!!
52654
52655     onResize : function(){
52656         Roo.form.Checkbox.superclass.onResize.apply(this, arguments);
52657         if(!this.boxLabel){
52658             this.el.alignTo(this.wrap, 'c-c');
52659         }
52660     },
52661
52662     initEvents : function(){
52663         Roo.form.Checkbox.superclass.initEvents.call(this);
52664         this.el.on("click", this.onClick,  this);
52665         this.el.on("change", this.onClick,  this);
52666     },
52667
52668
52669     getResizeEl : function(){
52670         return this.wrap;
52671     },
52672
52673     getPositionEl : function(){
52674         return this.wrap;
52675     },
52676
52677     
52678     // private
52679     onRender : function(ct, position){
52680         Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
52681        
52682         this.wrap = this.el.wrap({cls: 'x-form-daypick-item '});
52683         
52684         var r1 = '<table><tr>';
52685         var r2 = '<tr class="x-form-daypick-icons">';
52686         for (var i=0; i < 7; i++) {
52687             r1+= '<td><div>' + Date.dayNames[i].substring(0,3) + '</div></td>';
52688             r2+= '<td><img class="x-menu-item-icon" src="' + Roo.BLANK_IMAGE_URL  +'"></td>';
52689         }
52690         
52691         var viewEl = this.wrap.createChild( r1 + '</tr>' + r2 + '</tr></table>');
52692         viewEl.select('img').on('click', this.onClick, this);
52693         this.viewEl = viewEl;   
52694         
52695         
52696         // this will not work on Chrome!!!
52697         this.el.on('DOMAttrModified', this.setFromHidden,  this); //ff
52698         this.el.on('propertychange', this.setFromHidden,  this);  //ie
52699         
52700         
52701           
52702
52703     },
52704
52705     // private
52706     initValue : Roo.emptyFn,
52707
52708     /**
52709      * Returns the checked state of the checkbox.
52710      * @return {Boolean} True if checked, else false
52711      */
52712     getValue : function(){
52713         return this.el.dom.value;
52714         
52715     },
52716
52717         // private
52718     onClick : function(e){ 
52719         //this.setChecked(!this.checked);
52720         Roo.get(e.target).toggleClass('x-menu-item-checked');
52721         this.refreshValue();
52722         //if(this.el.dom.checked != this.checked){
52723         //    this.setValue(this.el.dom.checked);
52724        // }
52725     },
52726     
52727     // private
52728     refreshValue : function()
52729     {
52730         var val = '';
52731         this.viewEl.select('img',true).each(function(e,i,n)  {
52732             val += e.is(".x-menu-item-checked") ? String(n) : '';
52733         });
52734         this.setValue(val, true);
52735     },
52736
52737     /**
52738      * Sets the checked state of the checkbox.
52739      * On is always based on a string comparison between inputValue and the param.
52740      * @param {Boolean/String} value - the value to set 
52741      * @param {Boolean/String} suppressEvent - whether to suppress the checkchange event.
52742      */
52743     setValue : function(v,suppressEvent){
52744         if (!this.el.dom) {
52745             return;
52746         }
52747         var old = this.el.dom.value ;
52748         this.el.dom.value = v;
52749         if (suppressEvent) {
52750             return ;
52751         }
52752          
52753         // update display..
52754         this.viewEl.select('img',true).each(function(e,i,n)  {
52755             
52756             var on = e.is(".x-menu-item-checked");
52757             var newv = v.indexOf(String(n)) > -1;
52758             if (on != newv) {
52759                 e.toggleClass('x-menu-item-checked');
52760             }
52761             
52762         });
52763         
52764         
52765         this.fireEvent('change', this, v, old);
52766         
52767         
52768     },
52769    
52770     // handle setting of hidden value by some other method!!?!?
52771     setFromHidden: function()
52772     {
52773         if(!this.el){
52774             return;
52775         }
52776         //console.log("SET FROM HIDDEN");
52777         //alert('setFrom hidden');
52778         this.setValue(this.el.dom.value);
52779     },
52780     
52781     onDestroy : function()
52782     {
52783         if(this.viewEl){
52784             Roo.get(this.viewEl).remove();
52785         }
52786          
52787         Roo.form.DayPicker.superclass.onDestroy.call(this);
52788     }
52789
52790 });/*
52791  * RooJS Library 1.1.1
52792  * Copyright(c) 2008-2011  Alan Knowles
52793  *
52794  * License - LGPL
52795  */
52796  
52797
52798 /**
52799  * @class Roo.form.ComboCheck
52800  * @extends Roo.form.ComboBox
52801  * A combobox for multiple select items.
52802  *
52803  * FIXME - could do with a reset button..
52804  * 
52805  * @constructor
52806  * Create a new ComboCheck
52807  * @param {Object} config Configuration options
52808  */
52809 Roo.form.ComboCheck = function(config){
52810     Roo.form.ComboCheck.superclass.constructor.call(this, config);
52811     // should verify some data...
52812     // like
52813     // hiddenName = required..
52814     // displayField = required
52815     // valudField == required
52816     var req= [ 'hiddenName', 'displayField', 'valueField' ];
52817     var _t = this;
52818     Roo.each(req, function(e) {
52819         if ((typeof(_t[e]) == 'undefined' ) || !_t[e].length) {
52820             throw "Roo.form.ComboCheck : missing value for: " + e;
52821         }
52822     });
52823     
52824     
52825 };
52826
52827 Roo.extend(Roo.form.ComboCheck, Roo.form.ComboBox, {
52828      
52829      
52830     editable : false,
52831      
52832     selectedClass: 'x-menu-item-checked', 
52833     
52834     // private
52835     onRender : function(ct, position){
52836         var _t = this;
52837         
52838         
52839         
52840         if(!this.tpl){
52841             var cls = 'x-combo-list';
52842
52843             
52844             this.tpl =  new Roo.Template({
52845                 html :  '<div class="'+cls+'-item x-menu-check-item">' +
52846                    '<img class="x-menu-item-icon" style="margin: 0px;" src="' + Roo.BLANK_IMAGE_URL + '">' + 
52847                    '<span>{' + this.displayField + '}</span>' +
52848                     '</div>' 
52849                 
52850             });
52851         }
52852  
52853         
52854         Roo.form.ComboCheck.superclass.onRender.call(this, ct, position);
52855         this.view.singleSelect = false;
52856         this.view.multiSelect = true;
52857         this.view.toggleSelect = true;
52858         this.pageTb.add(new Roo.Toolbar.Fill(), {
52859             
52860             text: 'Done',
52861             handler: function()
52862             {
52863                 _t.collapse();
52864             }
52865         });
52866     },
52867     
52868     onViewOver : function(e, t){
52869         // do nothing...
52870         return;
52871         
52872     },
52873     
52874     onViewClick : function(doFocus,index){
52875         return;
52876         
52877     },
52878     select: function () {
52879         //Roo.log("SELECT CALLED");
52880     },
52881      
52882     selectByValue : function(xv, scrollIntoView){
52883         var ar = this.getValueArray();
52884         var sels = [];
52885         
52886         Roo.each(ar, function(v) {
52887             if(v === undefined || v === null){
52888                 return;
52889             }
52890             var r = this.findRecord(this.valueField, v);
52891             if(r){
52892                 sels.push(this.store.indexOf(r))
52893                 
52894             }
52895         },this);
52896         this.view.select(sels);
52897         return false;
52898     },
52899     
52900     
52901     
52902     onSelect : function(record, index){
52903        // Roo.log("onselect Called");
52904        // this is only called by the clear button now..
52905         this.view.clearSelections();
52906         this.setValue('[]');
52907         if (this.value != this.valueBefore) {
52908             this.fireEvent('change', this, this.value, this.valueBefore);
52909             this.valueBefore = this.value;
52910         }
52911     },
52912     getValueArray : function()
52913     {
52914         var ar = [] ;
52915         
52916         try {
52917             //Roo.log(this.value);
52918             if (typeof(this.value) == 'undefined') {
52919                 return [];
52920             }
52921             var ar = Roo.decode(this.value);
52922             return  ar instanceof Array ? ar : []; //?? valid?
52923             
52924         } catch(e) {
52925             Roo.log(e + "\nRoo.form.ComboCheck:getValueArray  invalid data:" + this.getValue());
52926             return [];
52927         }
52928          
52929     },
52930     expand : function ()
52931     {
52932         
52933         Roo.form.ComboCheck.superclass.expand.call(this);
52934         this.valueBefore = typeof(this.value) == 'undefined' ? '' : this.value;
52935         //this.valueBefore = typeof(this.valueBefore) == 'undefined' ? '' : this.valueBefore;
52936         
52937
52938     },
52939     
52940     collapse : function(){
52941         Roo.form.ComboCheck.superclass.collapse.call(this);
52942         var sl = this.view.getSelectedIndexes();
52943         var st = this.store;
52944         var nv = [];
52945         var tv = [];
52946         var r;
52947         Roo.each(sl, function(i) {
52948             r = st.getAt(i);
52949             nv.push(r.get(this.valueField));
52950         },this);
52951         this.setValue(Roo.encode(nv));
52952         if (this.value != this.valueBefore) {
52953
52954             this.fireEvent('change', this, this.value, this.valueBefore);
52955             this.valueBefore = this.value;
52956         }
52957         
52958     },
52959     
52960     setValue : function(v){
52961         // Roo.log(v);
52962         this.value = v;
52963         
52964         var vals = this.getValueArray();
52965         var tv = [];
52966         Roo.each(vals, function(k) {
52967             var r = this.findRecord(this.valueField, k);
52968             if(r){
52969                 tv.push(r.data[this.displayField]);
52970             }else if(this.valueNotFoundText !== undefined){
52971                 tv.push( this.valueNotFoundText );
52972             }
52973         },this);
52974        // Roo.log(tv);
52975         
52976         Roo.form.ComboBox.superclass.setValue.call(this, tv.join(', '));
52977         this.hiddenField.value = v;
52978         this.value = v;
52979     }
52980     
52981 });/*
52982  * Based on:
52983  * Ext JS Library 1.1.1
52984  * Copyright(c) 2006-2007, Ext JS, LLC.
52985  *
52986  * Originally Released Under LGPL - original licence link has changed is not relivant.
52987  *
52988  * Fork - LGPL
52989  * <script type="text/javascript">
52990  */
52991  
52992 /**
52993  * @class Roo.form.Signature
52994  * @extends Roo.form.Field
52995  * Signature field.  
52996  * @constructor
52997  * 
52998  * @param {Object} config Configuration options
52999  */
53000
53001 Roo.form.Signature = function(config){
53002     Roo.form.Signature.superclass.constructor.call(this, config);
53003     
53004     this.addEvents({// not in used??
53005          /**
53006          * @event confirm
53007          * Fires when the 'confirm' icon is pressed (add a listener to enable add button)
53008              * @param {Roo.form.Signature} combo This combo box
53009              */
53010         'confirm' : true,
53011         /**
53012          * @event reset
53013          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
53014              * @param {Roo.form.ComboBox} combo This combo box
53015              * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
53016              */
53017         'reset' : true
53018     });
53019 };
53020
53021 Roo.extend(Roo.form.Signature, Roo.form.Field,  {
53022     /**
53023      * @cfg {Object} labels Label to use when rendering a form.
53024      * defaults to 
53025      * labels : { 
53026      *      clear : "Clear",
53027      *      confirm : "Confirm"
53028      *  }
53029      */
53030     labels : { 
53031         clear : "Clear",
53032         confirm : "Confirm"
53033     },
53034     /**
53035      * @cfg {Number} width The signature panel width (defaults to 300)
53036      */
53037     width: 300,
53038     /**
53039      * @cfg {Number} height The signature panel height (defaults to 100)
53040      */
53041     height : 100,
53042     /**
53043      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to false)
53044      */
53045     allowBlank : false,
53046     
53047     //private
53048     // {Object} signPanel The signature SVG panel element (defaults to {})
53049     signPanel : {},
53050     // {Boolean} isMouseDown False to validate that the mouse down event (defaults to false)
53051     isMouseDown : false,
53052     // {Boolean} isConfirmed validate the signature is confirmed or not for submitting form (defaults to false)
53053     isConfirmed : false,
53054     // {String} signatureTmp SVG mapping string (defaults to empty string)
53055     signatureTmp : '',
53056     
53057     
53058     defaultAutoCreate : { // modified by initCompnoent..
53059         tag: "input",
53060         type:"hidden"
53061     },
53062
53063     // private
53064     onRender : function(ct, position){
53065         
53066         Roo.form.Signature.superclass.onRender.call(this, ct, position);
53067         
53068         this.wrap = this.el.wrap({
53069             cls:'x-form-signature-wrap', style : 'width: ' + this.width + 'px', cn:{cls:'x-form-signature'}
53070         });
53071         
53072         this.createToolbar(this);
53073         this.signPanel = this.wrap.createChild({
53074                 tag: 'div',
53075                 style: 'width: ' + this.width + 'px; height: ' + this.height + 'px; border: 0;'
53076             }, this.el
53077         );
53078             
53079         this.svgID = Roo.id();
53080         this.svgEl = this.signPanel.createChild({
53081               xmlns : 'http://www.w3.org/2000/svg',
53082               tag : 'svg',
53083               id : this.svgID + "-svg",
53084               width: this.width,
53085               height: this.height,
53086               viewBox: '0 0 '+this.width+' '+this.height,
53087               cn : [
53088                 {
53089                     tag: "rect",
53090                     id: this.svgID + "-svg-r",
53091                     width: this.width,
53092                     height: this.height,
53093                     fill: "#ffa"
53094                 },
53095                 {
53096                     tag: "line",
53097                     id: this.svgID + "-svg-l",
53098                     x1: "0", // start
53099                     y1: (this.height*0.8), // start set the line in 80% of height
53100                     x2: this.width, // end
53101                     y2: (this.height*0.8), // end set the line in 80% of height
53102                     'stroke': "#666",
53103                     'stroke-width': "1",
53104                     'stroke-dasharray': "3",
53105                     'shape-rendering': "crispEdges",
53106                     'pointer-events': "none"
53107                 },
53108                 {
53109                     tag: "path",
53110                     id: this.svgID + "-svg-p",
53111                     'stroke': "navy",
53112                     'stroke-width': "3",
53113                     'fill': "none",
53114                     'pointer-events': 'none'
53115                 }
53116               ]
53117         });
53118         this.createSVG();
53119         this.svgBox = this.svgEl.dom.getScreenCTM();
53120     },
53121     createSVG : function(){ 
53122         var svg = this.signPanel;
53123         var r = svg.select('#'+ this.svgID + '-svg-r', true).first().dom;
53124         var t = this;
53125
53126         r.addEventListener('mousedown', function(e) { return t.down(e); }, false);
53127         r.addEventListener('mousemove', function(e) { return t.move(e); }, false);
53128         r.addEventListener('mouseup', function(e) { return t.up(e); }, false);
53129         r.addEventListener('mouseout', function(e) { return t.up(e); }, false);
53130         r.addEventListener('touchstart', function(e) { return t.down(e); }, false);
53131         r.addEventListener('touchmove', function(e) { return t.move(e); }, false);
53132         r.addEventListener('touchend', function(e) { return t.up(e); }, false);
53133         
53134     },
53135     isTouchEvent : function(e){
53136         return e.type.match(/^touch/);
53137     },
53138     getCoords : function (e) {
53139         var pt    = this.svgEl.dom.createSVGPoint();
53140         pt.x = e.clientX; 
53141         pt.y = e.clientY;
53142         if (this.isTouchEvent(e)) {
53143             pt.x =  e.targetTouches[0].clientX;
53144             pt.y = e.targetTouches[0].clientY;
53145         }
53146         var a = this.svgEl.dom.getScreenCTM();
53147         var b = a.inverse();
53148         var mx = pt.matrixTransform(b);
53149         return mx.x + ',' + mx.y;
53150     },
53151     //mouse event headler 
53152     down : function (e) {
53153         this.signatureTmp += 'M' + this.getCoords(e) + ' ';
53154         this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr('d', this.signatureTmp);
53155         
53156         this.isMouseDown = true;
53157         
53158         e.preventDefault();
53159     },
53160     move : function (e) {
53161         if (this.isMouseDown) {
53162             this.signatureTmp += 'L' + this.getCoords(e) + ' ';
53163             this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr( 'd', this.signatureTmp);
53164         }
53165         
53166         e.preventDefault();
53167     },
53168     up : function (e) {
53169         this.isMouseDown = false;
53170         var sp = this.signatureTmp.split(' ');
53171         
53172         if(sp.length > 1){
53173             if(!sp[sp.length-2].match(/^L/)){
53174                 sp.pop();
53175                 sp.pop();
53176                 sp.push("");
53177                 this.signatureTmp = sp.join(" ");
53178             }
53179         }
53180         if(this.getValue() != this.signatureTmp){
53181             this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#ffa');
53182             this.isConfirmed = false;
53183         }
53184         e.preventDefault();
53185     },
53186     
53187     /**
53188      * Protected method that will not generally be called directly. It
53189      * is called when the editor creates its toolbar. Override this method if you need to
53190      * add custom toolbar buttons.
53191      * @param {HtmlEditor} editor
53192      */
53193     createToolbar : function(editor){
53194          function btn(id, toggle, handler){
53195             var xid = fid + '-'+ id ;
53196             return {
53197                 id : xid,
53198                 cmd : id,
53199                 cls : 'x-btn-icon x-edit-'+id,
53200                 enableToggle:toggle !== false,
53201                 scope: editor, // was editor...
53202                 handler:handler||editor.relayBtnCmd,
53203                 clickEvent:'mousedown',
53204                 tooltip: etb.buttonTips[id] || undefined, ///tips ???
53205                 tabIndex:-1
53206             };
53207         }
53208         
53209         
53210         var tb = new Roo.Toolbar(editor.wrap.dom.firstChild);
53211         this.tb = tb;
53212         this.tb.add(
53213            {
53214                 cls : ' x-signature-btn x-signature-'+id,
53215                 scope: editor, // was editor...
53216                 handler: this.reset,
53217                 clickEvent:'mousedown',
53218                 text: this.labels.clear
53219             },
53220             {
53221                  xtype : 'Fill',
53222                  xns: Roo.Toolbar
53223             }, 
53224             {
53225                 cls : '  x-signature-btn x-signature-'+id,
53226                 scope: editor, // was editor...
53227                 handler: this.confirmHandler,
53228                 clickEvent:'mousedown',
53229                 text: this.labels.confirm
53230             }
53231         );
53232     
53233     },
53234     //public
53235     /**
53236      * when user is clicked confirm then show this image.....
53237      * 
53238      * @return {String} Image Data URI
53239      */
53240     getImageDataURI : function(){
53241         var svg = this.svgEl.dom.parentNode.innerHTML;
53242         var src = 'data:image/svg+xml;base64,'+window.btoa(svg);
53243         return src; 
53244     },
53245     /**
53246      * 
53247      * @return {Boolean} this.isConfirmed
53248      */
53249     getConfirmed : function(){
53250         return this.isConfirmed;
53251     },
53252     /**
53253      * 
53254      * @return {Number} this.width
53255      */
53256     getWidth : function(){
53257         return this.width;
53258     },
53259     /**
53260      * 
53261      * @return {Number} this.height
53262      */
53263     getHeight : function(){
53264         return this.height;
53265     },
53266     // private
53267     getSignature : function(){
53268         return this.signatureTmp;
53269     },
53270     // private
53271     reset : function(){
53272         this.signatureTmp = '';
53273         this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#ffa');
53274         this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr( 'd', '');
53275         this.isConfirmed = false;
53276         Roo.form.Signature.superclass.reset.call(this);
53277     },
53278     setSignature : function(s){
53279         this.signatureTmp = s;
53280         this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#ffa');
53281         this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr( 'd', s);
53282         this.setValue(s);
53283         this.isConfirmed = false;
53284         Roo.form.Signature.superclass.reset.call(this);
53285     }, 
53286     test : function(){
53287 //        Roo.log(this.signPanel.dom.contentWindow.up())
53288     },
53289     //private
53290     setConfirmed : function(){
53291         
53292         
53293         
53294 //        Roo.log(Roo.get(this.signPanel.dom.contentWindow.r).attr('fill', '#cfc'));
53295     },
53296     // private
53297     confirmHandler : function(){
53298         if(!this.getSignature()){
53299             return;
53300         }
53301         
53302         this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#cfc');
53303         this.setValue(this.getSignature());
53304         this.isConfirmed = true;
53305         
53306         this.fireEvent('confirm', this);
53307     },
53308     // private
53309     // Subclasses should provide the validation implementation by overriding this
53310     validateValue : function(value){
53311         if(this.allowBlank){
53312             return true;
53313         }
53314         
53315         if(this.isConfirmed){
53316             return true;
53317         }
53318         return false;
53319     }
53320 });/*
53321  * Based on:
53322  * Ext JS Library 1.1.1
53323  * Copyright(c) 2006-2007, Ext JS, LLC.
53324  *
53325  * Originally Released Under LGPL - original licence link has changed is not relivant.
53326  *
53327  * Fork - LGPL
53328  * <script type="text/javascript">
53329  */
53330  
53331
53332 /**
53333  * @class Roo.form.ComboBox
53334  * @extends Roo.form.TriggerField
53335  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
53336  * @constructor
53337  * Create a new ComboBox.
53338  * @param {Object} config Configuration options
53339  */
53340 Roo.form.Select = function(config){
53341     Roo.form.Select.superclass.constructor.call(this, config);
53342      
53343 };
53344
53345 Roo.extend(Roo.form.Select , Roo.form.ComboBox, {
53346     /**
53347      * @cfg {String/HTMLElement/Element} transform The id, DOM node or element of an existing select to convert to a ComboBox
53348      */
53349     /**
53350      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
53351      * rendering into an Roo.Editor, defaults to false)
53352      */
53353     /**
53354      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
53355      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
53356      */
53357     /**
53358      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
53359      */
53360     /**
53361      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
53362      * the dropdown list (defaults to undefined, with no header element)
53363      */
53364
53365      /**
53366      * @cfg {String/Roo.Template} tpl The template to use to render the output
53367      */
53368      
53369     // private
53370     defaultAutoCreate : {tag: "select"  },
53371     /**
53372      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
53373      */
53374     listWidth: undefined,
53375     /**
53376      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
53377      * mode = 'remote' or 'text' if mode = 'local')
53378      */
53379     displayField: undefined,
53380     /**
53381      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
53382      * mode = 'remote' or 'value' if mode = 'local'). 
53383      * Note: use of a valueField requires the user make a selection
53384      * in order for a value to be mapped.
53385      */
53386     valueField: undefined,
53387     
53388     
53389     /**
53390      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
53391      * field's data value (defaults to the underlying DOM element's name)
53392      */
53393     hiddenName: undefined,
53394     /**
53395      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
53396      */
53397     listClass: '',
53398     /**
53399      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
53400      */
53401     selectedClass: 'x-combo-selected',
53402     /**
53403      * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
53404      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-arrow-trigger'
53405      * which displays a downward arrow icon).
53406      */
53407     triggerClass : 'x-form-arrow-trigger',
53408     /**
53409      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
53410      */
53411     shadow:'sides',
53412     /**
53413      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
53414      * anchor positions (defaults to 'tl-bl')
53415      */
53416     listAlign: 'tl-bl?',
53417     /**
53418      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
53419      */
53420     maxHeight: 300,
53421     /**
53422      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
53423      * query specified by the allQuery config option (defaults to 'query')
53424      */
53425     triggerAction: 'query',
53426     /**
53427      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
53428      * (defaults to 4, does not apply if editable = false)
53429      */
53430     minChars : 4,
53431     /**
53432      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
53433      * delay (typeAheadDelay) if it matches a known value (defaults to false)
53434      */
53435     typeAhead: false,
53436     /**
53437      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
53438      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
53439      */
53440     queryDelay: 500,
53441     /**
53442      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
53443      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
53444      */
53445     pageSize: 0,
53446     /**
53447      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
53448      * when editable = true (defaults to false)
53449      */
53450     selectOnFocus:false,
53451     /**
53452      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
53453      */
53454     queryParam: 'query',
53455     /**
53456      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
53457      * when mode = 'remote' (defaults to 'Loading...')
53458      */
53459     loadingText: 'Loading...',
53460     /**
53461      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
53462      */
53463     resizable: false,
53464     /**
53465      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
53466      */
53467     handleHeight : 8,
53468     /**
53469      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
53470      * traditional select (defaults to true)
53471      */
53472     editable: true,
53473     /**
53474      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
53475      */
53476     allQuery: '',
53477     /**
53478      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
53479      */
53480     mode: 'remote',
53481     /**
53482      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
53483      * listWidth has a higher value)
53484      */
53485     minListWidth : 70,
53486     /**
53487      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
53488      * allow the user to set arbitrary text into the field (defaults to false)
53489      */
53490     forceSelection:false,
53491     /**
53492      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
53493      * if typeAhead = true (defaults to 250)
53494      */
53495     typeAheadDelay : 250,
53496     /**
53497      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
53498      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
53499      */
53500     valueNotFoundText : undefined,
53501     
53502     /**
53503      * @cfg {String} defaultValue The value displayed after loading the store.
53504      */
53505     defaultValue: '',
53506     
53507     /**
53508      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
53509      */
53510     blockFocus : false,
53511     
53512     /**
53513      * @cfg {Boolean} disableClear Disable showing of clear button.
53514      */
53515     disableClear : false,
53516     /**
53517      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
53518      */
53519     alwaysQuery : false,
53520     
53521     //private
53522     addicon : false,
53523     editicon: false,
53524     
53525     // element that contains real text value.. (when hidden is used..)
53526      
53527     // private
53528     onRender : function(ct, position){
53529         Roo.form.Field.prototype.onRender.call(this, ct, position);
53530         
53531         if(this.store){
53532             this.store.on('beforeload', this.onBeforeLoad, this);
53533             this.store.on('load', this.onLoad, this);
53534             this.store.on('loadexception', this.onLoadException, this);
53535             this.store.load({});
53536         }
53537         
53538         
53539         
53540     },
53541
53542     // private
53543     initEvents : function(){
53544         //Roo.form.ComboBox.superclass.initEvents.call(this);
53545  
53546     },
53547
53548     onDestroy : function(){
53549        
53550         if(this.store){
53551             this.store.un('beforeload', this.onBeforeLoad, this);
53552             this.store.un('load', this.onLoad, this);
53553             this.store.un('loadexception', this.onLoadException, this);
53554         }
53555         //Roo.form.ComboBox.superclass.onDestroy.call(this);
53556     },
53557
53558     // private
53559     fireKey : function(e){
53560         if(e.isNavKeyPress() && !this.list.isVisible()){
53561             this.fireEvent("specialkey", this, e);
53562         }
53563     },
53564
53565     // private
53566     onResize: function(w, h){
53567         
53568         return; 
53569     
53570         
53571     },
53572
53573     /**
53574      * Allow or prevent the user from directly editing the field text.  If false is passed,
53575      * the user will only be able to select from the items defined in the dropdown list.  This method
53576      * is the runtime equivalent of setting the 'editable' config option at config time.
53577      * @param {Boolean} value True to allow the user to directly edit the field text
53578      */
53579     setEditable : function(value){
53580          
53581     },
53582
53583     // private
53584     onBeforeLoad : function(){
53585         
53586         Roo.log("Select before load");
53587         return;
53588     
53589         this.innerList.update(this.loadingText ?
53590                '<div class="loading-indicator">'+this.loadingText+'</div>' : '');
53591         //this.restrictHeight();
53592         this.selectedIndex = -1;
53593     },
53594
53595     // private
53596     onLoad : function(){
53597
53598     
53599         var dom = this.el.dom;
53600         dom.innerHTML = '';
53601          var od = dom.ownerDocument;
53602          
53603         if (this.emptyText) {
53604             var op = od.createElement('option');
53605             op.setAttribute('value', '');
53606             op.innerHTML = String.format('{0}', this.emptyText);
53607             dom.appendChild(op);
53608         }
53609         if(this.store.getCount() > 0){
53610            
53611             var vf = this.valueField;
53612             var df = this.displayField;
53613             this.store.data.each(function(r) {
53614                 // which colmsn to use... testing - cdoe / title..
53615                 var op = od.createElement('option');
53616                 op.setAttribute('value', r.data[vf]);
53617                 op.innerHTML = String.format('{0}', r.data[df]);
53618                 dom.appendChild(op);
53619             });
53620             if (typeof(this.defaultValue != 'undefined')) {
53621                 this.setValue(this.defaultValue);
53622             }
53623             
53624              
53625         }else{
53626             //this.onEmptyResults();
53627         }
53628         //this.el.focus();
53629     },
53630     // private
53631     onLoadException : function()
53632     {
53633         dom.innerHTML = '';
53634             
53635         Roo.log("Select on load exception");
53636         return;
53637     
53638         this.collapse();
53639         Roo.log(this.store.reader.jsonData);
53640         if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
53641             Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
53642         }
53643         
53644         
53645     },
53646     // private
53647     onTypeAhead : function(){
53648          
53649     },
53650
53651     // private
53652     onSelect : function(record, index){
53653         Roo.log('on select?');
53654         return;
53655         if(this.fireEvent('beforeselect', this, record, index) !== false){
53656             this.setFromData(index > -1 ? record.data : false);
53657             this.collapse();
53658             this.fireEvent('select', this, record, index);
53659         }
53660     },
53661
53662     /**
53663      * Returns the currently selected field value or empty string if no value is set.
53664      * @return {String} value The selected value
53665      */
53666     getValue : function(){
53667         var dom = this.el.dom;
53668         this.value = dom.options[dom.selectedIndex].value;
53669         return this.value;
53670         
53671     },
53672
53673     /**
53674      * Clears any text/value currently set in the field
53675      */
53676     clearValue : function(){
53677         this.value = '';
53678         this.el.dom.selectedIndex = this.emptyText ? 0 : -1;
53679         
53680     },
53681
53682     /**
53683      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
53684      * will be displayed in the field.  If the value does not match the data value of an existing item,
53685      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
53686      * Otherwise the field will be blank (although the value will still be set).
53687      * @param {String} value The value to match
53688      */
53689     setValue : function(v){
53690         var d = this.el.dom;
53691         for (var i =0; i < d.options.length;i++) {
53692             if (v == d.options[i].value) {
53693                 d.selectedIndex = i;
53694                 this.value = v;
53695                 return;
53696             }
53697         }
53698         this.clearValue();
53699     },
53700     /**
53701      * @property {Object} the last set data for the element
53702      */
53703     
53704     lastData : false,
53705     /**
53706      * Sets the value of the field based on a object which is related to the record format for the store.
53707      * @param {Object} value the value to set as. or false on reset?
53708      */
53709     setFromData : function(o){
53710         Roo.log('setfrom data?');
53711          
53712         
53713         
53714     },
53715     // private
53716     reset : function(){
53717         this.clearValue();
53718     },
53719     // private
53720     findRecord : function(prop, value){
53721         
53722         return false;
53723     
53724         var record;
53725         if(this.store.getCount() > 0){
53726             this.store.each(function(r){
53727                 if(r.data[prop] == value){
53728                     record = r;
53729                     return false;
53730                 }
53731                 return true;
53732             });
53733         }
53734         return record;
53735     },
53736     
53737     getName: function()
53738     {
53739         // returns hidden if it's set..
53740         if (!this.rendered) {return ''};
53741         return !this.hiddenName && this.el.dom.name  ? this.el.dom.name : (this.hiddenName || '');
53742         
53743     },
53744      
53745
53746     
53747
53748     // private
53749     onEmptyResults : function(){
53750         Roo.log('empty results');
53751         //this.collapse();
53752     },
53753
53754     /**
53755      * Returns true if the dropdown list is expanded, else false.
53756      */
53757     isExpanded : function(){
53758         return false;
53759     },
53760
53761     /**
53762      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
53763      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
53764      * @param {String} value The data value of the item to select
53765      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
53766      * selected item if it is not currently in view (defaults to true)
53767      * @return {Boolean} True if the value matched an item in the list, else false
53768      */
53769     selectByValue : function(v, scrollIntoView){
53770         Roo.log('select By Value');
53771         return false;
53772     
53773         if(v !== undefined && v !== null){
53774             var r = this.findRecord(this.valueField || this.displayField, v);
53775             if(r){
53776                 this.select(this.store.indexOf(r), scrollIntoView);
53777                 return true;
53778             }
53779         }
53780         return false;
53781     },
53782
53783     /**
53784      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
53785      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
53786      * @param {Number} index The zero-based index of the list item to select
53787      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
53788      * selected item if it is not currently in view (defaults to true)
53789      */
53790     select : function(index, scrollIntoView){
53791         Roo.log('select ');
53792         return  ;
53793         
53794         this.selectedIndex = index;
53795         this.view.select(index);
53796         if(scrollIntoView !== false){
53797             var el = this.view.getNode(index);
53798             if(el){
53799                 this.innerList.scrollChildIntoView(el, false);
53800             }
53801         }
53802     },
53803
53804       
53805
53806     // private
53807     validateBlur : function(){
53808         
53809         return;
53810         
53811     },
53812
53813     // private
53814     initQuery : function(){
53815         this.doQuery(this.getRawValue());
53816     },
53817
53818     // private
53819     doForce : function(){
53820         if(this.el.dom.value.length > 0){
53821             this.el.dom.value =
53822                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
53823              
53824         }
53825     },
53826
53827     /**
53828      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
53829      * query allowing the query action to be canceled if needed.
53830      * @param {String} query The SQL query to execute
53831      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
53832      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
53833      * saved in the current store (defaults to false)
53834      */
53835     doQuery : function(q, forceAll){
53836         
53837         Roo.log('doQuery?');
53838         if(q === undefined || q === null){
53839             q = '';
53840         }
53841         var qe = {
53842             query: q,
53843             forceAll: forceAll,
53844             combo: this,
53845             cancel:false
53846         };
53847         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
53848             return false;
53849         }
53850         q = qe.query;
53851         forceAll = qe.forceAll;
53852         if(forceAll === true || (q.length >= this.minChars)){
53853             if(this.lastQuery != q || this.alwaysQuery){
53854                 this.lastQuery = q;
53855                 if(this.mode == 'local'){
53856                     this.selectedIndex = -1;
53857                     if(forceAll){
53858                         this.store.clearFilter();
53859                     }else{
53860                         this.store.filter(this.displayField, q);
53861                     }
53862                     this.onLoad();
53863                 }else{
53864                     this.store.baseParams[this.queryParam] = q;
53865                     this.store.load({
53866                         params: this.getParams(q)
53867                     });
53868                     this.expand();
53869                 }
53870             }else{
53871                 this.selectedIndex = -1;
53872                 this.onLoad();   
53873             }
53874         }
53875     },
53876
53877     // private
53878     getParams : function(q){
53879         var p = {};
53880         //p[this.queryParam] = q;
53881         if(this.pageSize){
53882             p.start = 0;
53883             p.limit = this.pageSize;
53884         }
53885         return p;
53886     },
53887
53888     /**
53889      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
53890      */
53891     collapse : function(){
53892         
53893     },
53894
53895     // private
53896     collapseIf : function(e){
53897         
53898     },
53899
53900     /**
53901      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
53902      */
53903     expand : function(){
53904         
53905     } ,
53906
53907     // private
53908      
53909
53910     /** 
53911     * @cfg {Boolean} grow 
53912     * @hide 
53913     */
53914     /** 
53915     * @cfg {Number} growMin 
53916     * @hide 
53917     */
53918     /** 
53919     * @cfg {Number} growMax 
53920     * @hide 
53921     */
53922     /**
53923      * @hide
53924      * @method autoSize
53925      */
53926     
53927     setWidth : function()
53928     {
53929         
53930     },
53931     getResizeEl : function(){
53932         return this.el;
53933     }
53934 });//<script type="text/javasscript">
53935  
53936
53937 /**
53938  * @class Roo.DDView
53939  * A DnD enabled version of Roo.View.
53940  * @param {Element/String} container The Element in which to create the View.
53941  * @param {String} tpl The template string used to create the markup for each element of the View
53942  * @param {Object} config The configuration properties. These include all the config options of
53943  * {@link Roo.View} plus some specific to this class.<br>
53944  * <p>
53945  * Drag/drop is implemented by adding {@link Roo.data.Record}s to the target DDView. If copying is
53946  * not being performed, the original {@link Roo.data.Record} is removed from the source DDView.<br>
53947  * <p>
53948  * The following extra CSS rules are needed to provide insertion point highlighting:<pre><code>
53949 .x-view-drag-insert-above {
53950         border-top:1px dotted #3366cc;
53951 }
53952 .x-view-drag-insert-below {
53953         border-bottom:1px dotted #3366cc;
53954 }
53955 </code></pre>
53956  * 
53957  */
53958  
53959 Roo.DDView = function(container, tpl, config) {
53960     Roo.DDView.superclass.constructor.apply(this, arguments);
53961     this.getEl().setStyle("outline", "0px none");
53962     this.getEl().unselectable();
53963     if (this.dragGroup) {
53964         this.setDraggable(this.dragGroup.split(","));
53965     }
53966     if (this.dropGroup) {
53967         this.setDroppable(this.dropGroup.split(","));
53968     }
53969     if (this.deletable) {
53970         this.setDeletable();
53971     }
53972     this.isDirtyFlag = false;
53973         this.addEvents({
53974                 "drop" : true
53975         });
53976 };
53977
53978 Roo.extend(Roo.DDView, Roo.View, {
53979 /**     @cfg {String/Array} dragGroup The ddgroup name(s) for the View's DragZone. */
53980 /**     @cfg {String/Array} dropGroup The ddgroup name(s) for the View's DropZone. */
53981 /**     @cfg {Boolean} copy Causes drag operations to copy nodes rather than move. */
53982 /**     @cfg {Boolean} allowCopy Causes ctrl/drag operations to copy nodes rather than move. */
53983
53984         isFormField: true,
53985
53986         reset: Roo.emptyFn,
53987         
53988         clearInvalid: Roo.form.Field.prototype.clearInvalid,
53989
53990         validate: function() {
53991                 return true;
53992         },
53993         
53994         destroy: function() {
53995                 this.purgeListeners();
53996                 this.getEl.removeAllListeners();
53997                 this.getEl().remove();
53998                 if (this.dragZone) {
53999                         if (this.dragZone.destroy) {
54000                                 this.dragZone.destroy();
54001                         }
54002                 }
54003                 if (this.dropZone) {
54004                         if (this.dropZone.destroy) {
54005                                 this.dropZone.destroy();
54006                         }
54007                 }
54008         },
54009
54010 /**     Allows this class to be an Roo.form.Field so it can be found using {@link Roo.form.BasicForm#findField}. */
54011         getName: function() {
54012                 return this.name;
54013         },
54014
54015 /**     Loads the View from a JSON string representing the Records to put into the Store. */
54016         setValue: function(v) {
54017                 if (!this.store) {
54018                         throw "DDView.setValue(). DDView must be constructed with a valid Store";
54019                 }
54020                 var data = {};
54021                 data[this.store.reader.meta.root] = v ? [].concat(v) : [];
54022                 this.store.proxy = new Roo.data.MemoryProxy(data);
54023                 this.store.load();
54024         },
54025
54026 /**     @return {String} a parenthesised list of the ids of the Records in the View. */
54027         getValue: function() {
54028                 var result = '(';
54029                 this.store.each(function(rec) {
54030                         result += rec.id + ',';
54031                 });
54032                 return result.substr(0, result.length - 1) + ')';
54033         },
54034         
54035         getIds: function() {
54036                 var i = 0, result = new Array(this.store.getCount());
54037                 this.store.each(function(rec) {
54038                         result[i++] = rec.id;
54039                 });
54040                 return result;
54041         },
54042         
54043         isDirty: function() {
54044                 return this.isDirtyFlag;
54045         },
54046
54047 /**
54048  *      Part of the Roo.dd.DropZone interface. If no target node is found, the
54049  *      whole Element becomes the target, and this causes the drop gesture to append.
54050  */
54051     getTargetFromEvent : function(e) {
54052                 var target = e.getTarget();
54053                 while ((target !== null) && (target.parentNode != this.el.dom)) {
54054                 target = target.parentNode;
54055                 }
54056                 if (!target) {
54057                         target = this.el.dom.lastChild || this.el.dom;
54058                 }
54059                 return target;
54060     },
54061
54062 /**
54063  *      Create the drag data which consists of an object which has the property "ddel" as
54064  *      the drag proxy element. 
54065  */
54066     getDragData : function(e) {
54067         var target = this.findItemFromChild(e.getTarget());
54068                 if(target) {
54069                         this.handleSelection(e);
54070                         var selNodes = this.getSelectedNodes();
54071             var dragData = {
54072                 source: this,
54073                 copy: this.copy || (this.allowCopy && e.ctrlKey),
54074                 nodes: selNodes,
54075                 records: []
54076                         };
54077                         var selectedIndices = this.getSelectedIndexes();
54078                         for (var i = 0; i < selectedIndices.length; i++) {
54079                                 dragData.records.push(this.store.getAt(selectedIndices[i]));
54080                         }
54081                         if (selNodes.length == 1) {
54082                                 dragData.ddel = target.cloneNode(true); // the div element
54083                         } else {
54084                                 var div = document.createElement('div'); // create the multi element drag "ghost"
54085                                 div.className = 'multi-proxy';
54086                                 for (var i = 0, len = selNodes.length; i < len; i++) {
54087                                         div.appendChild(selNodes[i].cloneNode(true));
54088                                 }
54089                                 dragData.ddel = div;
54090                         }
54091             //console.log(dragData)
54092             //console.log(dragData.ddel.innerHTML)
54093                         return dragData;
54094                 }
54095         //console.log('nodragData')
54096                 return false;
54097     },
54098     
54099 /**     Specify to which ddGroup items in this DDView may be dragged. */
54100     setDraggable: function(ddGroup) {
54101         if (ddGroup instanceof Array) {
54102                 Roo.each(ddGroup, this.setDraggable, this);
54103                 return;
54104         }
54105         if (this.dragZone) {
54106                 this.dragZone.addToGroup(ddGroup);
54107         } else {
54108                         this.dragZone = new Roo.dd.DragZone(this.getEl(), {
54109                                 containerScroll: true,
54110                                 ddGroup: ddGroup 
54111
54112                         });
54113 //                      Draggability implies selection. DragZone's mousedown selects the element.
54114                         if (!this.multiSelect) { this.singleSelect = true; }
54115
54116 //                      Wire the DragZone's handlers up to methods in *this*
54117                         this.dragZone.getDragData = this.getDragData.createDelegate(this);
54118                 }
54119     },
54120
54121 /**     Specify from which ddGroup this DDView accepts drops. */
54122     setDroppable: function(ddGroup) {
54123         if (ddGroup instanceof Array) {
54124                 Roo.each(ddGroup, this.setDroppable, this);
54125                 return;
54126         }
54127         if (this.dropZone) {
54128                 this.dropZone.addToGroup(ddGroup);
54129         } else {
54130                         this.dropZone = new Roo.dd.DropZone(this.getEl(), {
54131                                 containerScroll: true,
54132                                 ddGroup: ddGroup
54133                         });
54134
54135 //                      Wire the DropZone's handlers up to methods in *this*
54136                         this.dropZone.getTargetFromEvent = this.getTargetFromEvent.createDelegate(this);
54137                         this.dropZone.onNodeEnter = this.onNodeEnter.createDelegate(this);
54138                         this.dropZone.onNodeOver = this.onNodeOver.createDelegate(this);
54139                         this.dropZone.onNodeOut = this.onNodeOut.createDelegate(this);
54140                         this.dropZone.onNodeDrop = this.onNodeDrop.createDelegate(this);
54141                 }
54142     },
54143
54144 /**     Decide whether to drop above or below a View node. */
54145     getDropPoint : function(e, n, dd){
54146         if (n == this.el.dom) { return "above"; }
54147                 var t = Roo.lib.Dom.getY(n), b = t + n.offsetHeight;
54148                 var c = t + (b - t) / 2;
54149                 var y = Roo.lib.Event.getPageY(e);
54150                 if(y <= c) {
54151                         return "above";
54152                 }else{
54153                         return "below";
54154                 }
54155     },
54156
54157     onNodeEnter : function(n, dd, e, data){
54158                 return false;
54159     },
54160     
54161     onNodeOver : function(n, dd, e, data){
54162                 var pt = this.getDropPoint(e, n, dd);
54163                 // set the insert point style on the target node
54164                 var dragElClass = this.dropNotAllowed;
54165                 if (pt) {
54166                         var targetElClass;
54167                         if (pt == "above"){
54168                                 dragElClass = n.previousSibling ? "x-tree-drop-ok-between" : "x-tree-drop-ok-above";
54169                                 targetElClass = "x-view-drag-insert-above";
54170                         } else {
54171                                 dragElClass = n.nextSibling ? "x-tree-drop-ok-between" : "x-tree-drop-ok-below";
54172                                 targetElClass = "x-view-drag-insert-below";
54173                         }
54174                         if (this.lastInsertClass != targetElClass){
54175                                 Roo.fly(n).replaceClass(this.lastInsertClass, targetElClass);
54176                                 this.lastInsertClass = targetElClass;
54177                         }
54178                 }
54179                 return dragElClass;
54180         },
54181
54182     onNodeOut : function(n, dd, e, data){
54183                 this.removeDropIndicators(n);
54184     },
54185
54186     onNodeDrop : function(n, dd, e, data){
54187         if (this.fireEvent("drop", this, n, dd, e, data) === false) {
54188                 return false;
54189         }
54190         var pt = this.getDropPoint(e, n, dd);
54191                 var insertAt = (n == this.el.dom) ? this.nodes.length : n.nodeIndex;
54192                 if (pt == "below") { insertAt++; }
54193                 for (var i = 0; i < data.records.length; i++) {
54194                         var r = data.records[i];
54195                         var dup = this.store.getById(r.id);
54196                         if (dup && (dd != this.dragZone)) {
54197                                 Roo.fly(this.getNode(this.store.indexOf(dup))).frame("red", 1);
54198                         } else {
54199                                 if (data.copy) {
54200                                         this.store.insert(insertAt++, r.copy());
54201                                 } else {
54202                                         data.source.isDirtyFlag = true;
54203                                         r.store.remove(r);
54204                                         this.store.insert(insertAt++, r);
54205                                 }
54206                                 this.isDirtyFlag = true;
54207                         }
54208                 }
54209                 this.dragZone.cachedTarget = null;
54210                 return true;
54211     },
54212
54213     removeDropIndicators : function(n){
54214                 if(n){
54215                         Roo.fly(n).removeClass([
54216                                 "x-view-drag-insert-above",
54217                                 "x-view-drag-insert-below"]);
54218                         this.lastInsertClass = "_noclass";
54219                 }
54220     },
54221
54222 /**
54223  *      Utility method. Add a delete option to the DDView's context menu.
54224  *      @param {String} imageUrl The URL of the "delete" icon image.
54225  */
54226         setDeletable: function(imageUrl) {
54227                 if (!this.singleSelect && !this.multiSelect) {
54228                         this.singleSelect = true;
54229                 }
54230                 var c = this.getContextMenu();
54231                 this.contextMenu.on("itemclick", function(item) {
54232                         switch (item.id) {
54233                                 case "delete":
54234                                         this.remove(this.getSelectedIndexes());
54235                                         break;
54236                         }
54237                 }, this);
54238                 this.contextMenu.add({
54239                         icon: imageUrl,
54240                         id: "delete",
54241                         text: 'Delete'
54242                 });
54243         },
54244         
54245 /**     Return the context menu for this DDView. */
54246         getContextMenu: function() {
54247                 if (!this.contextMenu) {
54248 //                      Create the View's context menu
54249                         this.contextMenu = new Roo.menu.Menu({
54250                                 id: this.id + "-contextmenu"
54251                         });
54252                         this.el.on("contextmenu", this.showContextMenu, this);
54253                 }
54254                 return this.contextMenu;
54255         },
54256         
54257         disableContextMenu: function() {
54258                 if (this.contextMenu) {
54259                         this.el.un("contextmenu", this.showContextMenu, this);
54260                 }
54261         },
54262
54263         showContextMenu: function(e, item) {
54264         item = this.findItemFromChild(e.getTarget());
54265                 if (item) {
54266                         e.stopEvent();
54267                         this.select(this.getNode(item), this.multiSelect && e.ctrlKey, true);
54268                         this.contextMenu.showAt(e.getXY());
54269             }
54270     },
54271
54272 /**
54273  *      Remove {@link Roo.data.Record}s at the specified indices.
54274  *      @param {Array/Number} selectedIndices The index (or Array of indices) of Records to remove.
54275  */
54276     remove: function(selectedIndices) {
54277                 selectedIndices = [].concat(selectedIndices);
54278                 for (var i = 0; i < selectedIndices.length; i++) {
54279                         var rec = this.store.getAt(selectedIndices[i]);
54280                         this.store.remove(rec);
54281                 }
54282     },
54283
54284 /**
54285  *      Double click fires the event, but also, if this is draggable, and there is only one other
54286  *      related DropZone, it transfers the selected node.
54287  */
54288     onDblClick : function(e){
54289         var item = this.findItemFromChild(e.getTarget());
54290         if(item){
54291             if (this.fireEvent("dblclick", this, this.indexOf(item), item, e) === false) {
54292                 return false;
54293             }
54294             if (this.dragGroup) {
54295                     var targets = Roo.dd.DragDropMgr.getRelated(this.dragZone, true);
54296                     while (targets.indexOf(this.dropZone) > -1) {
54297                             targets.remove(this.dropZone);
54298                                 }
54299                     if (targets.length == 1) {
54300                                         this.dragZone.cachedTarget = null;
54301                         var el = Roo.get(targets[0].getEl());
54302                         var box = el.getBox(true);
54303                         targets[0].onNodeDrop(el.dom, {
54304                                 target: el.dom,
54305                                 xy: [box.x, box.y + box.height - 1]
54306                         }, null, this.getDragData(e));
54307                     }
54308                 }
54309         }
54310     },
54311     
54312     handleSelection: function(e) {
54313                 this.dragZone.cachedTarget = null;
54314         var item = this.findItemFromChild(e.getTarget());
54315         if (!item) {
54316                 this.clearSelections(true);
54317                 return;
54318         }
54319                 if (item && (this.multiSelect || this.singleSelect)){
54320                         if(this.multiSelect && e.shiftKey && (!e.ctrlKey) && this.lastSelection){
54321                                 this.select(this.getNodes(this.indexOf(this.lastSelection), item.nodeIndex), false);
54322                         }else if (this.isSelected(this.getNode(item)) && e.ctrlKey){
54323                                 this.unselect(item);
54324                         } else {
54325                                 this.select(item, this.multiSelect && e.ctrlKey);
54326                                 this.lastSelection = item;
54327                         }
54328                 }
54329     },
54330
54331     onItemClick : function(item, index, e){
54332                 if(this.fireEvent("beforeclick", this, index, item, e) === false){
54333                         return false;
54334                 }
54335                 return true;
54336     },
54337
54338     unselect : function(nodeInfo, suppressEvent){
54339                 var node = this.getNode(nodeInfo);
54340                 if(node && this.isSelected(node)){
54341                         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
54342                                 Roo.fly(node).removeClass(this.selectedClass);
54343                                 this.selections.remove(node);
54344                                 if(!suppressEvent){
54345                                         this.fireEvent("selectionchange", this, this.selections);
54346                                 }
54347                         }
54348                 }
54349     }
54350 });
54351 /*
54352  * Based on:
54353  * Ext JS Library 1.1.1
54354  * Copyright(c) 2006-2007, Ext JS, LLC.
54355  *
54356  * Originally Released Under LGPL - original licence link has changed is not relivant.
54357  *
54358  * Fork - LGPL
54359  * <script type="text/javascript">
54360  */
54361  
54362 /**
54363  * @class Roo.LayoutManager
54364  * @extends Roo.util.Observable
54365  * Base class for layout managers.
54366  */
54367 Roo.LayoutManager = function(container, config){
54368     Roo.LayoutManager.superclass.constructor.call(this);
54369     this.el = Roo.get(container);
54370     // ie scrollbar fix
54371     if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
54372         document.body.scroll = "no";
54373     }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
54374         this.el.position('relative');
54375     }
54376     this.id = this.el.id;
54377     this.el.addClass("x-layout-container");
54378     /** false to disable window resize monitoring @type Boolean */
54379     this.monitorWindowResize = true;
54380     this.regions = {};
54381     this.addEvents({
54382         /**
54383          * @event layout
54384          * Fires when a layout is performed. 
54385          * @param {Roo.LayoutManager} this
54386          */
54387         "layout" : true,
54388         /**
54389          * @event regionresized
54390          * Fires when the user resizes a region. 
54391          * @param {Roo.LayoutRegion} region The resized region
54392          * @param {Number} newSize The new size (width for east/west, height for north/south)
54393          */
54394         "regionresized" : true,
54395         /**
54396          * @event regioncollapsed
54397          * Fires when a region is collapsed. 
54398          * @param {Roo.LayoutRegion} region The collapsed region
54399          */
54400         "regioncollapsed" : true,
54401         /**
54402          * @event regionexpanded
54403          * Fires when a region is expanded.  
54404          * @param {Roo.LayoutRegion} region The expanded region
54405          */
54406         "regionexpanded" : true
54407     });
54408     this.updating = false;
54409     Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
54410 };
54411
54412 Roo.extend(Roo.LayoutManager, Roo.util.Observable, {
54413     /**
54414      * Returns true if this layout is currently being updated
54415      * @return {Boolean}
54416      */
54417     isUpdating : function(){
54418         return this.updating; 
54419     },
54420     
54421     /**
54422      * Suspend the LayoutManager from doing auto-layouts while
54423      * making multiple add or remove calls
54424      */
54425     beginUpdate : function(){
54426         this.updating = true;    
54427     },
54428     
54429     /**
54430      * Restore auto-layouts and optionally disable the manager from performing a layout
54431      * @param {Boolean} noLayout true to disable a layout update 
54432      */
54433     endUpdate : function(noLayout){
54434         this.updating = false;
54435         if(!noLayout){
54436             this.layout();
54437         }    
54438     },
54439     
54440     layout: function(){
54441         
54442     },
54443     
54444     onRegionResized : function(region, newSize){
54445         this.fireEvent("regionresized", region, newSize);
54446         this.layout();
54447     },
54448     
54449     onRegionCollapsed : function(region){
54450         this.fireEvent("regioncollapsed", region);
54451     },
54452     
54453     onRegionExpanded : function(region){
54454         this.fireEvent("regionexpanded", region);
54455     },
54456         
54457     /**
54458      * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
54459      * performs box-model adjustments.
54460      * @return {Object} The size as an object {width: (the width), height: (the height)}
54461      */
54462     getViewSize : function(){
54463         var size;
54464         if(this.el.dom != document.body){
54465             size = this.el.getSize();
54466         }else{
54467             size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
54468         }
54469         size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
54470         size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
54471         return size;
54472     },
54473     
54474     /**
54475      * Returns the Element this layout is bound to.
54476      * @return {Roo.Element}
54477      */
54478     getEl : function(){
54479         return this.el;
54480     },
54481     
54482     /**
54483      * Returns the specified region.
54484      * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
54485      * @return {Roo.LayoutRegion}
54486      */
54487     getRegion : function(target){
54488         return this.regions[target.toLowerCase()];
54489     },
54490     
54491     onWindowResize : function(){
54492         if(this.monitorWindowResize){
54493             this.layout();
54494         }
54495     }
54496 });/*
54497  * Based on:
54498  * Ext JS Library 1.1.1
54499  * Copyright(c) 2006-2007, Ext JS, LLC.
54500  *
54501  * Originally Released Under LGPL - original licence link has changed is not relivant.
54502  *
54503  * Fork - LGPL
54504  * <script type="text/javascript">
54505  */
54506 /**
54507  * @class Roo.BorderLayout
54508  * @extends Roo.LayoutManager
54509  * @children Roo.ContentPanel
54510  * This class represents a common layout manager used in desktop applications. For screenshots and more details,
54511  * please see: <br><br>
54512  * <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>
54513  * <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>
54514  * Example:
54515  <pre><code>
54516  var layout = new Roo.BorderLayout(document.body, {
54517     north: {
54518         initialSize: 25,
54519         titlebar: false
54520     },
54521     west: {
54522         split:true,
54523         initialSize: 200,
54524         minSize: 175,
54525         maxSize: 400,
54526         titlebar: true,
54527         collapsible: true
54528     },
54529     east: {
54530         split:true,
54531         initialSize: 202,
54532         minSize: 175,
54533         maxSize: 400,
54534         titlebar: true,
54535         collapsible: true
54536     },
54537     south: {
54538         split:true,
54539         initialSize: 100,
54540         minSize: 100,
54541         maxSize: 200,
54542         titlebar: true,
54543         collapsible: true
54544     },
54545     center: {
54546         titlebar: true,
54547         autoScroll:true,
54548         resizeTabs: true,
54549         minTabWidth: 50,
54550         preferredTabWidth: 150
54551     }
54552 });
54553
54554 // shorthand
54555 var CP = Roo.ContentPanel;
54556
54557 layout.beginUpdate();
54558 layout.add("north", new CP("north", "North"));
54559 layout.add("south", new CP("south", {title: "South", closable: true}));
54560 layout.add("west", new CP("west", {title: "West"}));
54561 layout.add("east", new CP("autoTabs", {title: "Auto Tabs", closable: true}));
54562 layout.add("center", new CP("center1", {title: "Close Me", closable: true}));
54563 layout.add("center", new CP("center2", {title: "Center Panel", closable: false}));
54564 layout.getRegion("center").showPanel("center1");
54565 layout.endUpdate();
54566 </code></pre>
54567
54568 <b>The container the layout is rendered into can be either the body element or any other element.
54569 If it is not the body element, the container needs to either be an absolute positioned element,
54570 or you will need to add "position:relative" to the css of the container.  You will also need to specify
54571 the container size if it is not the body element.</b>
54572
54573 * @constructor
54574 * Create a new BorderLayout
54575 * @param {String/HTMLElement/Element} container The container this layout is bound to
54576 * @param {Object} config Configuration options
54577  */
54578 Roo.BorderLayout = function(container, config){
54579     config = config || {};
54580     Roo.BorderLayout.superclass.constructor.call(this, container, config);
54581     this.factory = config.factory || Roo.BorderLayout.RegionFactory;
54582     for(var i = 0, len = this.factory.validRegions.length; i < len; i++) {
54583         var target = this.factory.validRegions[i];
54584         if(config[target]){
54585             this.addRegion(target, config[target]);
54586         }
54587     }
54588 };
54589
54590 Roo.extend(Roo.BorderLayout, Roo.LayoutManager, {
54591         
54592         /**
54593          * @cfg {Roo.LayoutRegion} east
54594          */
54595         /**
54596          * @cfg {Roo.LayoutRegion} west
54597          */
54598         /**
54599          * @cfg {Roo.LayoutRegion} north
54600          */
54601         /**
54602          * @cfg {Roo.LayoutRegion} south
54603          */
54604         /**
54605          * @cfg {Roo.LayoutRegion} center
54606          */
54607     /**
54608      * Creates and adds a new region if it doesn't already exist.
54609      * @param {String} target The target region key (north, south, east, west or center).
54610      * @param {Object} config The regions config object
54611      * @return {BorderLayoutRegion} The new region
54612      */
54613     addRegion : function(target, config){
54614         if(!this.regions[target]){
54615             var r = this.factory.create(target, this, config);
54616             this.bindRegion(target, r);
54617         }
54618         return this.regions[target];
54619     },
54620
54621     // private (kinda)
54622     bindRegion : function(name, r){
54623         this.regions[name] = r;
54624         r.on("visibilitychange", this.layout, this);
54625         r.on("paneladded", this.layout, this);
54626         r.on("panelremoved", this.layout, this);
54627         r.on("invalidated", this.layout, this);
54628         r.on("resized", this.onRegionResized, this);
54629         r.on("collapsed", this.onRegionCollapsed, this);
54630         r.on("expanded", this.onRegionExpanded, this);
54631     },
54632
54633     /**
54634      * Performs a layout update.
54635      */
54636     layout : function(){
54637         if(this.updating) {
54638             return;
54639         }
54640         var size = this.getViewSize();
54641         var w = size.width;
54642         var h = size.height;
54643         var centerW = w;
54644         var centerH = h;
54645         var centerY = 0;
54646         var centerX = 0;
54647         //var x = 0, y = 0;
54648
54649         var rs = this.regions;
54650         var north = rs["north"];
54651         var south = rs["south"]; 
54652         var west = rs["west"];
54653         var east = rs["east"];
54654         var center = rs["center"];
54655         //if(this.hideOnLayout){ // not supported anymore
54656             //c.el.setStyle("display", "none");
54657         //}
54658         if(north && north.isVisible()){
54659             var b = north.getBox();
54660             var m = north.getMargins();
54661             b.width = w - (m.left+m.right);
54662             b.x = m.left;
54663             b.y = m.top;
54664             centerY = b.height + b.y + m.bottom;
54665             centerH -= centerY;
54666             north.updateBox(this.safeBox(b));
54667         }
54668         if(south && south.isVisible()){
54669             var b = south.getBox();
54670             var m = south.getMargins();
54671             b.width = w - (m.left+m.right);
54672             b.x = m.left;
54673             var totalHeight = (b.height + m.top + m.bottom);
54674             b.y = h - totalHeight + m.top;
54675             centerH -= totalHeight;
54676             south.updateBox(this.safeBox(b));
54677         }
54678         if(west && west.isVisible()){
54679             var b = west.getBox();
54680             var m = west.getMargins();
54681             b.height = centerH - (m.top+m.bottom);
54682             b.x = m.left;
54683             b.y = centerY + m.top;
54684             var totalWidth = (b.width + m.left + m.right);
54685             centerX += totalWidth;
54686             centerW -= totalWidth;
54687             west.updateBox(this.safeBox(b));
54688         }
54689         if(east && east.isVisible()){
54690             var b = east.getBox();
54691             var m = east.getMargins();
54692             b.height = centerH - (m.top+m.bottom);
54693             var totalWidth = (b.width + m.left + m.right);
54694             b.x = w - totalWidth + m.left;
54695             b.y = centerY + m.top;
54696             centerW -= totalWidth;
54697             east.updateBox(this.safeBox(b));
54698         }
54699         if(center){
54700             var m = center.getMargins();
54701             var centerBox = {
54702                 x: centerX + m.left,
54703                 y: centerY + m.top,
54704                 width: centerW - (m.left+m.right),
54705                 height: centerH - (m.top+m.bottom)
54706             };
54707             //if(this.hideOnLayout){
54708                 //center.el.setStyle("display", "block");
54709             //}
54710             center.updateBox(this.safeBox(centerBox));
54711         }
54712         this.el.repaint();
54713         this.fireEvent("layout", this);
54714     },
54715
54716     // private
54717     safeBox : function(box){
54718         box.width = Math.max(0, box.width);
54719         box.height = Math.max(0, box.height);
54720         return box;
54721     },
54722
54723     /**
54724      * Adds a ContentPanel (or subclass) to this layout.
54725      * @param {String} target The target region key (north, south, east, west or center).
54726      * @param {Roo.ContentPanel} panel The panel to add
54727      * @return {Roo.ContentPanel} The added panel
54728      */
54729     add : function(target, panel){
54730          
54731         target = target.toLowerCase();
54732         return this.regions[target].add(panel);
54733     },
54734
54735     /**
54736      * Remove a ContentPanel (or subclass) to this layout.
54737      * @param {String} target The target region key (north, south, east, west or center).
54738      * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
54739      * @return {Roo.ContentPanel} The removed panel
54740      */
54741     remove : function(target, panel){
54742         target = target.toLowerCase();
54743         return this.regions[target].remove(panel);
54744     },
54745
54746     /**
54747      * Searches all regions for a panel with the specified id
54748      * @param {String} panelId
54749      * @return {Roo.ContentPanel} The panel or null if it wasn't found
54750      */
54751     findPanel : function(panelId){
54752         var rs = this.regions;
54753         for(var target in rs){
54754             if(typeof rs[target] != "function"){
54755                 var p = rs[target].getPanel(panelId);
54756                 if(p){
54757                     return p;
54758                 }
54759             }
54760         }
54761         return null;
54762     },
54763
54764     /**
54765      * Searches all regions for a panel with the specified id and activates (shows) it.
54766      * @param {String/ContentPanel} panelId The panels id or the panel itself
54767      * @return {Roo.ContentPanel} The shown panel or null
54768      */
54769     showPanel : function(panelId) {
54770       var rs = this.regions;
54771       for(var target in rs){
54772          var r = rs[target];
54773          if(typeof r != "function"){
54774             if(r.hasPanel(panelId)){
54775                return r.showPanel(panelId);
54776             }
54777          }
54778       }
54779       return null;
54780    },
54781
54782    /**
54783      * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
54784      * @param {Roo.state.Provider} provider (optional) An alternate state provider
54785      */
54786     restoreState : function(provider){
54787         if(!provider){
54788             provider = Roo.state.Manager;
54789         }
54790         var sm = new Roo.LayoutStateManager();
54791         sm.init(this, provider);
54792     },
54793
54794     /**
54795      * Adds a batch of multiple ContentPanels dynamically by passing a special regions config object.  This config
54796      * object should contain properties for each region to add ContentPanels to, and each property's value should be
54797      * a valid ContentPanel config object.  Example:
54798      * <pre><code>
54799 // Create the main layout
54800 var layout = new Roo.BorderLayout('main-ct', {
54801     west: {
54802         split:true,
54803         minSize: 175,
54804         titlebar: true
54805     },
54806     center: {
54807         title:'Components'
54808     }
54809 }, 'main-ct');
54810
54811 // Create and add multiple ContentPanels at once via configs
54812 layout.batchAdd({
54813    west: {
54814        id: 'source-files',
54815        autoCreate:true,
54816        title:'Ext Source Files',
54817        autoScroll:true,
54818        fitToFrame:true
54819    },
54820    center : {
54821        el: cview,
54822        autoScroll:true,
54823        fitToFrame:true,
54824        toolbar: tb,
54825        resizeEl:'cbody'
54826    }
54827 });
54828 </code></pre>
54829      * @param {Object} regions An object containing ContentPanel configs by region name
54830      */
54831     batchAdd : function(regions){
54832         this.beginUpdate();
54833         for(var rname in regions){
54834             var lr = this.regions[rname];
54835             if(lr){
54836                 this.addTypedPanels(lr, regions[rname]);
54837             }
54838         }
54839         this.endUpdate();
54840     },
54841
54842     // private
54843     addTypedPanels : function(lr, ps){
54844         if(typeof ps == 'string'){
54845             lr.add(new Roo.ContentPanel(ps));
54846         }
54847         else if(ps instanceof Array){
54848             for(var i =0, len = ps.length; i < len; i++){
54849                 this.addTypedPanels(lr, ps[i]);
54850             }
54851         }
54852         else if(!ps.events){ // raw config?
54853             var el = ps.el;
54854             delete ps.el; // prevent conflict
54855             lr.add(new Roo.ContentPanel(el || Roo.id(), ps));
54856         }
54857         else {  // panel object assumed!
54858             lr.add(ps);
54859         }
54860     },
54861     /**
54862      * Adds a xtype elements to the layout.
54863      * <pre><code>
54864
54865 layout.addxtype({
54866        xtype : 'ContentPanel',
54867        region: 'west',
54868        items: [ .... ]
54869    }
54870 );
54871
54872 layout.addxtype({
54873         xtype : 'NestedLayoutPanel',
54874         region: 'west',
54875         layout: {
54876            center: { },
54877            west: { }   
54878         },
54879         items : [ ... list of content panels or nested layout panels.. ]
54880    }
54881 );
54882 </code></pre>
54883      * @param {Object} cfg Xtype definition of item to add.
54884      */
54885     addxtype : function(cfg)
54886     {
54887         // basically accepts a pannel...
54888         // can accept a layout region..!?!?
54889         //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
54890         
54891         if (!cfg.xtype.match(/Panel$/)) {
54892             return false;
54893         }
54894         var ret = false;
54895         
54896         if (typeof(cfg.region) == 'undefined') {
54897             Roo.log("Failed to add Panel, region was not set");
54898             Roo.log(cfg);
54899             return false;
54900         }
54901         var region = cfg.region;
54902         delete cfg.region;
54903         
54904           
54905         var xitems = [];
54906         if (cfg.items) {
54907             xitems = cfg.items;
54908             delete cfg.items;
54909         }
54910         var nb = false;
54911         
54912         switch(cfg.xtype) 
54913         {
54914             case 'ContentPanel':  // ContentPanel (el, cfg)
54915             case 'ScrollPanel':  // ContentPanel (el, cfg)
54916             case 'ViewPanel': 
54917                 if(cfg.autoCreate) {
54918                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
54919                 } else {
54920                     var el = this.el.createChild();
54921                     ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
54922                 }
54923                 
54924                 this.add(region, ret);
54925                 break;
54926             
54927             
54928             case 'TreePanel': // our new panel!
54929                 cfg.el = this.el.createChild();
54930                 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
54931                 this.add(region, ret);
54932                 break;
54933             
54934             case 'NestedLayoutPanel': 
54935                 // create a new Layout (which is  a Border Layout...
54936                 var el = this.el.createChild();
54937                 var clayout = cfg.layout;
54938                 delete cfg.layout;
54939                 clayout.items   = clayout.items  || [];
54940                 // replace this exitems with the clayout ones..
54941                 xitems = clayout.items;
54942                  
54943                 
54944                 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
54945                     cfg.background = false;
54946                 }
54947                 var layout = new Roo.BorderLayout(el, clayout);
54948                 
54949                 ret = new Roo[cfg.xtype](layout, cfg); // new panel!!!!!
54950                 //console.log('adding nested layout panel '  + cfg.toSource());
54951                 this.add(region, ret);
54952                 nb = {}; /// find first...
54953                 break;
54954                 
54955             case 'GridPanel': 
54956             
54957                 // needs grid and region
54958                 
54959                 //var el = this.getRegion(region).el.createChild();
54960                 var el = this.el.createChild();
54961                 // create the grid first...
54962                 
54963                 var grid = new Roo.grid[cfg.grid.xtype](el, cfg.grid);
54964                 delete cfg.grid;
54965                 if (region == 'center' && this.active ) {
54966                     cfg.background = false;
54967                 }
54968                 ret = new Roo[cfg.xtype](grid, cfg); // new panel!!!!!
54969                 
54970                 this.add(region, ret);
54971                 if (cfg.background) {
54972                     ret.on('activate', function(gp) {
54973                         if (!gp.grid.rendered) {
54974                             gp.grid.render();
54975                         }
54976                     });
54977                 } else {
54978                     grid.render();
54979                 }
54980                 break;
54981            
54982            
54983            
54984                 
54985                 
54986                 
54987             default:
54988                 if (typeof(Roo[cfg.xtype]) != 'undefined') {
54989                     
54990                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
54991                     this.add(region, ret);
54992                 } else {
54993                 
54994                     alert("Can not add '" + cfg.xtype + "' to BorderLayout");
54995                     return null;
54996                 }
54997                 
54998              // GridPanel (grid, cfg)
54999             
55000         }
55001         this.beginUpdate();
55002         // add children..
55003         var region = '';
55004         var abn = {};
55005         Roo.each(xitems, function(i)  {
55006             region = nb && i.region ? i.region : false;
55007             
55008             var add = ret.addxtype(i);
55009            
55010             if (region) {
55011                 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
55012                 if (!i.background) {
55013                     abn[region] = nb[region] ;
55014                 }
55015             }
55016             
55017         });
55018         this.endUpdate();
55019
55020         // make the last non-background panel active..
55021         //if (nb) { Roo.log(abn); }
55022         if (nb) {
55023             
55024             for(var r in abn) {
55025                 region = this.getRegion(r);
55026                 if (region) {
55027                     // tried using nb[r], but it does not work..
55028                      
55029                     region.showPanel(abn[r]);
55030                    
55031                 }
55032             }
55033         }
55034         return ret;
55035         
55036     }
55037 });
55038
55039 /**
55040  * Shortcut for creating a new BorderLayout object and adding one or more ContentPanels to it in a single step, handling
55041  * the beginUpdate and endUpdate calls internally.  The key to this method is the <b>panels</b> property that can be
55042  * provided with each region config, which allows you to add ContentPanel configs in addition to the region configs
55043  * during creation.  The following code is equivalent to the constructor-based example at the beginning of this class:
55044  * <pre><code>
55045 // shorthand
55046 var CP = Roo.ContentPanel;
55047
55048 var layout = Roo.BorderLayout.create({
55049     north: {
55050         initialSize: 25,
55051         titlebar: false,
55052         panels: [new CP("north", "North")]
55053     },
55054     west: {
55055         split:true,
55056         initialSize: 200,
55057         minSize: 175,
55058         maxSize: 400,
55059         titlebar: true,
55060         collapsible: true,
55061         panels: [new CP("west", {title: "West"})]
55062     },
55063     east: {
55064         split:true,
55065         initialSize: 202,
55066         minSize: 175,
55067         maxSize: 400,
55068         titlebar: true,
55069         collapsible: true,
55070         panels: [new CP("autoTabs", {title: "Auto Tabs", closable: true})]
55071     },
55072     south: {
55073         split:true,
55074         initialSize: 100,
55075         minSize: 100,
55076         maxSize: 200,
55077         titlebar: true,
55078         collapsible: true,
55079         panels: [new CP("south", {title: "South", closable: true})]
55080     },
55081     center: {
55082         titlebar: true,
55083         autoScroll:true,
55084         resizeTabs: true,
55085         minTabWidth: 50,
55086         preferredTabWidth: 150,
55087         panels: [
55088             new CP("center1", {title: "Close Me", closable: true}),
55089             new CP("center2", {title: "Center Panel", closable: false})
55090         ]
55091     }
55092 }, document.body);
55093
55094 layout.getRegion("center").showPanel("center1");
55095 </code></pre>
55096  * @param config
55097  * @param targetEl
55098  */
55099 Roo.BorderLayout.create = function(config, targetEl){
55100     var layout = new Roo.BorderLayout(targetEl || document.body, config);
55101     layout.beginUpdate();
55102     var regions = Roo.BorderLayout.RegionFactory.validRegions;
55103     for(var j = 0, jlen = regions.length; j < jlen; j++){
55104         var lr = regions[j];
55105         if(layout.regions[lr] && config[lr].panels){
55106             var r = layout.regions[lr];
55107             var ps = config[lr].panels;
55108             layout.addTypedPanels(r, ps);
55109         }
55110     }
55111     layout.endUpdate();
55112     return layout;
55113 };
55114
55115 // private
55116 Roo.BorderLayout.RegionFactory = {
55117     // private
55118     validRegions : ["north","south","east","west","center"],
55119
55120     // private
55121     create : function(target, mgr, config){
55122         target = target.toLowerCase();
55123         if(config.lightweight || config.basic){
55124             return new Roo.BasicLayoutRegion(mgr, config, target);
55125         }
55126         switch(target){
55127             case "north":
55128                 return new Roo.NorthLayoutRegion(mgr, config);
55129             case "south":
55130                 return new Roo.SouthLayoutRegion(mgr, config);
55131             case "east":
55132                 return new Roo.EastLayoutRegion(mgr, config);
55133             case "west":
55134                 return new Roo.WestLayoutRegion(mgr, config);
55135             case "center":
55136                 return new Roo.CenterLayoutRegion(mgr, config);
55137         }
55138         throw 'Layout region "'+target+'" not supported.';
55139     }
55140 };/*
55141  * Based on:
55142  * Ext JS Library 1.1.1
55143  * Copyright(c) 2006-2007, Ext JS, LLC.
55144  *
55145  * Originally Released Under LGPL - original licence link has changed is not relivant.
55146  *
55147  * Fork - LGPL
55148  * <script type="text/javascript">
55149  */
55150  
55151 /**
55152  * @class Roo.BasicLayoutRegion
55153  * @extends Roo.util.Observable
55154  * This class represents a lightweight region in a layout manager. This region does not move dom nodes
55155  * and does not have a titlebar, tabs or any other features. All it does is size and position 
55156  * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
55157  */
55158 Roo.BasicLayoutRegion = function(mgr, config, pos, skipConfig){
55159     this.mgr = mgr;
55160     this.position  = pos;
55161     this.events = {
55162         /**
55163          * @scope Roo.BasicLayoutRegion
55164          */
55165         
55166         /**
55167          * @event beforeremove
55168          * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
55169          * @param {Roo.LayoutRegion} this
55170          * @param {Roo.ContentPanel} panel The panel
55171          * @param {Object} e The cancel event object
55172          */
55173         "beforeremove" : true,
55174         /**
55175          * @event invalidated
55176          * Fires when the layout for this region is changed.
55177          * @param {Roo.LayoutRegion} this
55178          */
55179         "invalidated" : true,
55180         /**
55181          * @event visibilitychange
55182          * Fires when this region is shown or hidden 
55183          * @param {Roo.LayoutRegion} this
55184          * @param {Boolean} visibility true or false
55185          */
55186         "visibilitychange" : true,
55187         /**
55188          * @event paneladded
55189          * Fires when a panel is added. 
55190          * @param {Roo.LayoutRegion} this
55191          * @param {Roo.ContentPanel} panel The panel
55192          */
55193         "paneladded" : true,
55194         /**
55195          * @event panelremoved
55196          * Fires when a panel is removed. 
55197          * @param {Roo.LayoutRegion} this
55198          * @param {Roo.ContentPanel} panel The panel
55199          */
55200         "panelremoved" : true,
55201         /**
55202          * @event beforecollapse
55203          * Fires when this region before collapse.
55204          * @param {Roo.LayoutRegion} this
55205          */
55206         "beforecollapse" : true,
55207         /**
55208          * @event collapsed
55209          * Fires when this region is collapsed.
55210          * @param {Roo.LayoutRegion} this
55211          */
55212         "collapsed" : true,
55213         /**
55214          * @event expanded
55215          * Fires when this region is expanded.
55216          * @param {Roo.LayoutRegion} this
55217          */
55218         "expanded" : true,
55219         /**
55220          * @event slideshow
55221          * Fires when this region is slid into view.
55222          * @param {Roo.LayoutRegion} this
55223          */
55224         "slideshow" : true,
55225         /**
55226          * @event slidehide
55227          * Fires when this region slides out of view. 
55228          * @param {Roo.LayoutRegion} this
55229          */
55230         "slidehide" : true,
55231         /**
55232          * @event panelactivated
55233          * Fires when a panel is activated. 
55234          * @param {Roo.LayoutRegion} this
55235          * @param {Roo.ContentPanel} panel The activated panel
55236          */
55237         "panelactivated" : true,
55238         /**
55239          * @event resized
55240          * Fires when the user resizes this region. 
55241          * @param {Roo.LayoutRegion} this
55242          * @param {Number} newSize The new size (width for east/west, height for north/south)
55243          */
55244         "resized" : true
55245     };
55246     /** A collection of panels in this region. @type Roo.util.MixedCollection */
55247     this.panels = new Roo.util.MixedCollection();
55248     this.panels.getKey = this.getPanelId.createDelegate(this);
55249     this.box = null;
55250     this.activePanel = null;
55251     // ensure listeners are added...
55252     
55253     if (config.listeners || config.events) {
55254         Roo.BasicLayoutRegion.superclass.constructor.call(this, {
55255             listeners : config.listeners || {},
55256             events : config.events || {}
55257         });
55258     }
55259     
55260     if(skipConfig !== true){
55261         this.applyConfig(config);
55262     }
55263 };
55264
55265 Roo.extend(Roo.BasicLayoutRegion, Roo.util.Observable, {
55266     getPanelId : function(p){
55267         return p.getId();
55268     },
55269     
55270     applyConfig : function(config){
55271         this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
55272         this.config = config;
55273         
55274     },
55275     
55276     /**
55277      * Resizes the region to the specified size. For vertical regions (west, east) this adjusts 
55278      * the width, for horizontal (north, south) the height.
55279      * @param {Number} newSize The new width or height
55280      */
55281     resizeTo : function(newSize){
55282         var el = this.el ? this.el :
55283                  (this.activePanel ? this.activePanel.getEl() : null);
55284         if(el){
55285             switch(this.position){
55286                 case "east":
55287                 case "west":
55288                     el.setWidth(newSize);
55289                     this.fireEvent("resized", this, newSize);
55290                 break;
55291                 case "north":
55292                 case "south":
55293                     el.setHeight(newSize);
55294                     this.fireEvent("resized", this, newSize);
55295                 break;                
55296             }
55297         }
55298     },
55299     
55300     getBox : function(){
55301         return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
55302     },
55303     
55304     getMargins : function(){
55305         return this.margins;
55306     },
55307     
55308     updateBox : function(box){
55309         this.box = box;
55310         var el = this.activePanel.getEl();
55311         el.dom.style.left = box.x + "px";
55312         el.dom.style.top = box.y + "px";
55313         this.activePanel.setSize(box.width, box.height);
55314     },
55315     
55316     /**
55317      * Returns the container element for this region.
55318      * @return {Roo.Element}
55319      */
55320     getEl : function(){
55321         return this.activePanel;
55322     },
55323     
55324     /**
55325      * Returns true if this region is currently visible.
55326      * @return {Boolean}
55327      */
55328     isVisible : function(){
55329         return this.activePanel ? true : false;
55330     },
55331     
55332     setActivePanel : function(panel){
55333         panel = this.getPanel(panel);
55334         if(this.activePanel && this.activePanel != panel){
55335             this.activePanel.setActiveState(false);
55336             this.activePanel.getEl().setLeftTop(-10000,-10000);
55337         }
55338         this.activePanel = panel;
55339         panel.setActiveState(true);
55340         if(this.box){
55341             panel.setSize(this.box.width, this.box.height);
55342         }
55343         this.fireEvent("panelactivated", this, panel);
55344         this.fireEvent("invalidated");
55345     },
55346     
55347     /**
55348      * Show the specified panel.
55349      * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
55350      * @return {Roo.ContentPanel} The shown panel or null
55351      */
55352     showPanel : function(panel){
55353         if(panel = this.getPanel(panel)){
55354             this.setActivePanel(panel);
55355         }
55356         return panel;
55357     },
55358     
55359     /**
55360      * Get the active panel for this region.
55361      * @return {Roo.ContentPanel} The active panel or null
55362      */
55363     getActivePanel : function(){
55364         return this.activePanel;
55365     },
55366     
55367     /**
55368      * Add the passed ContentPanel(s)
55369      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
55370      * @return {Roo.ContentPanel} The panel added (if only one was added)
55371      */
55372     add : function(panel){
55373         if(arguments.length > 1){
55374             for(var i = 0, len = arguments.length; i < len; i++) {
55375                 this.add(arguments[i]);
55376             }
55377             return null;
55378         }
55379         if(this.hasPanel(panel)){
55380             this.showPanel(panel);
55381             return panel;
55382         }
55383         var el = panel.getEl();
55384         if(el.dom.parentNode != this.mgr.el.dom){
55385             this.mgr.el.dom.appendChild(el.dom);
55386         }
55387         if(panel.setRegion){
55388             panel.setRegion(this);
55389         }
55390         this.panels.add(panel);
55391         el.setStyle("position", "absolute");
55392         if(!panel.background){
55393             this.setActivePanel(panel);
55394             if(this.config.initialSize && this.panels.getCount()==1){
55395                 this.resizeTo(this.config.initialSize);
55396             }
55397         }
55398         this.fireEvent("paneladded", this, panel);
55399         return panel;
55400     },
55401     
55402     /**
55403      * Returns true if the panel is in this region.
55404      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
55405      * @return {Boolean}
55406      */
55407     hasPanel : function(panel){
55408         if(typeof panel == "object"){ // must be panel obj
55409             panel = panel.getId();
55410         }
55411         return this.getPanel(panel) ? true : false;
55412     },
55413     
55414     /**
55415      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
55416      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
55417      * @param {Boolean} preservePanel Overrides the config preservePanel option
55418      * @return {Roo.ContentPanel} The panel that was removed
55419      */
55420     remove : function(panel, preservePanel){
55421         panel = this.getPanel(panel);
55422         if(!panel){
55423             return null;
55424         }
55425         var e = {};
55426         this.fireEvent("beforeremove", this, panel, e);
55427         if(e.cancel === true){
55428             return null;
55429         }
55430         var panelId = panel.getId();
55431         this.panels.removeKey(panelId);
55432         return panel;
55433     },
55434     
55435     /**
55436      * Returns the panel specified or null if it's not in this region.
55437      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
55438      * @return {Roo.ContentPanel}
55439      */
55440     getPanel : function(id){
55441         if(typeof id == "object"){ // must be panel obj
55442             return id;
55443         }
55444         return this.panels.get(id);
55445     },
55446     
55447     /**
55448      * Returns this regions position (north/south/east/west/center).
55449      * @return {String} 
55450      */
55451     getPosition: function(){
55452         return this.position;    
55453     }
55454 });/*
55455  * Based on:
55456  * Ext JS Library 1.1.1
55457  * Copyright(c) 2006-2007, Ext JS, LLC.
55458  *
55459  * Originally Released Under LGPL - original licence link has changed is not relivant.
55460  *
55461  * Fork - LGPL
55462  * <script type="text/javascript">
55463  */
55464  
55465 /**
55466  * @class Roo.LayoutRegion
55467  * @extends Roo.BasicLayoutRegion
55468  * This class represents a region in a layout manager.
55469  * @cfg {Boolean}   collapsible     False to disable collapsing (defaults to true)
55470  * @cfg {Boolean}   collapsed       True to set the initial display to collapsed (defaults to false)
55471  * @cfg {Boolean}   floatable       False to disable floating (defaults to true)
55472  * @cfg {Object}    margins         Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
55473  * @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})
55474  * @cfg {String}    tabPosition     (top|bottom) "top" or "bottom" (defaults to "bottom")
55475  * @cfg {String}    collapsedTitle  Optional string message to display in the collapsed block of a north or south region
55476  * @cfg {Boolean}   alwaysShowTabs  True to always display tabs even when there is only 1 panel (defaults to false)
55477  * @cfg {Boolean}   autoScroll      True to enable overflow scrolling (defaults to false)
55478  * @cfg {Boolean}   titlebar        True to display a title bar (defaults to true)
55479  * @cfg {String}    title           The title for the region (overrides panel titles)
55480  * @cfg {Boolean}   animate         True to animate expand/collapse (defaults to false)
55481  * @cfg {Boolean}   autoHide        False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
55482  * @cfg {Boolean}   preservePanels  True to preserve removed panels so they can be readded later (defaults to false)
55483  * @cfg {Boolean}   closeOnTab      True to place the close icon on the tabs instead of the region titlebar (defaults to false)
55484  * @cfg {Boolean}   hideTabs        True to hide the tab strip (defaults to false)
55485  * @cfg {Boolean}   resizeTabs      True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
55486  *                      the space available, similar to FireFox 1.5 tabs (defaults to false)
55487  * @cfg {Number}    minTabWidth     The minimum tab width (defaults to 40)
55488  * @cfg {Number}    preferredTabWidth The preferred tab width (defaults to 150)
55489  * @cfg {Boolean}   showPin         True to show a pin button
55490  * @cfg {Boolean}   hidden          True to start the region hidden (defaults to false)
55491  * @cfg {Boolean}   hideWhenEmpty   True to hide the region when it has no panels
55492  * @cfg {Boolean}   disableTabTips  True to disable tab tooltips
55493  * @cfg {Number}    width           For East/West panels
55494  * @cfg {Number}    height          For North/South panels
55495  * @cfg {Boolean}   split           To show the splitter
55496  * @cfg {Boolean}   toolbar         xtype configuration for a toolbar - shows on right of tabbar
55497  */
55498 Roo.LayoutRegion = function(mgr, config, pos){
55499     Roo.LayoutRegion.superclass.constructor.call(this, mgr, config, pos, true);
55500     var dh = Roo.DomHelper;
55501     /** This region's container element 
55502     * @type Roo.Element */
55503     this.el = dh.append(mgr.el.dom, {tag: "div", cls: "x-layout-panel x-layout-panel-" + this.position}, true);
55504     /** This region's title element 
55505     * @type Roo.Element */
55506
55507     this.titleEl = dh.append(this.el.dom, {tag: "div", unselectable: "on", cls: "x-unselectable x-layout-panel-hd x-layout-title-"+this.position, children:[
55508         {tag: "span", cls: "x-unselectable x-layout-panel-hd-text", unselectable: "on", html: "&#160;"},
55509         {tag: "div", cls: "x-unselectable x-layout-panel-hd-tools", unselectable: "on"}
55510     ]}, true);
55511     this.titleEl.enableDisplayMode();
55512     /** This region's title text element 
55513     * @type HTMLElement */
55514     this.titleTextEl = this.titleEl.dom.firstChild;
55515     this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
55516     this.closeBtn = this.createTool(this.tools.dom, "x-layout-close");
55517     this.closeBtn.enableDisplayMode();
55518     this.closeBtn.on("click", this.closeClicked, this);
55519     this.closeBtn.hide();
55520
55521     this.createBody(config);
55522     this.visible = true;
55523     this.collapsed = false;
55524
55525     if(config.hideWhenEmpty){
55526         this.hide();
55527         this.on("paneladded", this.validateVisibility, this);
55528         this.on("panelremoved", this.validateVisibility, this);
55529     }
55530     this.applyConfig(config);
55531 };
55532
55533 Roo.extend(Roo.LayoutRegion, Roo.BasicLayoutRegion, {
55534
55535     createBody : function(){
55536         /** This region's body element 
55537         * @type Roo.Element */
55538         this.bodyEl = this.el.createChild({tag: "div", cls: "x-layout-panel-body"});
55539     },
55540
55541     applyConfig : function(c){
55542         if(c.collapsible && this.position != "center" && !this.collapsedEl){
55543             var dh = Roo.DomHelper;
55544             if(c.titlebar !== false){
55545                 this.collapseBtn = this.createTool(this.tools.dom, "x-layout-collapse-"+this.position);
55546                 this.collapseBtn.on("click", this.collapse, this);
55547                 this.collapseBtn.enableDisplayMode();
55548
55549                 if(c.showPin === true || this.showPin){
55550                     this.stickBtn = this.createTool(this.tools.dom, "x-layout-stick");
55551                     this.stickBtn.enableDisplayMode();
55552                     this.stickBtn.on("click", this.expand, this);
55553                     this.stickBtn.hide();
55554                 }
55555             }
55556             /** This region's collapsed element
55557             * @type Roo.Element */
55558             this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
55559                 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
55560             ]}, true);
55561             if(c.floatable !== false){
55562                this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
55563                this.collapsedEl.on("click", this.collapseClick, this);
55564             }
55565
55566             if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
55567                 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
55568                    id: "message", unselectable: "on", style:{"float":"left"}});
55569                this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
55570              }
55571             this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
55572             this.expandBtn.on("click", this.expand, this);
55573         }
55574         if(this.collapseBtn){
55575             this.collapseBtn.setVisible(c.collapsible == true);
55576         }
55577         this.cmargins = c.cmargins || this.cmargins ||
55578                          (this.position == "west" || this.position == "east" ?
55579                              {top: 0, left: 2, right:2, bottom: 0} :
55580                              {top: 2, left: 0, right:0, bottom: 2});
55581         this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
55582         this.bottomTabs = c.tabPosition != "top";
55583         this.autoScroll = c.autoScroll || false;
55584         if(this.autoScroll){
55585             this.bodyEl.setStyle("overflow", "auto");
55586         }else{
55587             this.bodyEl.setStyle("overflow", "hidden");
55588         }
55589         //if(c.titlebar !== false){
55590             if((!c.titlebar && !c.title) || c.titlebar === false){
55591                 this.titleEl.hide();
55592             }else{
55593                 this.titleEl.show();
55594                 if(c.title){
55595                     this.titleTextEl.innerHTML = c.title;
55596                 }
55597             }
55598         //}
55599         this.duration = c.duration || .30;
55600         this.slideDuration = c.slideDuration || .45;
55601         this.config = c;
55602         if(c.collapsed){
55603             this.collapse(true);
55604         }
55605         if(c.hidden){
55606             this.hide();
55607         }
55608     },
55609     /**
55610      * Returns true if this region is currently visible.
55611      * @return {Boolean}
55612      */
55613     isVisible : function(){
55614         return this.visible;
55615     },
55616
55617     /**
55618      * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
55619      * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&amp;#160;")
55620      */
55621     setCollapsedTitle : function(title){
55622         title = title || "&#160;";
55623         if(this.collapsedTitleTextEl){
55624             this.collapsedTitleTextEl.innerHTML = title;
55625         }
55626     },
55627
55628     getBox : function(){
55629         var b;
55630         if(!this.collapsed){
55631             b = this.el.getBox(false, true);
55632         }else{
55633             b = this.collapsedEl.getBox(false, true);
55634         }
55635         return b;
55636     },
55637
55638     getMargins : function(){
55639         return this.collapsed ? this.cmargins : this.margins;
55640     },
55641
55642     highlight : function(){
55643         this.el.addClass("x-layout-panel-dragover");
55644     },
55645
55646     unhighlight : function(){
55647         this.el.removeClass("x-layout-panel-dragover");
55648     },
55649
55650     updateBox : function(box){
55651         this.box = box;
55652         if(!this.collapsed){
55653             this.el.dom.style.left = box.x + "px";
55654             this.el.dom.style.top = box.y + "px";
55655             this.updateBody(box.width, box.height);
55656         }else{
55657             this.collapsedEl.dom.style.left = box.x + "px";
55658             this.collapsedEl.dom.style.top = box.y + "px";
55659             this.collapsedEl.setSize(box.width, box.height);
55660         }
55661         if(this.tabs){
55662             this.tabs.autoSizeTabs();
55663         }
55664     },
55665
55666     updateBody : function(w, h){
55667         if(w !== null){
55668             this.el.setWidth(w);
55669             w -= this.el.getBorderWidth("rl");
55670             if(this.config.adjustments){
55671                 w += this.config.adjustments[0];
55672             }
55673         }
55674         if(h !== null){
55675             this.el.setHeight(h);
55676             h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
55677             h -= this.el.getBorderWidth("tb");
55678             if(this.config.adjustments){
55679                 h += this.config.adjustments[1];
55680             }
55681             this.bodyEl.setHeight(h);
55682             if(this.tabs){
55683                 h = this.tabs.syncHeight(h);
55684             }
55685         }
55686         if(this.panelSize){
55687             w = w !== null ? w : this.panelSize.width;
55688             h = h !== null ? h : this.panelSize.height;
55689         }
55690         if(this.activePanel){
55691             var el = this.activePanel.getEl();
55692             w = w !== null ? w : el.getWidth();
55693             h = h !== null ? h : el.getHeight();
55694             this.panelSize = {width: w, height: h};
55695             this.activePanel.setSize(w, h);
55696         }
55697         if(Roo.isIE && this.tabs){
55698             this.tabs.el.repaint();
55699         }
55700     },
55701
55702     /**
55703      * Returns the container element for this region.
55704      * @return {Roo.Element}
55705      */
55706     getEl : function(){
55707         return this.el;
55708     },
55709
55710     /**
55711      * Hides this region.
55712      */
55713     hide : function(){
55714         if(!this.collapsed){
55715             this.el.dom.style.left = "-2000px";
55716             this.el.hide();
55717         }else{
55718             this.collapsedEl.dom.style.left = "-2000px";
55719             this.collapsedEl.hide();
55720         }
55721         this.visible = false;
55722         this.fireEvent("visibilitychange", this, false);
55723     },
55724
55725     /**
55726      * Shows this region if it was previously hidden.
55727      */
55728     show : function(){
55729         if(!this.collapsed){
55730             this.el.show();
55731         }else{
55732             this.collapsedEl.show();
55733         }
55734         this.visible = true;
55735         this.fireEvent("visibilitychange", this, true);
55736     },
55737
55738     closeClicked : function(){
55739         if(this.activePanel){
55740             this.remove(this.activePanel);
55741         }
55742     },
55743
55744     collapseClick : function(e){
55745         if(this.isSlid){
55746            e.stopPropagation();
55747            this.slideIn();
55748         }else{
55749            e.stopPropagation();
55750            this.slideOut();
55751         }
55752     },
55753
55754     /**
55755      * Collapses this region.
55756      * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
55757      */
55758     collapse : function(skipAnim, skipCheck){
55759         if(this.collapsed) {
55760             return;
55761         }
55762         
55763         if(skipCheck || this.fireEvent("beforecollapse", this) != false){
55764             
55765             this.collapsed = true;
55766             if(this.split){
55767                 this.split.el.hide();
55768             }
55769             if(this.config.animate && skipAnim !== true){
55770                 this.fireEvent("invalidated", this);
55771                 this.animateCollapse();
55772             }else{
55773                 this.el.setLocation(-20000,-20000);
55774                 this.el.hide();
55775                 this.collapsedEl.show();
55776                 this.fireEvent("collapsed", this);
55777                 this.fireEvent("invalidated", this);
55778             }
55779         }
55780         
55781     },
55782
55783     animateCollapse : function(){
55784         // overridden
55785     },
55786
55787     /**
55788      * Expands this region if it was previously collapsed.
55789      * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
55790      * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
55791      */
55792     expand : function(e, skipAnim){
55793         if(e) {
55794             e.stopPropagation();
55795         }
55796         if(!this.collapsed || this.el.hasActiveFx()) {
55797             return;
55798         }
55799         if(this.isSlid){
55800             this.afterSlideIn();
55801             skipAnim = true;
55802         }
55803         this.collapsed = false;
55804         if(this.config.animate && skipAnim !== true){
55805             this.animateExpand();
55806         }else{
55807             this.el.show();
55808             if(this.split){
55809                 this.split.el.show();
55810             }
55811             this.collapsedEl.setLocation(-2000,-2000);
55812             this.collapsedEl.hide();
55813             this.fireEvent("invalidated", this);
55814             this.fireEvent("expanded", this);
55815         }
55816     },
55817
55818     animateExpand : function(){
55819         // overridden
55820     },
55821
55822     initTabs : function()
55823     {
55824         this.bodyEl.setStyle("overflow", "hidden");
55825         var ts = new Roo.TabPanel(
55826                 this.bodyEl.dom,
55827                 {
55828                     tabPosition: this.bottomTabs ? 'bottom' : 'top',
55829                     disableTooltips: this.config.disableTabTips,
55830                     toolbar : this.config.toolbar
55831                 }
55832         );
55833         if(this.config.hideTabs){
55834             ts.stripWrap.setDisplayed(false);
55835         }
55836         this.tabs = ts;
55837         ts.resizeTabs = this.config.resizeTabs === true;
55838         ts.minTabWidth = this.config.minTabWidth || 40;
55839         ts.maxTabWidth = this.config.maxTabWidth || 250;
55840         ts.preferredTabWidth = this.config.preferredTabWidth || 150;
55841         ts.monitorResize = false;
55842         ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
55843         ts.bodyEl.addClass('x-layout-tabs-body');
55844         this.panels.each(this.initPanelAsTab, this);
55845     },
55846
55847     initPanelAsTab : function(panel){
55848         var ti = this.tabs.addTab(panel.getEl().id, panel.getTitle(), null,
55849                     this.config.closeOnTab && panel.isClosable());
55850         if(panel.tabTip !== undefined){
55851             ti.setTooltip(panel.tabTip);
55852         }
55853         ti.on("activate", function(){
55854               this.setActivePanel(panel);
55855         }, this);
55856         if(this.config.closeOnTab){
55857             ti.on("beforeclose", function(t, e){
55858                 e.cancel = true;
55859                 this.remove(panel);
55860             }, this);
55861         }
55862         return ti;
55863     },
55864
55865     updatePanelTitle : function(panel, title){
55866         if(this.activePanel == panel){
55867             this.updateTitle(title);
55868         }
55869         if(this.tabs){
55870             var ti = this.tabs.getTab(panel.getEl().id);
55871             ti.setText(title);
55872             if(panel.tabTip !== undefined){
55873                 ti.setTooltip(panel.tabTip);
55874             }
55875         }
55876     },
55877
55878     updateTitle : function(title){
55879         if(this.titleTextEl && !this.config.title){
55880             this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : "&#160;");
55881         }
55882     },
55883
55884     setActivePanel : function(panel){
55885         panel = this.getPanel(panel);
55886         if(this.activePanel && this.activePanel != panel){
55887             this.activePanel.setActiveState(false);
55888         }
55889         this.activePanel = panel;
55890         panel.setActiveState(true);
55891         if(this.panelSize){
55892             panel.setSize(this.panelSize.width, this.panelSize.height);
55893         }
55894         if(this.closeBtn){
55895             this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
55896         }
55897         this.updateTitle(panel.getTitle());
55898         if(this.tabs){
55899             this.fireEvent("invalidated", this);
55900         }
55901         this.fireEvent("panelactivated", this, panel);
55902     },
55903
55904     /**
55905      * Shows the specified panel.
55906      * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
55907      * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
55908      */
55909     showPanel : function(panel)
55910     {
55911         panel = this.getPanel(panel);
55912         if(panel){
55913             if(this.tabs){
55914                 var tab = this.tabs.getTab(panel.getEl().id);
55915                 if(tab.isHidden()){
55916                     this.tabs.unhideTab(tab.id);
55917                 }
55918                 tab.activate();
55919             }else{
55920                 this.setActivePanel(panel);
55921             }
55922         }
55923         return panel;
55924     },
55925
55926     /**
55927      * Get the active panel for this region.
55928      * @return {Roo.ContentPanel} The active panel or null
55929      */
55930     getActivePanel : function(){
55931         return this.activePanel;
55932     },
55933
55934     validateVisibility : function(){
55935         if(this.panels.getCount() < 1){
55936             this.updateTitle("&#160;");
55937             this.closeBtn.hide();
55938             this.hide();
55939         }else{
55940             if(!this.isVisible()){
55941                 this.show();
55942             }
55943         }
55944     },
55945
55946     /**
55947      * Adds the passed ContentPanel(s) to this region.
55948      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
55949      * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
55950      */
55951     add : function(panel){
55952         if(arguments.length > 1){
55953             for(var i = 0, len = arguments.length; i < len; i++) {
55954                 this.add(arguments[i]);
55955             }
55956             return null;
55957         }
55958         if(this.hasPanel(panel)){
55959             this.showPanel(panel);
55960             return panel;
55961         }
55962         panel.setRegion(this);
55963         this.panels.add(panel);
55964         if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
55965             this.bodyEl.dom.appendChild(panel.getEl().dom);
55966             if(panel.background !== true){
55967                 this.setActivePanel(panel);
55968             }
55969             this.fireEvent("paneladded", this, panel);
55970             return panel;
55971         }
55972         if(!this.tabs){
55973             this.initTabs();
55974         }else{
55975             this.initPanelAsTab(panel);
55976         }
55977         if(panel.background !== true){
55978             this.tabs.activate(panel.getEl().id);
55979         }
55980         this.fireEvent("paneladded", this, panel);
55981         return panel;
55982     },
55983
55984     /**
55985      * Hides the tab for the specified panel.
55986      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
55987      */
55988     hidePanel : function(panel){
55989         if(this.tabs && (panel = this.getPanel(panel))){
55990             this.tabs.hideTab(panel.getEl().id);
55991         }
55992     },
55993
55994     /**
55995      * Unhides the tab for a previously hidden panel.
55996      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
55997      */
55998     unhidePanel : function(panel){
55999         if(this.tabs && (panel = this.getPanel(panel))){
56000             this.tabs.unhideTab(panel.getEl().id);
56001         }
56002     },
56003
56004     clearPanels : function(){
56005         while(this.panels.getCount() > 0){
56006              this.remove(this.panels.first());
56007         }
56008     },
56009
56010     /**
56011      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
56012      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
56013      * @param {Boolean} preservePanel Overrides the config preservePanel option
56014      * @return {Roo.ContentPanel} The panel that was removed
56015      */
56016     remove : function(panel, preservePanel){
56017         panel = this.getPanel(panel);
56018         if(!panel){
56019             return null;
56020         }
56021         var e = {};
56022         this.fireEvent("beforeremove", this, panel, e);
56023         if(e.cancel === true){
56024             return null;
56025         }
56026         preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
56027         var panelId = panel.getId();
56028         this.panels.removeKey(panelId);
56029         if(preservePanel){
56030             document.body.appendChild(panel.getEl().dom);
56031         }
56032         if(this.tabs){
56033             this.tabs.removeTab(panel.getEl().id);
56034         }else if (!preservePanel){
56035             this.bodyEl.dom.removeChild(panel.getEl().dom);
56036         }
56037         if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
56038             var p = this.panels.first();
56039             var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
56040             tempEl.appendChild(p.getEl().dom);
56041             this.bodyEl.update("");
56042             this.bodyEl.dom.appendChild(p.getEl().dom);
56043             tempEl = null;
56044             this.updateTitle(p.getTitle());
56045             this.tabs = null;
56046             this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
56047             this.setActivePanel(p);
56048         }
56049         panel.setRegion(null);
56050         if(this.activePanel == panel){
56051             this.activePanel = null;
56052         }
56053         if(this.config.autoDestroy !== false && preservePanel !== true){
56054             try{panel.destroy();}catch(e){}
56055         }
56056         this.fireEvent("panelremoved", this, panel);
56057         return panel;
56058     },
56059
56060     /**
56061      * Returns the TabPanel component used by this region
56062      * @return {Roo.TabPanel}
56063      */
56064     getTabs : function(){
56065         return this.tabs;
56066     },
56067
56068     createTool : function(parentEl, className){
56069         var btn = Roo.DomHelper.append(parentEl, {tag: "div", cls: "x-layout-tools-button",
56070             children: [{tag: "div", cls: "x-layout-tools-button-inner " + className, html: "&#160;"}]}, true);
56071         btn.addClassOnOver("x-layout-tools-button-over");
56072         return btn;
56073     }
56074 });/*
56075  * Based on:
56076  * Ext JS Library 1.1.1
56077  * Copyright(c) 2006-2007, Ext JS, LLC.
56078  *
56079  * Originally Released Under LGPL - original licence link has changed is not relivant.
56080  *
56081  * Fork - LGPL
56082  * <script type="text/javascript">
56083  */
56084  
56085
56086
56087 /**
56088  * @class Roo.SplitLayoutRegion
56089  * @extends Roo.LayoutRegion
56090  * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
56091  */
56092 Roo.SplitLayoutRegion = function(mgr, config, pos, cursor){
56093     this.cursor = cursor;
56094     Roo.SplitLayoutRegion.superclass.constructor.call(this, mgr, config, pos);
56095 };
56096
56097 Roo.extend(Roo.SplitLayoutRegion, Roo.LayoutRegion, {
56098     splitTip : "Drag to resize.",
56099     collapsibleSplitTip : "Drag to resize. Double click to hide.",
56100     useSplitTips : false,
56101
56102     applyConfig : function(config){
56103         Roo.SplitLayoutRegion.superclass.applyConfig.call(this, config);
56104         if(config.split){
56105             if(!this.split){
56106                 var splitEl = Roo.DomHelper.append(this.mgr.el.dom, 
56107                         {tag: "div", id: this.el.id + "-split", cls: "x-layout-split x-layout-split-"+this.position, html: "&#160;"});
56108                 /** The SplitBar for this region 
56109                 * @type Roo.SplitBar */
56110                 this.split = new Roo.SplitBar(splitEl, this.el, this.orientation);
56111                 this.split.on("moved", this.onSplitMove, this);
56112                 this.split.useShim = config.useShim === true;
56113                 this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
56114                 if(this.useSplitTips){
56115                     this.split.el.dom.title = config.collapsible ? this.collapsibleSplitTip : this.splitTip;
56116                 }
56117                 if(config.collapsible){
56118                     this.split.el.on("dblclick", this.collapse,  this);
56119                 }
56120             }
56121             if(typeof config.minSize != "undefined"){
56122                 this.split.minSize = config.minSize;
56123             }
56124             if(typeof config.maxSize != "undefined"){
56125                 this.split.maxSize = config.maxSize;
56126             }
56127             if(config.hideWhenEmpty || config.hidden || config.collapsed){
56128                 this.hideSplitter();
56129             }
56130         }
56131     },
56132
56133     getHMaxSize : function(){
56134          var cmax = this.config.maxSize || 10000;
56135          var center = this.mgr.getRegion("center");
56136          return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
56137     },
56138
56139     getVMaxSize : function(){
56140          var cmax = this.config.maxSize || 10000;
56141          var center = this.mgr.getRegion("center");
56142          return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
56143     },
56144
56145     onSplitMove : function(split, newSize){
56146         this.fireEvent("resized", this, newSize);
56147     },
56148     
56149     /** 
56150      * Returns the {@link Roo.SplitBar} for this region.
56151      * @return {Roo.SplitBar}
56152      */
56153     getSplitBar : function(){
56154         return this.split;
56155     },
56156     
56157     hide : function(){
56158         this.hideSplitter();
56159         Roo.SplitLayoutRegion.superclass.hide.call(this);
56160     },
56161
56162     hideSplitter : function(){
56163         if(this.split){
56164             this.split.el.setLocation(-2000,-2000);
56165             this.split.el.hide();
56166         }
56167     },
56168
56169     show : function(){
56170         if(this.split){
56171             this.split.el.show();
56172         }
56173         Roo.SplitLayoutRegion.superclass.show.call(this);
56174     },
56175     
56176     beforeSlide: function(){
56177         if(Roo.isGecko){// firefox overflow auto bug workaround
56178             this.bodyEl.clip();
56179             if(this.tabs) {
56180                 this.tabs.bodyEl.clip();
56181             }
56182             if(this.activePanel){
56183                 this.activePanel.getEl().clip();
56184                 
56185                 if(this.activePanel.beforeSlide){
56186                     this.activePanel.beforeSlide();
56187                 }
56188             }
56189         }
56190     },
56191     
56192     afterSlide : function(){
56193         if(Roo.isGecko){// firefox overflow auto bug workaround
56194             this.bodyEl.unclip();
56195             if(this.tabs) {
56196                 this.tabs.bodyEl.unclip();
56197             }
56198             if(this.activePanel){
56199                 this.activePanel.getEl().unclip();
56200                 if(this.activePanel.afterSlide){
56201                     this.activePanel.afterSlide();
56202                 }
56203             }
56204         }
56205     },
56206
56207     initAutoHide : function(){
56208         if(this.autoHide !== false){
56209             if(!this.autoHideHd){
56210                 var st = new Roo.util.DelayedTask(this.slideIn, this);
56211                 this.autoHideHd = {
56212                     "mouseout": function(e){
56213                         if(!e.within(this.el, true)){
56214                             st.delay(500);
56215                         }
56216                     },
56217                     "mouseover" : function(e){
56218                         st.cancel();
56219                     },
56220                     scope : this
56221                 };
56222             }
56223             this.el.on(this.autoHideHd);
56224         }
56225     },
56226
56227     clearAutoHide : function(){
56228         if(this.autoHide !== false){
56229             this.el.un("mouseout", this.autoHideHd.mouseout);
56230             this.el.un("mouseover", this.autoHideHd.mouseover);
56231         }
56232     },
56233
56234     clearMonitor : function(){
56235         Roo.get(document).un("click", this.slideInIf, this);
56236     },
56237
56238     // these names are backwards but not changed for compat
56239     slideOut : function(){
56240         if(this.isSlid || this.el.hasActiveFx()){
56241             return;
56242         }
56243         this.isSlid = true;
56244         if(this.collapseBtn){
56245             this.collapseBtn.hide();
56246         }
56247         this.closeBtnState = this.closeBtn.getStyle('display');
56248         this.closeBtn.hide();
56249         if(this.stickBtn){
56250             this.stickBtn.show();
56251         }
56252         this.el.show();
56253         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
56254         this.beforeSlide();
56255         this.el.setStyle("z-index", 10001);
56256         this.el.slideIn(this.getSlideAnchor(), {
56257             callback: function(){
56258                 this.afterSlide();
56259                 this.initAutoHide();
56260                 Roo.get(document).on("click", this.slideInIf, this);
56261                 this.fireEvent("slideshow", this);
56262             },
56263             scope: this,
56264             block: true
56265         });
56266     },
56267
56268     afterSlideIn : function(){
56269         this.clearAutoHide();
56270         this.isSlid = false;
56271         this.clearMonitor();
56272         this.el.setStyle("z-index", "");
56273         if(this.collapseBtn){
56274             this.collapseBtn.show();
56275         }
56276         this.closeBtn.setStyle('display', this.closeBtnState);
56277         if(this.stickBtn){
56278             this.stickBtn.hide();
56279         }
56280         this.fireEvent("slidehide", this);
56281     },
56282
56283     slideIn : function(cb){
56284         if(!this.isSlid || this.el.hasActiveFx()){
56285             Roo.callback(cb);
56286             return;
56287         }
56288         this.isSlid = false;
56289         this.beforeSlide();
56290         this.el.slideOut(this.getSlideAnchor(), {
56291             callback: function(){
56292                 this.el.setLeftTop(-10000, -10000);
56293                 this.afterSlide();
56294                 this.afterSlideIn();
56295                 Roo.callback(cb);
56296             },
56297             scope: this,
56298             block: true
56299         });
56300     },
56301     
56302     slideInIf : function(e){
56303         if(!e.within(this.el)){
56304             this.slideIn();
56305         }
56306     },
56307
56308     animateCollapse : function(){
56309         this.beforeSlide();
56310         this.el.setStyle("z-index", 20000);
56311         var anchor = this.getSlideAnchor();
56312         this.el.slideOut(anchor, {
56313             callback : function(){
56314                 this.el.setStyle("z-index", "");
56315                 this.collapsedEl.slideIn(anchor, {duration:.3});
56316                 this.afterSlide();
56317                 this.el.setLocation(-10000,-10000);
56318                 this.el.hide();
56319                 this.fireEvent("collapsed", this);
56320             },
56321             scope: this,
56322             block: true
56323         });
56324     },
56325
56326     animateExpand : function(){
56327         this.beforeSlide();
56328         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
56329         this.el.setStyle("z-index", 20000);
56330         this.collapsedEl.hide({
56331             duration:.1
56332         });
56333         this.el.slideIn(this.getSlideAnchor(), {
56334             callback : function(){
56335                 this.el.setStyle("z-index", "");
56336                 this.afterSlide();
56337                 if(this.split){
56338                     this.split.el.show();
56339                 }
56340                 this.fireEvent("invalidated", this);
56341                 this.fireEvent("expanded", this);
56342             },
56343             scope: this,
56344             block: true
56345         });
56346     },
56347
56348     anchors : {
56349         "west" : "left",
56350         "east" : "right",
56351         "north" : "top",
56352         "south" : "bottom"
56353     },
56354
56355     sanchors : {
56356         "west" : "l",
56357         "east" : "r",
56358         "north" : "t",
56359         "south" : "b"
56360     },
56361
56362     canchors : {
56363         "west" : "tl-tr",
56364         "east" : "tr-tl",
56365         "north" : "tl-bl",
56366         "south" : "bl-tl"
56367     },
56368
56369     getAnchor : function(){
56370         return this.anchors[this.position];
56371     },
56372
56373     getCollapseAnchor : function(){
56374         return this.canchors[this.position];
56375     },
56376
56377     getSlideAnchor : function(){
56378         return this.sanchors[this.position];
56379     },
56380
56381     getAlignAdj : function(){
56382         var cm = this.cmargins;
56383         switch(this.position){
56384             case "west":
56385                 return [0, 0];
56386             break;
56387             case "east":
56388                 return [0, 0];
56389             break;
56390             case "north":
56391                 return [0, 0];
56392             break;
56393             case "south":
56394                 return [0, 0];
56395             break;
56396         }
56397     },
56398
56399     getExpandAdj : function(){
56400         var c = this.collapsedEl, cm = this.cmargins;
56401         switch(this.position){
56402             case "west":
56403                 return [-(cm.right+c.getWidth()+cm.left), 0];
56404             break;
56405             case "east":
56406                 return [cm.right+c.getWidth()+cm.left, 0];
56407             break;
56408             case "north":
56409                 return [0, -(cm.top+cm.bottom+c.getHeight())];
56410             break;
56411             case "south":
56412                 return [0, cm.top+cm.bottom+c.getHeight()];
56413             break;
56414         }
56415     }
56416 });/*
56417  * Based on:
56418  * Ext JS Library 1.1.1
56419  * Copyright(c) 2006-2007, Ext JS, LLC.
56420  *
56421  * Originally Released Under LGPL - original licence link has changed is not relivant.
56422  *
56423  * Fork - LGPL
56424  * <script type="text/javascript">
56425  */
56426 /*
56427  * These classes are private internal classes
56428  */
56429 Roo.CenterLayoutRegion = function(mgr, config){
56430     Roo.LayoutRegion.call(this, mgr, config, "center");
56431     this.visible = true;
56432     this.minWidth = config.minWidth || 20;
56433     this.minHeight = config.minHeight || 20;
56434 };
56435
56436 Roo.extend(Roo.CenterLayoutRegion, Roo.LayoutRegion, {
56437     hide : function(){
56438         // center panel can't be hidden
56439     },
56440     
56441     show : function(){
56442         // center panel can't be hidden
56443     },
56444     
56445     getMinWidth: function(){
56446         return this.minWidth;
56447     },
56448     
56449     getMinHeight: function(){
56450         return this.minHeight;
56451     }
56452 });
56453
56454
56455 Roo.NorthLayoutRegion = function(mgr, config){
56456     Roo.LayoutRegion.call(this, mgr, config, "north", "n-resize");
56457     if(this.split){
56458         this.split.placement = Roo.SplitBar.TOP;
56459         this.split.orientation = Roo.SplitBar.VERTICAL;
56460         this.split.el.addClass("x-layout-split-v");
56461     }
56462     var size = config.initialSize || config.height;
56463     if(typeof size != "undefined"){
56464         this.el.setHeight(size);
56465     }
56466 };
56467 Roo.extend(Roo.NorthLayoutRegion, Roo.SplitLayoutRegion, {
56468     orientation: Roo.SplitBar.VERTICAL,
56469     getBox : function(){
56470         if(this.collapsed){
56471             return this.collapsedEl.getBox();
56472         }
56473         var box = this.el.getBox();
56474         if(this.split){
56475             box.height += this.split.el.getHeight();
56476         }
56477         return box;
56478     },
56479     
56480     updateBox : function(box){
56481         if(this.split && !this.collapsed){
56482             box.height -= this.split.el.getHeight();
56483             this.split.el.setLeft(box.x);
56484             this.split.el.setTop(box.y+box.height);
56485             this.split.el.setWidth(box.width);
56486         }
56487         if(this.collapsed){
56488             this.updateBody(box.width, null);
56489         }
56490         Roo.LayoutRegion.prototype.updateBox.call(this, box);
56491     }
56492 });
56493
56494 Roo.SouthLayoutRegion = function(mgr, config){
56495     Roo.SplitLayoutRegion.call(this, mgr, config, "south", "s-resize");
56496     if(this.split){
56497         this.split.placement = Roo.SplitBar.BOTTOM;
56498         this.split.orientation = Roo.SplitBar.VERTICAL;
56499         this.split.el.addClass("x-layout-split-v");
56500     }
56501     var size = config.initialSize || config.height;
56502     if(typeof size != "undefined"){
56503         this.el.setHeight(size);
56504     }
56505 };
56506 Roo.extend(Roo.SouthLayoutRegion, Roo.SplitLayoutRegion, {
56507     orientation: Roo.SplitBar.VERTICAL,
56508     getBox : function(){
56509         if(this.collapsed){
56510             return this.collapsedEl.getBox();
56511         }
56512         var box = this.el.getBox();
56513         if(this.split){
56514             var sh = this.split.el.getHeight();
56515             box.height += sh;
56516             box.y -= sh;
56517         }
56518         return box;
56519     },
56520     
56521     updateBox : function(box){
56522         if(this.split && !this.collapsed){
56523             var sh = this.split.el.getHeight();
56524             box.height -= sh;
56525             box.y += sh;
56526             this.split.el.setLeft(box.x);
56527             this.split.el.setTop(box.y-sh);
56528             this.split.el.setWidth(box.width);
56529         }
56530         if(this.collapsed){
56531             this.updateBody(box.width, null);
56532         }
56533         Roo.LayoutRegion.prototype.updateBox.call(this, box);
56534     }
56535 });
56536
56537 Roo.EastLayoutRegion = function(mgr, config){
56538     Roo.SplitLayoutRegion.call(this, mgr, config, "east", "e-resize");
56539     if(this.split){
56540         this.split.placement = Roo.SplitBar.RIGHT;
56541         this.split.orientation = Roo.SplitBar.HORIZONTAL;
56542         this.split.el.addClass("x-layout-split-h");
56543     }
56544     var size = config.initialSize || config.width;
56545     if(typeof size != "undefined"){
56546         this.el.setWidth(size);
56547     }
56548 };
56549 Roo.extend(Roo.EastLayoutRegion, Roo.SplitLayoutRegion, {
56550     orientation: Roo.SplitBar.HORIZONTAL,
56551     getBox : function(){
56552         if(this.collapsed){
56553             return this.collapsedEl.getBox();
56554         }
56555         var box = this.el.getBox();
56556         if(this.split){
56557             var sw = this.split.el.getWidth();
56558             box.width += sw;
56559             box.x -= sw;
56560         }
56561         return box;
56562     },
56563
56564     updateBox : function(box){
56565         if(this.split && !this.collapsed){
56566             var sw = this.split.el.getWidth();
56567             box.width -= sw;
56568             this.split.el.setLeft(box.x);
56569             this.split.el.setTop(box.y);
56570             this.split.el.setHeight(box.height);
56571             box.x += sw;
56572         }
56573         if(this.collapsed){
56574             this.updateBody(null, box.height);
56575         }
56576         Roo.LayoutRegion.prototype.updateBox.call(this, box);
56577     }
56578 });
56579
56580 Roo.WestLayoutRegion = function(mgr, config){
56581     Roo.SplitLayoutRegion.call(this, mgr, config, "west", "w-resize");
56582     if(this.split){
56583         this.split.placement = Roo.SplitBar.LEFT;
56584         this.split.orientation = Roo.SplitBar.HORIZONTAL;
56585         this.split.el.addClass("x-layout-split-h");
56586     }
56587     var size = config.initialSize || config.width;
56588     if(typeof size != "undefined"){
56589         this.el.setWidth(size);
56590     }
56591 };
56592 Roo.extend(Roo.WestLayoutRegion, Roo.SplitLayoutRegion, {
56593     orientation: Roo.SplitBar.HORIZONTAL,
56594     getBox : function(){
56595         if(this.collapsed){
56596             return this.collapsedEl.getBox();
56597         }
56598         var box = this.el.getBox();
56599         if(this.split){
56600             box.width += this.split.el.getWidth();
56601         }
56602         return box;
56603     },
56604     
56605     updateBox : function(box){
56606         if(this.split && !this.collapsed){
56607             var sw = this.split.el.getWidth();
56608             box.width -= sw;
56609             this.split.el.setLeft(box.x+box.width);
56610             this.split.el.setTop(box.y);
56611             this.split.el.setHeight(box.height);
56612         }
56613         if(this.collapsed){
56614             this.updateBody(null, box.height);
56615         }
56616         Roo.LayoutRegion.prototype.updateBox.call(this, box);
56617     }
56618 });
56619 /*
56620  * Based on:
56621  * Ext JS Library 1.1.1
56622  * Copyright(c) 2006-2007, Ext JS, LLC.
56623  *
56624  * Originally Released Under LGPL - original licence link has changed is not relivant.
56625  *
56626  * Fork - LGPL
56627  * <script type="text/javascript">
56628  */
56629  
56630  
56631 /*
56632  * Private internal class for reading and applying state
56633  */
56634 Roo.LayoutStateManager = function(layout){
56635      // default empty state
56636      this.state = {
56637         north: {},
56638         south: {},
56639         east: {},
56640         west: {}       
56641     };
56642 };
56643
56644 Roo.LayoutStateManager.prototype = {
56645     init : function(layout, provider){
56646         this.provider = provider;
56647         var state = provider.get(layout.id+"-layout-state");
56648         if(state){
56649             var wasUpdating = layout.isUpdating();
56650             if(!wasUpdating){
56651                 layout.beginUpdate();
56652             }
56653             for(var key in state){
56654                 if(typeof state[key] != "function"){
56655                     var rstate = state[key];
56656                     var r = layout.getRegion(key);
56657                     if(r && rstate){
56658                         if(rstate.size){
56659                             r.resizeTo(rstate.size);
56660                         }
56661                         if(rstate.collapsed == true){
56662                             r.collapse(true);
56663                         }else{
56664                             r.expand(null, true);
56665                         }
56666                     }
56667                 }
56668             }
56669             if(!wasUpdating){
56670                 layout.endUpdate();
56671             }
56672             this.state = state; 
56673         }
56674         this.layout = layout;
56675         layout.on("regionresized", this.onRegionResized, this);
56676         layout.on("regioncollapsed", this.onRegionCollapsed, this);
56677         layout.on("regionexpanded", this.onRegionExpanded, this);
56678     },
56679     
56680     storeState : function(){
56681         this.provider.set(this.layout.id+"-layout-state", this.state);
56682     },
56683     
56684     onRegionResized : function(region, newSize){
56685         this.state[region.getPosition()].size = newSize;
56686         this.storeState();
56687     },
56688     
56689     onRegionCollapsed : function(region){
56690         this.state[region.getPosition()].collapsed = true;
56691         this.storeState();
56692     },
56693     
56694     onRegionExpanded : function(region){
56695         this.state[region.getPosition()].collapsed = false;
56696         this.storeState();
56697     }
56698 };/*
56699  * Based on:
56700  * Ext JS Library 1.1.1
56701  * Copyright(c) 2006-2007, Ext JS, LLC.
56702  *
56703  * Originally Released Under LGPL - original licence link has changed is not relivant.
56704  *
56705  * Fork - LGPL
56706  * <script type="text/javascript">
56707  */
56708 /**
56709  * @class Roo.ContentPanel
56710  * @extends Roo.util.Observable
56711  * @children Roo.form.Form Roo.JsonView Roo.View
56712  * @parent Roo.BorderLayout Roo.LayoutDialog builder-top
56713  * A basic ContentPanel element.
56714  * @cfg {Boolean}   fitToFrame    True for this panel to adjust its size to fit when the region resizes  (defaults to false)
56715  * @cfg {Boolean}   fitContainer   When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container  (defaults to false)
56716  * @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
56717  * @cfg {Boolean}   closable      True if the panel can be closed/removed
56718  * @cfg {Boolean}   background    True if the panel should not be activated when it is added (defaults to false)
56719  * @cfg {String|HTMLElement|Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
56720  * @cfg {Roo.Toolbar}   toolbar       A toolbar for this panel
56721  * @cfg {Boolean} autoScroll    True to scroll overflow in this panel (use with {@link #fitToFrame})
56722  * @cfg {String} title          The title for this panel
56723  * @cfg {Array} adjustments     Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
56724  * @cfg {String} url            Calls {@link #setUrl} with this value
56725  * @cfg {String} region [required]   (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
56726  * @cfg {String|Object} params  When used with {@link #url}, calls {@link #setUrl} with this value
56727  * @cfg {Boolean} loadOnce      When used with {@link #url}, calls {@link #setUrl} with this value
56728  * @cfg {String}    content        Raw content to fill content panel with (uses setContent on construction.)
56729  * @cfg {String}    style  Extra style to add to the content panel
56730  * @cfg {Roo.menu.Menu} menu  popup menu
56731
56732  * @constructor
56733  * Create a new ContentPanel.
56734  * @param {String/HTMLElement/Roo.Element} el The container element for this panel
56735  * @param {String/Object} config A string to set only the title or a config object
56736  * @param {String} content (optional) Set the HTML content for this panel
56737  * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
56738  */
56739 Roo.ContentPanel = function(el, config, content){
56740     
56741      
56742     /*
56743     if(el.autoCreate || el.xtype){ // xtype is available if this is called from factory
56744         config = el;
56745         el = Roo.id();
56746     }
56747     if (config && config.parentLayout) { 
56748         el = config.parentLayout.el.createChild(); 
56749     }
56750     */
56751     if(el.autoCreate){ // xtype is available if this is called from factory
56752         config = el;
56753         el = Roo.id();
56754     }
56755     this.el = Roo.get(el);
56756     if(!this.el && config && config.autoCreate){
56757         if(typeof config.autoCreate == "object"){
56758             if(!config.autoCreate.id){
56759                 config.autoCreate.id = config.id||el;
56760             }
56761             this.el = Roo.DomHelper.append(document.body,
56762                         config.autoCreate, true);
56763         }else{
56764             this.el = Roo.DomHelper.append(document.body,
56765                         {tag: "div", cls: "x-layout-inactive-content", id: config.id||el}, true);
56766         }
56767     }
56768     
56769     
56770     this.closable = false;
56771     this.loaded = false;
56772     this.active = false;
56773     if(typeof config == "string"){
56774         this.title = config;
56775     }else{
56776         Roo.apply(this, config);
56777     }
56778     
56779     if (this.toolbar && !this.toolbar.el && this.toolbar.xtype) {
56780         this.wrapEl = this.el.wrap();
56781         this.toolbar.container = this.el.insertSibling(false, 'before');
56782         this.toolbar = new Roo.Toolbar(this.toolbar);
56783     }
56784     
56785     // xtype created footer. - not sure if will work as we normally have to render first..
56786     if (this.footer && !this.footer.el && this.footer.xtype) {
56787         if (!this.wrapEl) {
56788             this.wrapEl = this.el.wrap();
56789         }
56790     
56791         this.footer.container = this.wrapEl.createChild();
56792          
56793         this.footer = Roo.factory(this.footer, Roo);
56794         
56795     }
56796     
56797     if(this.resizeEl){
56798         this.resizeEl = Roo.get(this.resizeEl, true);
56799     }else{
56800         this.resizeEl = this.el;
56801     }
56802     // handle view.xtype
56803     
56804  
56805     
56806     
56807     this.addEvents({
56808         /**
56809          * @event activate
56810          * Fires when this panel is activated. 
56811          * @param {Roo.ContentPanel} this
56812          */
56813         "activate" : true,
56814         /**
56815          * @event deactivate
56816          * Fires when this panel is activated. 
56817          * @param {Roo.ContentPanel} this
56818          */
56819         "deactivate" : true,
56820
56821         /**
56822          * @event resize
56823          * Fires when this panel is resized if fitToFrame is true.
56824          * @param {Roo.ContentPanel} this
56825          * @param {Number} width The width after any component adjustments
56826          * @param {Number} height The height after any component adjustments
56827          */
56828         "resize" : true,
56829         
56830          /**
56831          * @event render
56832          * Fires when this tab is created
56833          * @param {Roo.ContentPanel} this
56834          */
56835         "render" : true
56836          
56837         
56838     });
56839     
56840
56841     
56842     
56843     if(this.autoScroll){
56844         this.resizeEl.setStyle("overflow", "auto");
56845     } else {
56846         // fix randome scrolling
56847         this.el.on('scroll', function() {
56848             Roo.log('fix random scolling');
56849             this.scrollTo('top',0); 
56850         });
56851     }
56852     content = content || this.content;
56853     if(content){
56854         this.setContent(content);
56855     }
56856     if(config && config.url){
56857         this.setUrl(this.url, this.params, this.loadOnce);
56858     }
56859     
56860     
56861     
56862     Roo.ContentPanel.superclass.constructor.call(this);
56863     
56864     if (this.view && typeof(this.view.xtype) != 'undefined') {
56865         this.view.el = this.el.appendChild(document.createElement("div"));
56866         this.view = Roo.factory(this.view); 
56867         this.view.render  &&  this.view.render(false, '');  
56868     }
56869     
56870     
56871     this.fireEvent('render', this);
56872 };
56873
56874 Roo.extend(Roo.ContentPanel, Roo.util.Observable, {
56875     tabTip:'',
56876     setRegion : function(region){
56877         this.region = region;
56878         if(region){
56879            this.el.replaceClass("x-layout-inactive-content", "x-layout-active-content");
56880         }else{
56881            this.el.replaceClass("x-layout-active-content", "x-layout-inactive-content");
56882         } 
56883     },
56884     
56885     /**
56886      * Returns the toolbar for this Panel if one was configured. 
56887      * @return {Roo.Toolbar} 
56888      */
56889     getToolbar : function(){
56890         return this.toolbar;
56891     },
56892     
56893     setActiveState : function(active){
56894         this.active = active;
56895         if(!active){
56896             this.fireEvent("deactivate", this);
56897         }else{
56898             this.fireEvent("activate", this);
56899         }
56900     },
56901     /**
56902      * Updates this panel's element
56903      * @param {String} content The new content
56904      * @param {Boolean} loadScripts (optional) true to look for and process scripts
56905     */
56906     setContent : function(content, loadScripts){
56907         this.el.update(content, loadScripts);
56908     },
56909
56910     ignoreResize : function(w, h){
56911         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
56912             return true;
56913         }else{
56914             this.lastSize = {width: w, height: h};
56915             return false;
56916         }
56917     },
56918     /**
56919      * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
56920      * @return {Roo.UpdateManager} The UpdateManager
56921      */
56922     getUpdateManager : function(){
56923         return this.el.getUpdateManager();
56924     },
56925      /**
56926      * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
56927      * @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:
56928 <pre><code>
56929 panel.load({
56930     url: "your-url.php",
56931     params: {param1: "foo", param2: "bar"}, // or a URL encoded string
56932     callback: yourFunction,
56933     scope: yourObject, //(optional scope)
56934     discardUrl: false,
56935     nocache: false,
56936     text: "Loading...",
56937     timeout: 30,
56938     scripts: false
56939 });
56940 </code></pre>
56941      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
56942      * 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.
56943      * @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}
56944      * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
56945      * @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.
56946      * @return {Roo.ContentPanel} this
56947      */
56948     load : function(){
56949         var um = this.el.getUpdateManager();
56950         um.update.apply(um, arguments);
56951         return this;
56952     },
56953
56954
56955     /**
56956      * 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.
56957      * @param {String/Function} url The URL to load the content from or a function to call to get the URL
56958      * @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)
56959      * @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)
56960      * @return {Roo.UpdateManager} The UpdateManager
56961      */
56962     setUrl : function(url, params, loadOnce){
56963         if(this.refreshDelegate){
56964             this.removeListener("activate", this.refreshDelegate);
56965         }
56966         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
56967         this.on("activate", this.refreshDelegate);
56968         return this.el.getUpdateManager();
56969     },
56970     
56971     _handleRefresh : function(url, params, loadOnce){
56972         if(!loadOnce || !this.loaded){
56973             var updater = this.el.getUpdateManager();
56974             updater.update(url, params, this._setLoaded.createDelegate(this));
56975         }
56976     },
56977     
56978     _setLoaded : function(){
56979         this.loaded = true;
56980     }, 
56981     
56982     /**
56983      * Returns this panel's id
56984      * @return {String} 
56985      */
56986     getId : function(){
56987         return this.el.id;
56988     },
56989     
56990     /** 
56991      * Returns this panel's element - used by regiosn to add.
56992      * @return {Roo.Element} 
56993      */
56994     getEl : function(){
56995         return this.wrapEl || this.el;
56996     },
56997     
56998     adjustForComponents : function(width, height)
56999     {
57000         //Roo.log('adjustForComponents ');
57001         if(this.resizeEl != this.el){
57002             width -= this.el.getFrameWidth('lr');
57003             height -= this.el.getFrameWidth('tb');
57004         }
57005         if(this.toolbar){
57006             var te = this.toolbar.getEl();
57007             height -= te.getHeight();
57008             te.setWidth(width);
57009         }
57010         if(this.footer){
57011             var te = this.footer.getEl();
57012             //Roo.log("footer:" + te.getHeight());
57013             
57014             height -= te.getHeight();
57015             te.setWidth(width);
57016         }
57017         
57018         
57019         if(this.adjustments){
57020             width += this.adjustments[0];
57021             height += this.adjustments[1];
57022         }
57023         return {"width": width, "height": height};
57024     },
57025     
57026     setSize : function(width, height){
57027         if(this.fitToFrame && !this.ignoreResize(width, height)){
57028             if(this.fitContainer && this.resizeEl != this.el){
57029                 this.el.setSize(width, height);
57030             }
57031             var size = this.adjustForComponents(width, height);
57032             this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
57033             this.fireEvent('resize', this, size.width, size.height);
57034         }
57035     },
57036     
57037     /**
57038      * Returns this panel's title
57039      * @return {String} 
57040      */
57041     getTitle : function(){
57042         return this.title;
57043     },
57044     
57045     /**
57046      * Set this panel's title
57047      * @param {String} title
57048      */
57049     setTitle : function(title){
57050         this.title = title;
57051         if(this.region){
57052             this.region.updatePanelTitle(this, title);
57053         }
57054     },
57055     
57056     /**
57057      * Returns true is this panel was configured to be closable
57058      * @return {Boolean} 
57059      */
57060     isClosable : function(){
57061         return this.closable;
57062     },
57063     
57064     beforeSlide : function(){
57065         this.el.clip();
57066         this.resizeEl.clip();
57067     },
57068     
57069     afterSlide : function(){
57070         this.el.unclip();
57071         this.resizeEl.unclip();
57072     },
57073     
57074     /**
57075      *   Force a content refresh from the URL specified in the {@link #setUrl} method.
57076      *   Will fail silently if the {@link #setUrl} method has not been called.
57077      *   This does not activate the panel, just updates its content.
57078      */
57079     refresh : function(){
57080         if(this.refreshDelegate){
57081            this.loaded = false;
57082            this.refreshDelegate();
57083         }
57084     },
57085     
57086     /**
57087      * Destroys this panel
57088      */
57089     destroy : function(){
57090         this.el.removeAllListeners();
57091         var tempEl = document.createElement("span");
57092         tempEl.appendChild(this.el.dom);
57093         tempEl.innerHTML = "";
57094         this.el.remove();
57095         this.el = null;
57096     },
57097     
57098     /**
57099      * form - if the content panel contains a form - this is a reference to it.
57100      * @type {Roo.form.Form}
57101      */
57102     form : false,
57103     /**
57104      * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
57105      *    This contains a reference to it.
57106      * @type {Roo.View}
57107      */
57108     view : false,
57109     
57110       /**
57111      * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
57112      * <pre><code>
57113
57114 layout.addxtype({
57115        xtype : 'Form',
57116        items: [ .... ]
57117    }
57118 );
57119
57120 </code></pre>
57121      * @param {Object} cfg Xtype definition of item to add.
57122      */
57123     
57124     addxtype : function(cfg) {
57125         // add form..
57126         if (cfg.xtype.match(/^Form$/)) {
57127             
57128             var el;
57129             //if (this.footer) {
57130             //    el = this.footer.container.insertSibling(false, 'before');
57131             //} else {
57132                 el = this.el.createChild();
57133             //}
57134
57135             this.form = new  Roo.form.Form(cfg);
57136             
57137             
57138             if ( this.form.allItems.length) {
57139                 this.form.render(el.dom);
57140             }
57141             return this.form;
57142         }
57143         // should only have one of theses..
57144         if ([ 'View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
57145             // views.. should not be just added - used named prop 'view''
57146             
57147             cfg.el = this.el.appendChild(document.createElement("div"));
57148             // factory?
57149             
57150             var ret = new Roo.factory(cfg);
57151              
57152              ret.render && ret.render(false, ''); // render blank..
57153             this.view = ret;
57154             return ret;
57155         }
57156         return false;
57157     }
57158 });
57159
57160 /**
57161  * @class Roo.GridPanel
57162  * @extends Roo.ContentPanel
57163  * @constructor
57164  * Create a new GridPanel.
57165  * @param {Roo.grid.Grid} grid The grid for this panel
57166  * @param {String/Object} config A string to set only the panel's title, or a config object
57167  */
57168 Roo.GridPanel = function(grid, config){
57169     
57170   
57171     this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
57172         {tag: "div", cls: "x-layout-grid-wrapper x-layout-inactive-content"}, true);
57173         
57174     this.wrapper.dom.appendChild(grid.getGridEl().dom);
57175     
57176     Roo.GridPanel.superclass.constructor.call(this, this.wrapper, config);
57177     
57178     if(this.toolbar){
57179         this.toolbar.el.insertBefore(this.wrapper.dom.firstChild);
57180     }
57181     // xtype created footer. - not sure if will work as we normally have to render first..
57182     if (this.footer && !this.footer.el && this.footer.xtype) {
57183         
57184         this.footer.container = this.grid.getView().getFooterPanel(true);
57185         this.footer.dataSource = this.grid.dataSource;
57186         this.footer = Roo.factory(this.footer, Roo);
57187         
57188     }
57189     
57190     grid.monitorWindowResize = false; // turn off autosizing
57191     grid.autoHeight = false;
57192     grid.autoWidth = false;
57193     this.grid = grid;
57194     this.grid.getGridEl().replaceClass("x-layout-inactive-content", "x-layout-component-panel");
57195 };
57196
57197 Roo.extend(Roo.GridPanel, Roo.ContentPanel, {
57198     getId : function(){
57199         return this.grid.id;
57200     },
57201     
57202     /**
57203      * Returns the grid for this panel
57204      * @return {Roo.grid.Grid} 
57205      */
57206     getGrid : function(){
57207         return this.grid;    
57208     },
57209     
57210     setSize : function(width, height){
57211         if(!this.ignoreResize(width, height)){
57212             var grid = this.grid;
57213             var size = this.adjustForComponents(width, height);
57214             grid.getGridEl().setSize(size.width, size.height);
57215             grid.autoSize();
57216         }
57217     },
57218     
57219     beforeSlide : function(){
57220         this.grid.getView().scroller.clip();
57221     },
57222     
57223     afterSlide : function(){
57224         this.grid.getView().scroller.unclip();
57225     },
57226     
57227     destroy : function(){
57228         this.grid.destroy();
57229         delete this.grid;
57230         Roo.GridPanel.superclass.destroy.call(this); 
57231     }
57232 });
57233
57234
57235 /**
57236  * @class Roo.NestedLayoutPanel
57237  * @extends Roo.ContentPanel
57238  * @constructor
57239  * Create a new NestedLayoutPanel.
57240  * 
57241  * 
57242  * @param {Roo.BorderLayout} layout [required] The layout for this panel
57243  * @param {String/Object} config A string to set only the title or a config object
57244  */
57245 Roo.NestedLayoutPanel = function(layout, config)
57246 {
57247     // construct with only one argument..
57248     /* FIXME - implement nicer consturctors
57249     if (layout.layout) {
57250         config = layout;
57251         layout = config.layout;
57252         delete config.layout;
57253     }
57254     if (layout.xtype && !layout.getEl) {
57255         // then layout needs constructing..
57256         layout = Roo.factory(layout, Roo);
57257     }
57258     */
57259     
57260     
57261     Roo.NestedLayoutPanel.superclass.constructor.call(this, layout.getEl(), config);
57262     
57263     layout.monitorWindowResize = false; // turn off autosizing
57264     this.layout = layout;
57265     this.layout.getEl().addClass("x-layout-nested-layout");
57266     
57267     
57268     
57269     
57270 };
57271
57272 Roo.extend(Roo.NestedLayoutPanel, Roo.ContentPanel, {
57273
57274     setSize : function(width, height){
57275         if(!this.ignoreResize(width, height)){
57276             var size = this.adjustForComponents(width, height);
57277             var el = this.layout.getEl();
57278             el.setSize(size.width, size.height);
57279             var touch = el.dom.offsetWidth;
57280             this.layout.layout();
57281             // ie requires a double layout on the first pass
57282             if(Roo.isIE && !this.initialized){
57283                 this.initialized = true;
57284                 this.layout.layout();
57285             }
57286         }
57287     },
57288     
57289     // activate all subpanels if not currently active..
57290     
57291     setActiveState : function(active){
57292         this.active = active;
57293         if(!active){
57294             this.fireEvent("deactivate", this);
57295             return;
57296         }
57297         
57298         this.fireEvent("activate", this);
57299         // not sure if this should happen before or after..
57300         if (!this.layout) {
57301             return; // should not happen..
57302         }
57303         var reg = false;
57304         for (var r in this.layout.regions) {
57305             reg = this.layout.getRegion(r);
57306             if (reg.getActivePanel()) {
57307                 //reg.showPanel(reg.getActivePanel()); // force it to activate.. 
57308                 reg.setActivePanel(reg.getActivePanel());
57309                 continue;
57310             }
57311             if (!reg.panels.length) {
57312                 continue;
57313             }
57314             reg.showPanel(reg.getPanel(0));
57315         }
57316         
57317         
57318         
57319         
57320     },
57321     
57322     /**
57323      * Returns the nested BorderLayout for this panel
57324      * @return {Roo.BorderLayout} 
57325      */
57326     getLayout : function(){
57327         return this.layout;
57328     },
57329     
57330      /**
57331      * Adds a xtype elements to the layout of the nested panel
57332      * <pre><code>
57333
57334 panel.addxtype({
57335        xtype : 'ContentPanel',
57336        region: 'west',
57337        items: [ .... ]
57338    }
57339 );
57340
57341 panel.addxtype({
57342         xtype : 'NestedLayoutPanel',
57343         region: 'west',
57344         layout: {
57345            center: { },
57346            west: { }   
57347         },
57348         items : [ ... list of content panels or nested layout panels.. ]
57349    }
57350 );
57351 </code></pre>
57352      * @param {Object} cfg Xtype definition of item to add.
57353      */
57354     addxtype : function(cfg) {
57355         return this.layout.addxtype(cfg);
57356     
57357     }
57358 });
57359
57360 Roo.ScrollPanel = function(el, config, content){
57361     config = config || {};
57362     config.fitToFrame = true;
57363     Roo.ScrollPanel.superclass.constructor.call(this, el, config, content);
57364     
57365     this.el.dom.style.overflow = "hidden";
57366     var wrap = this.el.wrap({cls: "x-scroller x-layout-inactive-content"});
57367     this.el.removeClass("x-layout-inactive-content");
57368     this.el.on("mousewheel", this.onWheel, this);
57369
57370     var up = wrap.createChild({cls: "x-scroller-up", html: "&#160;"}, this.el.dom);
57371     var down = wrap.createChild({cls: "x-scroller-down", html: "&#160;"});
57372     up.unselectable(); down.unselectable();
57373     up.on("click", this.scrollUp, this);
57374     down.on("click", this.scrollDown, this);
57375     up.addClassOnOver("x-scroller-btn-over");
57376     down.addClassOnOver("x-scroller-btn-over");
57377     up.addClassOnClick("x-scroller-btn-click");
57378     down.addClassOnClick("x-scroller-btn-click");
57379     this.adjustments = [0, -(up.getHeight() + down.getHeight())];
57380
57381     this.resizeEl = this.el;
57382     this.el = wrap; this.up = up; this.down = down;
57383 };
57384
57385 Roo.extend(Roo.ScrollPanel, Roo.ContentPanel, {
57386     increment : 100,
57387     wheelIncrement : 5,
57388     scrollUp : function(){
57389         this.resizeEl.scroll("up", this.increment, {callback: this.afterScroll, scope: this});
57390     },
57391
57392     scrollDown : function(){
57393         this.resizeEl.scroll("down", this.increment, {callback: this.afterScroll, scope: this});
57394     },
57395
57396     afterScroll : function(){
57397         var el = this.resizeEl;
57398         var t = el.dom.scrollTop, h = el.dom.scrollHeight, ch = el.dom.clientHeight;
57399         this.up[t == 0 ? "addClass" : "removeClass"]("x-scroller-btn-disabled");
57400         this.down[h - t <= ch ? "addClass" : "removeClass"]("x-scroller-btn-disabled");
57401     },
57402
57403     setSize : function(){
57404         Roo.ScrollPanel.superclass.setSize.apply(this, arguments);
57405         this.afterScroll();
57406     },
57407
57408     onWheel : function(e){
57409         var d = e.getWheelDelta();
57410         this.resizeEl.dom.scrollTop -= (d*this.wheelIncrement);
57411         this.afterScroll();
57412         e.stopEvent();
57413     },
57414
57415     setContent : function(content, loadScripts){
57416         this.resizeEl.update(content, loadScripts);
57417     }
57418
57419 });
57420
57421
57422
57423 /**
57424  * @class Roo.TreePanel
57425  * @extends Roo.ContentPanel
57426  * Treepanel component
57427  * 
57428  * @constructor
57429  * Create a new TreePanel. - defaults to fit/scoll contents.
57430  * @param {String/Object} config A string to set only the panel's title, or a config object
57431  */
57432 Roo.TreePanel = function(config){
57433     var el = config.el;
57434     var tree = config.tree;
57435     delete config.tree; 
57436     delete config.el; // hopefull!
57437     
57438     // wrapper for IE7 strict & safari scroll issue
57439     
57440     var treeEl = el.createChild();
57441     config.resizeEl = treeEl;
57442     
57443     
57444     
57445     Roo.TreePanel.superclass.constructor.call(this, el, config);
57446  
57447  
57448     this.tree = new Roo.tree.TreePanel(treeEl , tree);
57449     //console.log(tree);
57450     this.on('activate', function()
57451     {
57452         if (this.tree.rendered) {
57453             return;
57454         }
57455         //console.log('render tree');
57456         this.tree.render();
57457     });
57458     // this should not be needed.. - it's actually the 'el' that resizes?
57459     // actuall it breaks the containerScroll - dragging nodes auto scroll at top
57460     
57461     //this.on('resize',  function (cp, w, h) {
57462     //        this.tree.innerCt.setWidth(w);
57463     //        this.tree.innerCt.setHeight(h);
57464     //        //this.tree.innerCt.setStyle('overflow-y', 'auto');
57465     //});
57466
57467         
57468     
57469 };
57470
57471 Roo.extend(Roo.TreePanel, Roo.ContentPanel, {   
57472     fitToFrame : true,
57473     autoScroll : true,
57474     /*
57475      * @cfg {Roo.tree.TreePanel} tree [required] The tree TreePanel, with config etc.
57476      */
57477     tree : false
57478
57479 });
57480
57481
57482
57483
57484
57485
57486
57487
57488
57489
57490
57491 /*
57492  * Based on:
57493  * Ext JS Library 1.1.1
57494  * Copyright(c) 2006-2007, Ext JS, LLC.
57495  *
57496  * Originally Released Under LGPL - original licence link has changed is not relivant.
57497  *
57498  * Fork - LGPL
57499  * <script type="text/javascript">
57500  */
57501  
57502
57503 /**
57504  * @class Roo.ReaderLayout
57505  * @extends Roo.BorderLayout
57506  * This is a pre-built layout that represents a classic, 5-pane application.  It consists of a header, a primary
57507  * center region containing two nested regions (a top one for a list view and one for item preview below),
57508  * and regions on either side that can be used for navigation, application commands, informational displays, etc.
57509  * The setup and configuration work exactly the same as it does for a {@link Roo.BorderLayout} - this class simply
57510  * expedites the setup of the overall layout and regions for this common application style.
57511  * Example:
57512  <pre><code>
57513 var reader = new Roo.ReaderLayout();
57514 var CP = Roo.ContentPanel;  // shortcut for adding
57515
57516 reader.beginUpdate();
57517 reader.add("north", new CP("north", "North"));
57518 reader.add("west", new CP("west", {title: "West"}));
57519 reader.add("east", new CP("east", {title: "East"}));
57520
57521 reader.regions.listView.add(new CP("listView", "List"));
57522 reader.regions.preview.add(new CP("preview", "Preview"));
57523 reader.endUpdate();
57524 </code></pre>
57525 * @constructor
57526 * Create a new ReaderLayout
57527 * @param {Object} config Configuration options
57528 * @param {String/HTMLElement/Element} container (optional) The container this layout is bound to (defaults to
57529 * document.body if omitted)
57530 */
57531 Roo.ReaderLayout = function(config, renderTo){
57532     var c = config || {size:{}};
57533     Roo.ReaderLayout.superclass.constructor.call(this, renderTo || document.body, {
57534         north: c.north !== false ? Roo.apply({
57535             split:false,
57536             initialSize: 32,
57537             titlebar: false
57538         }, c.north) : false,
57539         west: c.west !== false ? Roo.apply({
57540             split:true,
57541             initialSize: 200,
57542             minSize: 175,
57543             maxSize: 400,
57544             titlebar: true,
57545             collapsible: true,
57546             animate: true,
57547             margins:{left:5,right:0,bottom:5,top:5},
57548             cmargins:{left:5,right:5,bottom:5,top:5}
57549         }, c.west) : false,
57550         east: c.east !== false ? Roo.apply({
57551             split:true,
57552             initialSize: 200,
57553             minSize: 175,
57554             maxSize: 400,
57555             titlebar: true,
57556             collapsible: true,
57557             animate: true,
57558             margins:{left:0,right:5,bottom:5,top:5},
57559             cmargins:{left:5,right:5,bottom:5,top:5}
57560         }, c.east) : false,
57561         center: Roo.apply({
57562             tabPosition: 'top',
57563             autoScroll:false,
57564             closeOnTab: true,
57565             titlebar:false,
57566             margins:{left:c.west!==false ? 0 : 5,right:c.east!==false ? 0 : 5,bottom:5,top:2}
57567         }, c.center)
57568     });
57569
57570     this.el.addClass('x-reader');
57571
57572     this.beginUpdate();
57573
57574     var inner = new Roo.BorderLayout(Roo.get(document.body).createChild(), {
57575         south: c.preview !== false ? Roo.apply({
57576             split:true,
57577             initialSize: 200,
57578             minSize: 100,
57579             autoScroll:true,
57580             collapsible:true,
57581             titlebar: true,
57582             cmargins:{top:5,left:0, right:0, bottom:0}
57583         }, c.preview) : false,
57584         center: Roo.apply({
57585             autoScroll:false,
57586             titlebar:false,
57587             minHeight:200
57588         }, c.listView)
57589     });
57590     this.add('center', new Roo.NestedLayoutPanel(inner,
57591             Roo.apply({title: c.mainTitle || '',tabTip:''},c.innerPanelCfg)));
57592
57593     this.endUpdate();
57594
57595     this.regions.preview = inner.getRegion('south');
57596     this.regions.listView = inner.getRegion('center');
57597 };
57598
57599 Roo.extend(Roo.ReaderLayout, Roo.BorderLayout);/*
57600  * Based on:
57601  * Ext JS Library 1.1.1
57602  * Copyright(c) 2006-2007, Ext JS, LLC.
57603  *
57604  * Originally Released Under LGPL - original licence link has changed is not relivant.
57605  *
57606  * Fork - LGPL
57607  * <script type="text/javascript">
57608  */
57609  
57610 /**
57611  * @class Roo.grid.Grid
57612  * @extends Roo.util.Observable
57613  * This class represents the primary interface of a component based grid control.
57614  * <br><br>Usage:<pre><code>
57615  var grid = new Roo.grid.Grid("my-container-id", {
57616      ds: myDataStore,
57617      cm: myColModel,
57618      selModel: mySelectionModel,
57619      autoSizeColumns: true,
57620      monitorWindowResize: false,
57621      trackMouseOver: true
57622  });
57623  // set any options
57624  grid.render();
57625  * </code></pre>
57626  * <b>Common Problems:</b><br/>
57627  * - Grid does not resize properly when going smaller: Setting overflow hidden on the container
57628  * element will correct this<br/>
57629  * - If you get el.style[camel]= NaNpx or -2px or something related, be certain you have given your container element
57630  * dimensions. The grid adapts to your container's size, if your container has no size defined then the results
57631  * are unpredictable.<br/>
57632  * - Do not render the grid into an element with display:none. Try using visibility:hidden. Otherwise there is no way for the
57633  * grid to calculate dimensions/offsets.<br/>
57634   * @constructor
57635  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
57636  * The container MUST have some type of size defined for the grid to fill. The container will be
57637  * automatically set to position relative if it isn't already.
57638  * @param {Object} config A config object that sets properties on this grid.
57639  */
57640 Roo.grid.Grid = function(container, config){
57641         // initialize the container
57642         this.container = Roo.get(container);
57643         this.container.update("");
57644         this.container.setStyle("overflow", "hidden");
57645     this.container.addClass('x-grid-container');
57646
57647     this.id = this.container.id;
57648
57649     Roo.apply(this, config);
57650     // check and correct shorthanded configs
57651     if(this.ds){
57652         this.dataSource = this.ds;
57653         delete this.ds;
57654     }
57655     if(this.cm){
57656         this.colModel = this.cm;
57657         delete this.cm;
57658     }
57659     if(this.sm){
57660         this.selModel = this.sm;
57661         delete this.sm;
57662     }
57663
57664     if (this.selModel) {
57665         this.selModel = Roo.factory(this.selModel, Roo.grid);
57666         this.sm = this.selModel;
57667         this.sm.xmodule = this.xmodule || false;
57668     }
57669     if (typeof(this.colModel.config) == 'undefined') {
57670         this.colModel = new Roo.grid.ColumnModel(this.colModel);
57671         this.cm = this.colModel;
57672         this.cm.xmodule = this.xmodule || false;
57673     }
57674     if (this.dataSource) {
57675         this.dataSource= Roo.factory(this.dataSource, Roo.data);
57676         this.ds = this.dataSource;
57677         this.ds.xmodule = this.xmodule || false;
57678          
57679     }
57680     
57681     
57682     
57683     if(this.width){
57684         this.container.setWidth(this.width);
57685     }
57686
57687     if(this.height){
57688         this.container.setHeight(this.height);
57689     }
57690     /** @private */
57691         this.addEvents({
57692         // raw events
57693         /**
57694          * @event click
57695          * The raw click event for the entire grid.
57696          * @param {Roo.EventObject} e
57697          */
57698         "click" : true,
57699         /**
57700          * @event dblclick
57701          * The raw dblclick event for the entire grid.
57702          * @param {Roo.EventObject} e
57703          */
57704         "dblclick" : true,
57705         /**
57706          * @event contextmenu
57707          * The raw contextmenu event for the entire grid.
57708          * @param {Roo.EventObject} e
57709          */
57710         "contextmenu" : true,
57711         /**
57712          * @event mousedown
57713          * The raw mousedown event for the entire grid.
57714          * @param {Roo.EventObject} e
57715          */
57716         "mousedown" : true,
57717         /**
57718          * @event mouseup
57719          * The raw mouseup event for the entire grid.
57720          * @param {Roo.EventObject} e
57721          */
57722         "mouseup" : true,
57723         /**
57724          * @event mouseover
57725          * The raw mouseover event for the entire grid.
57726          * @param {Roo.EventObject} e
57727          */
57728         "mouseover" : true,
57729         /**
57730          * @event mouseout
57731          * The raw mouseout event for the entire grid.
57732          * @param {Roo.EventObject} e
57733          */
57734         "mouseout" : true,
57735         /**
57736          * @event keypress
57737          * The raw keypress event for the entire grid.
57738          * @param {Roo.EventObject} e
57739          */
57740         "keypress" : true,
57741         /**
57742          * @event keydown
57743          * The raw keydown event for the entire grid.
57744          * @param {Roo.EventObject} e
57745          */
57746         "keydown" : true,
57747
57748         // custom events
57749
57750         /**
57751          * @event cellclick
57752          * Fires when a cell is clicked
57753          * @param {Grid} this
57754          * @param {Number} rowIndex
57755          * @param {Number} columnIndex
57756          * @param {Roo.EventObject} e
57757          */
57758         "cellclick" : true,
57759         /**
57760          * @event celldblclick
57761          * Fires when a cell is double clicked
57762          * @param {Grid} this
57763          * @param {Number} rowIndex
57764          * @param {Number} columnIndex
57765          * @param {Roo.EventObject} e
57766          */
57767         "celldblclick" : true,
57768         /**
57769          * @event rowclick
57770          * Fires when a row is clicked
57771          * @param {Grid} this
57772          * @param {Number} rowIndex
57773          * @param {Roo.EventObject} e
57774          */
57775         "rowclick" : true,
57776         /**
57777          * @event rowdblclick
57778          * Fires when a row is double clicked
57779          * @param {Grid} this
57780          * @param {Number} rowIndex
57781          * @param {Roo.EventObject} e
57782          */
57783         "rowdblclick" : true,
57784         /**
57785          * @event headerclick
57786          * Fires when a header is clicked
57787          * @param {Grid} this
57788          * @param {Number} columnIndex
57789          * @param {Roo.EventObject} e
57790          */
57791         "headerclick" : true,
57792         /**
57793          * @event headerdblclick
57794          * Fires when a header cell is double clicked
57795          * @param {Grid} this
57796          * @param {Number} columnIndex
57797          * @param {Roo.EventObject} e
57798          */
57799         "headerdblclick" : true,
57800         /**
57801          * @event rowcontextmenu
57802          * Fires when a row is right clicked
57803          * @param {Grid} this
57804          * @param {Number} rowIndex
57805          * @param {Roo.EventObject} e
57806          */
57807         "rowcontextmenu" : true,
57808         /**
57809          * @event cellcontextmenu
57810          * Fires when a cell is right clicked
57811          * @param {Grid} this
57812          * @param {Number} rowIndex
57813          * @param {Number} cellIndex
57814          * @param {Roo.EventObject} e
57815          */
57816          "cellcontextmenu" : true,
57817         /**
57818          * @event headercontextmenu
57819          * Fires when a header is right clicked
57820          * @param {Grid} this
57821          * @param {Number} columnIndex
57822          * @param {Roo.EventObject} e
57823          */
57824         "headercontextmenu" : true,
57825         /**
57826          * @event bodyscroll
57827          * Fires when the body element is scrolled
57828          * @param {Number} scrollLeft
57829          * @param {Number} scrollTop
57830          */
57831         "bodyscroll" : true,
57832         /**
57833          * @event columnresize
57834          * Fires when the user resizes a column
57835          * @param {Number} columnIndex
57836          * @param {Number} newSize
57837          */
57838         "columnresize" : true,
57839         /**
57840          * @event columnmove
57841          * Fires when the user moves a column
57842          * @param {Number} oldIndex
57843          * @param {Number} newIndex
57844          */
57845         "columnmove" : true,
57846         /**
57847          * @event startdrag
57848          * Fires when row(s) start being dragged
57849          * @param {Grid} this
57850          * @param {Roo.GridDD} dd The drag drop object
57851          * @param {event} e The raw browser event
57852          */
57853         "startdrag" : true,
57854         /**
57855          * @event enddrag
57856          * Fires when a drag operation is complete
57857          * @param {Grid} this
57858          * @param {Roo.GridDD} dd The drag drop object
57859          * @param {event} e The raw browser event
57860          */
57861         "enddrag" : true,
57862         /**
57863          * @event dragdrop
57864          * Fires when dragged row(s) are dropped on a valid DD target
57865          * @param {Grid} this
57866          * @param {Roo.GridDD} dd The drag drop object
57867          * @param {String} targetId The target drag drop object
57868          * @param {event} e The raw browser event
57869          */
57870         "dragdrop" : true,
57871         /**
57872          * @event dragover
57873          * Fires while row(s) are being dragged. "targetId" is the id of the Yahoo.util.DD object the selected rows are being dragged over.
57874          * @param {Grid} this
57875          * @param {Roo.GridDD} dd The drag drop object
57876          * @param {String} targetId The target drag drop object
57877          * @param {event} e The raw browser event
57878          */
57879         "dragover" : true,
57880         /**
57881          * @event dragenter
57882          *  Fires when the dragged row(s) first cross another DD target while being dragged
57883          * @param {Grid} this
57884          * @param {Roo.GridDD} dd The drag drop object
57885          * @param {String} targetId The target drag drop object
57886          * @param {event} e The raw browser event
57887          */
57888         "dragenter" : true,
57889         /**
57890          * @event dragout
57891          * Fires when the dragged row(s) leave another DD target while being dragged
57892          * @param {Grid} this
57893          * @param {Roo.GridDD} dd The drag drop object
57894          * @param {String} targetId The target drag drop object
57895          * @param {event} e The raw browser event
57896          */
57897         "dragout" : true,
57898         /**
57899          * @event rowclass
57900          * Fires when a row is rendered, so you can change add a style to it.
57901          * @param {GridView} gridview   The grid view
57902          * @param {Object} rowcfg   contains record  rowIndex and rowClass - set rowClass to add a style.
57903          */
57904         'rowclass' : true,
57905
57906         /**
57907          * @event render
57908          * Fires when the grid is rendered
57909          * @param {Grid} grid
57910          */
57911         'render' : true
57912     });
57913
57914     Roo.grid.Grid.superclass.constructor.call(this);
57915 };
57916 Roo.extend(Roo.grid.Grid, Roo.util.Observable, {
57917     
57918     /**
57919          * @cfg {Roo.grid.AbstractSelectionModel} sm The selection Model (default = Roo.grid.RowSelectionModel)
57920          */
57921         /**
57922          * @cfg {Roo.grid.GridView} view  The view that renders the grid (default = Roo.grid.GridView)
57923          */
57924         /**
57925          * @cfg {Roo.grid.ColumnModel} cm[] The columns of the grid
57926          */
57927         /**
57928          * @cfg {Roo.grid.Store} ds The data store for the grid
57929          */
57930         /**
57931          * @cfg {Roo.Toolbar} toolbar a toolbar for buttons etc.
57932          */
57933         /**
57934      * @cfg {String} ddGroup - drag drop group.
57935      */
57936       /**
57937      * @cfg {String} dragGroup - drag group (?? not sure if needed.)
57938      */
57939
57940     /**
57941      * @cfg {Number} minColumnWidth The minimum width a column can be resized to. Default is 25.
57942      */
57943     minColumnWidth : 25,
57944
57945     /**
57946      * @cfg {Boolean} autoSizeColumns True to automatically resize the columns to fit their content
57947      * <b>on initial render.</b> It is more efficient to explicitly size the columns
57948      * through the ColumnModel's {@link Roo.grid.ColumnModel#width} config option.  Default is false.
57949      */
57950     autoSizeColumns : false,
57951
57952     /**
57953      * @cfg {Boolean} autoSizeHeaders True to measure headers with column data when auto sizing columns. Default is true.
57954      */
57955     autoSizeHeaders : true,
57956
57957     /**
57958      * @cfg {Boolean} monitorWindowResize True to autoSize the grid when the window resizes. Default is true.
57959      */
57960     monitorWindowResize : true,
57961
57962     /**
57963      * @cfg {Boolean} maxRowsToMeasure If autoSizeColumns is on, maxRowsToMeasure can be used to limit the number of
57964      * rows measured to get a columns size. Default is 0 (all rows).
57965      */
57966     maxRowsToMeasure : 0,
57967
57968     /**
57969      * @cfg {Boolean} trackMouseOver True to highlight rows when the mouse is over. Default is true.
57970      */
57971     trackMouseOver : true,
57972
57973     /**
57974     * @cfg {Boolean} enableDrag  True to enable drag of rows. Default is false. (double check if this is needed?)
57975     */
57976       /**
57977     * @cfg {Boolean} enableDrop  True to enable drop of elements. Default is false. (double check if this is needed?)
57978     */
57979     
57980     /**
57981     * @cfg {Boolean} enableDragDrop True to enable drag and drop of rows. Default is false.
57982     */
57983     enableDragDrop : false,
57984     
57985     /**
57986     * @cfg {Boolean} enableColumnMove True to enable drag and drop reorder of columns. Default is true.
57987     */
57988     enableColumnMove : true,
57989     
57990     /**
57991     * @cfg {Boolean} enableColumnHide True to enable hiding of columns with the header context menu. Default is true.
57992     */
57993     enableColumnHide : true,
57994     
57995     /**
57996     * @cfg {Boolean} enableRowHeightSync True to manually sync row heights across locked and not locked rows. Default is false.
57997     */
57998     enableRowHeightSync : false,
57999     
58000     /**
58001     * @cfg {Boolean} stripeRows True to stripe the rows.  Default is true.
58002     */
58003     stripeRows : true,
58004     
58005     /**
58006     * @cfg {Boolean} autoHeight True to fit the height of the grid container to the height of the data. Default is false.
58007     */
58008     autoHeight : false,
58009
58010     /**
58011      * @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.
58012      */
58013     autoExpandColumn : false,
58014
58015     /**
58016     * @cfg {Number} autoExpandMin The minimum width the autoExpandColumn can have (if enabled).
58017     * Default is 50.
58018     */
58019     autoExpandMin : 50,
58020
58021     /**
58022     * @cfg {Number} autoExpandMax The maximum width the autoExpandColumn can have (if enabled). Default is 1000.
58023     */
58024     autoExpandMax : 1000,
58025
58026     /**
58027     * @cfg {Object} view The {@link Roo.grid.GridView} used by the grid. This can be set before a call to render().
58028     */
58029     view : null,
58030
58031     /**
58032     * @cfg {Object} loadMask An {@link Roo.LoadMask} config or true to mask the grid while loading. Default is false.
58033     */
58034     loadMask : false,
58035     /**
58036     * @cfg {Roo.dd.DropTarget} dropTarget An {@link Roo.dd.DropTarget} config
58037     */
58038     dropTarget: false,
58039     
58040    
58041     
58042     // private
58043     rendered : false,
58044
58045     /**
58046     * @cfg {Boolean} autoWidth True to set the grid's width to the default total width of the grid's columns instead
58047     * of a fixed width. Default is false.
58048     */
58049     /**
58050     * @cfg {Number} maxHeight Sets the maximum height of the grid - ignored if autoHeight is not on.
58051     */
58052     
58053     
58054     /**
58055     * @cfg {String} ddText Configures the text is the drag proxy (defaults to "%0 selected row(s)").
58056     * %0 is replaced with the number of selected rows.
58057     */
58058     ddText : "{0} selected row{1}",
58059     
58060     
58061     /**
58062      * Called once after all setup has been completed and the grid is ready to be rendered.
58063      * @return {Roo.grid.Grid} this
58064      */
58065     render : function()
58066     {
58067         var c = this.container;
58068         // try to detect autoHeight/width mode
58069         if((!c.dom.offsetHeight || c.dom.offsetHeight < 20) || c.getStyle("height") == "auto"){
58070             this.autoHeight = true;
58071         }
58072         var view = this.getView();
58073         view.init(this);
58074
58075         c.on("click", this.onClick, this);
58076         c.on("dblclick", this.onDblClick, this);
58077         c.on("contextmenu", this.onContextMenu, this);
58078         c.on("keydown", this.onKeyDown, this);
58079         if (Roo.isTouch) {
58080             c.on("touchstart", this.onTouchStart, this);
58081         }
58082
58083         this.relayEvents(c, ["mousedown","mouseup","mouseover","mouseout","keypress"]);
58084
58085         this.getSelectionModel().init(this);
58086
58087         view.render();
58088
58089         if(this.loadMask){
58090             this.loadMask = new Roo.LoadMask(this.container,
58091                     Roo.apply({store:this.dataSource}, this.loadMask));
58092         }
58093         
58094         
58095         if (this.toolbar && this.toolbar.xtype) {
58096             this.toolbar.container = this.getView().getHeaderPanel(true);
58097             this.toolbar = new Roo.Toolbar(this.toolbar);
58098         }
58099         if (this.footer && this.footer.xtype) {
58100             this.footer.dataSource = this.getDataSource();
58101             this.footer.container = this.getView().getFooterPanel(true);
58102             this.footer = Roo.factory(this.footer, Roo);
58103         }
58104         if (this.dropTarget && this.dropTarget.xtype) {
58105             delete this.dropTarget.xtype;
58106             this.dropTarget =  new Roo.dd.DropTarget(this.getView().mainBody, this.dropTarget);
58107         }
58108         
58109         
58110         this.rendered = true;
58111         this.fireEvent('render', this);
58112         return this;
58113     },
58114
58115     /**
58116      * Reconfigures the grid to use a different Store and Column Model.
58117      * The View will be bound to the new objects and refreshed.
58118      * @param {Roo.data.Store} dataSource The new {@link Roo.data.Store} object
58119      * @param {Roo.grid.ColumnModel} The new {@link Roo.grid.ColumnModel} object
58120      */
58121     reconfigure : function(dataSource, colModel){
58122         if(this.loadMask){
58123             this.loadMask.destroy();
58124             this.loadMask = new Roo.LoadMask(this.container,
58125                     Roo.apply({store:dataSource}, this.loadMask));
58126         }
58127         this.view.bind(dataSource, colModel);
58128         this.dataSource = dataSource;
58129         this.colModel = colModel;
58130         this.view.refresh(true);
58131     },
58132     /**
58133      * addColumns
58134      * Add's a column, default at the end..
58135      
58136      * @param {int} position to add (default end)
58137      * @param {Array} of objects of column configuration see {@link Roo.grid.ColumnModel} 
58138      */
58139     addColumns : function(pos, ar)
58140     {
58141         
58142         for (var i =0;i< ar.length;i++) {
58143             var cfg = ar[i];
58144             cfg.id = typeof(cfg.id) == 'undefined' ? Roo.id() : cfg.id; // don't normally use this..
58145             this.cm.lookup[cfg.id] = cfg;
58146         }
58147         
58148         
58149         if (typeof(pos) == 'undefined' || pos >= this.cm.config.length) {
58150             pos = this.cm.config.length; //this.cm.config.push(cfg);
58151         } 
58152         pos = Math.max(0,pos);
58153         ar.unshift(0);
58154         ar.unshift(pos);
58155         this.cm.config.splice.apply(this.cm.config, ar);
58156         
58157         
58158         
58159         this.view.generateRules(this.cm);
58160         this.view.refresh(true);
58161         
58162     },
58163     
58164     
58165     
58166     
58167     // private
58168     onKeyDown : function(e){
58169         this.fireEvent("keydown", e);
58170     },
58171
58172     /**
58173      * Destroy this grid.
58174      * @param {Boolean} removeEl True to remove the element
58175      */
58176     destroy : function(removeEl, keepListeners){
58177         if(this.loadMask){
58178             this.loadMask.destroy();
58179         }
58180         var c = this.container;
58181         c.removeAllListeners();
58182         this.view.destroy();
58183         this.colModel.purgeListeners();
58184         if(!keepListeners){
58185             this.purgeListeners();
58186         }
58187         c.update("");
58188         if(removeEl === true){
58189             c.remove();
58190         }
58191     },
58192
58193     // private
58194     processEvent : function(name, e){
58195         // does this fire select???
58196         //Roo.log('grid:processEvent '  + name);
58197         
58198         if (name != 'touchstart' ) {
58199             this.fireEvent(name, e);    
58200         }
58201         
58202         var t = e.getTarget();
58203         var v = this.view;
58204         var header = v.findHeaderIndex(t);
58205         if(header !== false){
58206             var ename = name == 'touchstart' ? 'click' : name;
58207              
58208             this.fireEvent("header" + ename, this, header, e);
58209         }else{
58210             var row = v.findRowIndex(t);
58211             var cell = v.findCellIndex(t);
58212             if (name == 'touchstart') {
58213                 // first touch is always a click.
58214                 // hopefull this happens after selection is updated.?
58215                 name = false;
58216                 
58217                 if (typeof(this.selModel.getSelectedCell) != 'undefined') {
58218                     var cs = this.selModel.getSelectedCell();
58219                     if (row == cs[0] && cell == cs[1]){
58220                         name = 'dblclick';
58221                     }
58222                 }
58223                 if (typeof(this.selModel.getSelections) != 'undefined') {
58224                     var cs = this.selModel.getSelections();
58225                     var ds = this.dataSource;
58226                     if (cs.length == 1 && ds.getAt(row) == cs[0]){
58227                         name = 'dblclick';
58228                     }
58229                 }
58230                 if (!name) {
58231                     return;
58232                 }
58233             }
58234             
58235             
58236             if(row !== false){
58237                 this.fireEvent("row" + name, this, row, e);
58238                 if(cell !== false){
58239                     this.fireEvent("cell" + name, this, row, cell, e);
58240                 }
58241             }
58242         }
58243     },
58244
58245     // private
58246     onClick : function(e){
58247         this.processEvent("click", e);
58248     },
58249    // private
58250     onTouchStart : function(e){
58251         this.processEvent("touchstart", e);
58252     },
58253
58254     // private
58255     onContextMenu : function(e, t){
58256         this.processEvent("contextmenu", e);
58257     },
58258
58259     // private
58260     onDblClick : function(e){
58261         this.processEvent("dblclick", e);
58262     },
58263
58264     // private
58265     walkCells : function(row, col, step, fn, scope){
58266         var cm = this.colModel, clen = cm.getColumnCount();
58267         var ds = this.dataSource, rlen = ds.getCount(), first = true;
58268         if(step < 0){
58269             if(col < 0){
58270                 row--;
58271                 first = false;
58272             }
58273             while(row >= 0){
58274                 if(!first){
58275                     col = clen-1;
58276                 }
58277                 first = false;
58278                 while(col >= 0){
58279                     if(fn.call(scope || this, row, col, cm) === true){
58280                         return [row, col];
58281                     }
58282                     col--;
58283                 }
58284                 row--;
58285             }
58286         } else {
58287             if(col >= clen){
58288                 row++;
58289                 first = false;
58290             }
58291             while(row < rlen){
58292                 if(!first){
58293                     col = 0;
58294                 }
58295                 first = false;
58296                 while(col < clen){
58297                     if(fn.call(scope || this, row, col, cm) === true){
58298                         return [row, col];
58299                     }
58300                     col++;
58301                 }
58302                 row++;
58303             }
58304         }
58305         return null;
58306     },
58307
58308     // private
58309     getSelections : function(){
58310         return this.selModel.getSelections();
58311     },
58312
58313     /**
58314      * Causes the grid to manually recalculate its dimensions. Generally this is done automatically,
58315      * but if manual update is required this method will initiate it.
58316      */
58317     autoSize : function(){
58318         if(this.rendered){
58319             this.view.layout();
58320             if(this.view.adjustForScroll){
58321                 this.view.adjustForScroll();
58322             }
58323         }
58324     },
58325
58326     /**
58327      * Returns the grid's underlying element.
58328      * @return {Element} The element
58329      */
58330     getGridEl : function(){
58331         return this.container;
58332     },
58333
58334     // private for compatibility, overridden by editor grid
58335     stopEditing : function(){},
58336
58337     /**
58338      * Returns the grid's SelectionModel.
58339      * @return {SelectionModel}
58340      */
58341     getSelectionModel : function(){
58342         if(!this.selModel){
58343             this.selModel = new Roo.grid.RowSelectionModel();
58344         }
58345         return this.selModel;
58346     },
58347
58348     /**
58349      * Returns the grid's DataSource.
58350      * @return {DataSource}
58351      */
58352     getDataSource : function(){
58353         return this.dataSource;
58354     },
58355
58356     /**
58357      * Returns the grid's ColumnModel.
58358      * @return {ColumnModel}
58359      */
58360     getColumnModel : function(){
58361         return this.colModel;
58362     },
58363
58364     /**
58365      * Returns the grid's GridView object.
58366      * @return {GridView}
58367      */
58368     getView : function(){
58369         if(!this.view){
58370             this.view = new Roo.grid.GridView(this.viewConfig);
58371             this.relayEvents(this.view, [
58372                 "beforerowremoved", "beforerowsinserted",
58373                 "beforerefresh", "rowremoved",
58374                 "rowsinserted", "rowupdated" ,"refresh"
58375             ]);
58376         }
58377         return this.view;
58378     },
58379     /**
58380      * Called to get grid's drag proxy text, by default returns this.ddText.
58381      * Override this to put something different in the dragged text.
58382      * @return {String}
58383      */
58384     getDragDropText : function(){
58385         var count = this.selModel.getCount();
58386         return String.format(this.ddText, count, count == 1 ? '' : 's');
58387     }
58388 });
58389 /*
58390  * Based on:
58391  * Ext JS Library 1.1.1
58392  * Copyright(c) 2006-2007, Ext JS, LLC.
58393  *
58394  * Originally Released Under LGPL - original licence link has changed is not relivant.
58395  *
58396  * Fork - LGPL
58397  * <script type="text/javascript">
58398  */
58399  /**
58400  * @class Roo.grid.AbstractGridView
58401  * @extends Roo.util.Observable
58402  * @abstract
58403  * Abstract base class for grid Views
58404  * @constructor
58405  */
58406 Roo.grid.AbstractGridView = function(){
58407         this.grid = null;
58408         
58409         this.events = {
58410             "beforerowremoved" : true,
58411             "beforerowsinserted" : true,
58412             "beforerefresh" : true,
58413             "rowremoved" : true,
58414             "rowsinserted" : true,
58415             "rowupdated" : true,
58416             "refresh" : true
58417         };
58418     Roo.grid.AbstractGridView.superclass.constructor.call(this);
58419 };
58420
58421 Roo.extend(Roo.grid.AbstractGridView, Roo.util.Observable, {
58422     rowClass : "x-grid-row",
58423     cellClass : "x-grid-cell",
58424     tdClass : "x-grid-td",
58425     hdClass : "x-grid-hd",
58426     splitClass : "x-grid-hd-split",
58427     
58428     init: function(grid){
58429         this.grid = grid;
58430                 var cid = this.grid.getGridEl().id;
58431         this.colSelector = "#" + cid + " ." + this.cellClass + "-";
58432         this.tdSelector = "#" + cid + " ." + this.tdClass + "-";
58433         this.hdSelector = "#" + cid + " ." + this.hdClass + "-";
58434         this.splitSelector = "#" + cid + " ." + this.splitClass + "-";
58435         },
58436         
58437     getColumnRenderers : function(){
58438         var renderers = [];
58439         var cm = this.grid.colModel;
58440         var colCount = cm.getColumnCount();
58441         for(var i = 0; i < colCount; i++){
58442             renderers[i] = cm.getRenderer(i);
58443         }
58444         return renderers;
58445     },
58446     
58447     getColumnIds : function(){
58448         var ids = [];
58449         var cm = this.grid.colModel;
58450         var colCount = cm.getColumnCount();
58451         for(var i = 0; i < colCount; i++){
58452             ids[i] = cm.getColumnId(i);
58453         }
58454         return ids;
58455     },
58456     
58457     getDataIndexes : function(){
58458         if(!this.indexMap){
58459             this.indexMap = this.buildIndexMap();
58460         }
58461         return this.indexMap.colToData;
58462     },
58463     
58464     getColumnIndexByDataIndex : function(dataIndex){
58465         if(!this.indexMap){
58466             this.indexMap = this.buildIndexMap();
58467         }
58468         return this.indexMap.dataToCol[dataIndex];
58469     },
58470     
58471     /**
58472      * Set a css style for a column dynamically. 
58473      * @param {Number} colIndex The index of the column
58474      * @param {String} name The css property name
58475      * @param {String} value The css value
58476      */
58477     setCSSStyle : function(colIndex, name, value){
58478         var selector = "#" + this.grid.id + " .x-grid-col-" + colIndex;
58479         Roo.util.CSS.updateRule(selector, name, value);
58480     },
58481     
58482     generateRules : function(cm){
58483         var ruleBuf = [], rulesId = this.grid.id + '-cssrules';
58484         Roo.util.CSS.removeStyleSheet(rulesId);
58485         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
58486             var cid = cm.getColumnId(i);
58487             ruleBuf.push(this.colSelector, cid, " {\n", cm.config[i].css, "}\n",
58488                          this.tdSelector, cid, " {\n}\n",
58489                          this.hdSelector, cid, " {\n}\n",
58490                          this.splitSelector, cid, " {\n}\n");
58491         }
58492         return Roo.util.CSS.createStyleSheet(ruleBuf.join(""), rulesId);
58493     }
58494 });/*
58495  * Based on:
58496  * Ext JS Library 1.1.1
58497  * Copyright(c) 2006-2007, Ext JS, LLC.
58498  *
58499  * Originally Released Under LGPL - original licence link has changed is not relivant.
58500  *
58501  * Fork - LGPL
58502  * <script type="text/javascript">
58503  */
58504
58505 // private
58506 // This is a support class used internally by the Grid components
58507 Roo.grid.HeaderDragZone = function(grid, hd, hd2){
58508     this.grid = grid;
58509     this.view = grid.getView();
58510     this.ddGroup = "gridHeader" + this.grid.getGridEl().id;
58511     Roo.grid.HeaderDragZone.superclass.constructor.call(this, hd);
58512     if(hd2){
58513         this.setHandleElId(Roo.id(hd));
58514         this.setOuterHandleElId(Roo.id(hd2));
58515     }
58516     this.scroll = false;
58517 };
58518 Roo.extend(Roo.grid.HeaderDragZone, Roo.dd.DragZone, {
58519     maxDragWidth: 120,
58520     getDragData : function(e){
58521         var t = Roo.lib.Event.getTarget(e);
58522         var h = this.view.findHeaderCell(t);
58523         if(h){
58524             return {ddel: h.firstChild, header:h};
58525         }
58526         return false;
58527     },
58528
58529     onInitDrag : function(e){
58530         this.view.headersDisabled = true;
58531         var clone = this.dragData.ddel.cloneNode(true);
58532         clone.id = Roo.id();
58533         clone.style.width = Math.min(this.dragData.header.offsetWidth,this.maxDragWidth) + "px";
58534         this.proxy.update(clone);
58535         return true;
58536     },
58537
58538     afterValidDrop : function(){
58539         var v = this.view;
58540         setTimeout(function(){
58541             v.headersDisabled = false;
58542         }, 50);
58543     },
58544
58545     afterInvalidDrop : function(){
58546         var v = this.view;
58547         setTimeout(function(){
58548             v.headersDisabled = false;
58549         }, 50);
58550     }
58551 });
58552 /*
58553  * Based on:
58554  * Ext JS Library 1.1.1
58555  * Copyright(c) 2006-2007, Ext JS, LLC.
58556  *
58557  * Originally Released Under LGPL - original licence link has changed is not relivant.
58558  *
58559  * Fork - LGPL
58560  * <script type="text/javascript">
58561  */
58562 // private
58563 // This is a support class used internally by the Grid components
58564 Roo.grid.HeaderDropZone = function(grid, hd, hd2){
58565     this.grid = grid;
58566     this.view = grid.getView();
58567     // split the proxies so they don't interfere with mouse events
58568     this.proxyTop = Roo.DomHelper.append(document.body, {
58569         cls:"col-move-top", html:"&#160;"
58570     }, true);
58571     this.proxyBottom = Roo.DomHelper.append(document.body, {
58572         cls:"col-move-bottom", html:"&#160;"
58573     }, true);
58574     this.proxyTop.hide = this.proxyBottom.hide = function(){
58575         this.setLeftTop(-100,-100);
58576         this.setStyle("visibility", "hidden");
58577     };
58578     this.ddGroup = "gridHeader" + this.grid.getGridEl().id;
58579     // temporarily disabled
58580     //Roo.dd.ScrollManager.register(this.view.scroller.dom);
58581     Roo.grid.HeaderDropZone.superclass.constructor.call(this, grid.getGridEl().dom);
58582 };
58583 Roo.extend(Roo.grid.HeaderDropZone, Roo.dd.DropZone, {
58584     proxyOffsets : [-4, -9],
58585     fly: Roo.Element.fly,
58586
58587     getTargetFromEvent : function(e){
58588         var t = Roo.lib.Event.getTarget(e);
58589         var cindex = this.view.findCellIndex(t);
58590         if(cindex !== false){
58591             return this.view.getHeaderCell(cindex);
58592         }
58593         return null;
58594     },
58595
58596     nextVisible : function(h){
58597         var v = this.view, cm = this.grid.colModel;
58598         h = h.nextSibling;
58599         while(h){
58600             if(!cm.isHidden(v.getCellIndex(h))){
58601                 return h;
58602             }
58603             h = h.nextSibling;
58604         }
58605         return null;
58606     },
58607
58608     prevVisible : function(h){
58609         var v = this.view, cm = this.grid.colModel;
58610         h = h.prevSibling;
58611         while(h){
58612             if(!cm.isHidden(v.getCellIndex(h))){
58613                 return h;
58614             }
58615             h = h.prevSibling;
58616         }
58617         return null;
58618     },
58619
58620     positionIndicator : function(h, n, e){
58621         var x = Roo.lib.Event.getPageX(e);
58622         var r = Roo.lib.Dom.getRegion(n.firstChild);
58623         var px, pt, py = r.top + this.proxyOffsets[1];
58624         if((r.right - x) <= (r.right-r.left)/2){
58625             px = r.right+this.view.borderWidth;
58626             pt = "after";
58627         }else{
58628             px = r.left;
58629             pt = "before";
58630         }
58631         var oldIndex = this.view.getCellIndex(h);
58632         var newIndex = this.view.getCellIndex(n);
58633
58634         if(this.grid.colModel.isFixed(newIndex)){
58635             return false;
58636         }
58637
58638         var locked = this.grid.colModel.isLocked(newIndex);
58639
58640         if(pt == "after"){
58641             newIndex++;
58642         }
58643         if(oldIndex < newIndex){
58644             newIndex--;
58645         }
58646         if(oldIndex == newIndex && (locked == this.grid.colModel.isLocked(oldIndex))){
58647             return false;
58648         }
58649         px +=  this.proxyOffsets[0];
58650         this.proxyTop.setLeftTop(px, py);
58651         this.proxyTop.show();
58652         if(!this.bottomOffset){
58653             this.bottomOffset = this.view.mainHd.getHeight();
58654         }
58655         this.proxyBottom.setLeftTop(px, py+this.proxyTop.dom.offsetHeight+this.bottomOffset);
58656         this.proxyBottom.show();
58657         return pt;
58658     },
58659
58660     onNodeEnter : function(n, dd, e, data){
58661         if(data.header != n){
58662             this.positionIndicator(data.header, n, e);
58663         }
58664     },
58665
58666     onNodeOver : function(n, dd, e, data){
58667         var result = false;
58668         if(data.header != n){
58669             result = this.positionIndicator(data.header, n, e);
58670         }
58671         if(!result){
58672             this.proxyTop.hide();
58673             this.proxyBottom.hide();
58674         }
58675         return result ? this.dropAllowed : this.dropNotAllowed;
58676     },
58677
58678     onNodeOut : function(n, dd, e, data){
58679         this.proxyTop.hide();
58680         this.proxyBottom.hide();
58681     },
58682
58683     onNodeDrop : function(n, dd, e, data){
58684         var h = data.header;
58685         if(h != n){
58686             var cm = this.grid.colModel;
58687             var x = Roo.lib.Event.getPageX(e);
58688             var r = Roo.lib.Dom.getRegion(n.firstChild);
58689             var pt = (r.right - x) <= ((r.right-r.left)/2) ? "after" : "before";
58690             var oldIndex = this.view.getCellIndex(h);
58691             var newIndex = this.view.getCellIndex(n);
58692             var locked = cm.isLocked(newIndex);
58693             if(pt == "after"){
58694                 newIndex++;
58695             }
58696             if(oldIndex < newIndex){
58697                 newIndex--;
58698             }
58699             if(oldIndex == newIndex && (locked == cm.isLocked(oldIndex))){
58700                 return false;
58701             }
58702             cm.setLocked(oldIndex, locked, true);
58703             cm.moveColumn(oldIndex, newIndex);
58704             this.grid.fireEvent("columnmove", oldIndex, newIndex);
58705             return true;
58706         }
58707         return false;
58708     }
58709 });
58710 /*
58711  * Based on:
58712  * Ext JS Library 1.1.1
58713  * Copyright(c) 2006-2007, Ext JS, LLC.
58714  *
58715  * Originally Released Under LGPL - original licence link has changed is not relivant.
58716  *
58717  * Fork - LGPL
58718  * <script type="text/javascript">
58719  */
58720   
58721 /**
58722  * @class Roo.grid.GridView
58723  * @extends Roo.util.Observable
58724  *
58725  * @constructor
58726  * @param {Object} config
58727  */
58728 Roo.grid.GridView = function(config){
58729     Roo.grid.GridView.superclass.constructor.call(this);
58730     this.el = null;
58731
58732     Roo.apply(this, config);
58733 };
58734
58735 Roo.extend(Roo.grid.GridView, Roo.grid.AbstractGridView, {
58736
58737     unselectable :  'unselectable="on"',
58738     unselectableCls :  'x-unselectable',
58739     
58740     
58741     rowClass : "x-grid-row",
58742
58743     cellClass : "x-grid-col",
58744
58745     tdClass : "x-grid-td",
58746
58747     hdClass : "x-grid-hd",
58748
58749     splitClass : "x-grid-split",
58750
58751     sortClasses : ["sort-asc", "sort-desc"],
58752
58753     enableMoveAnim : false,
58754
58755     hlColor: "C3DAF9",
58756
58757     dh : Roo.DomHelper,
58758
58759     fly : Roo.Element.fly,
58760
58761     css : Roo.util.CSS,
58762
58763     borderWidth: 1,
58764
58765     splitOffset: 3,
58766
58767     scrollIncrement : 22,
58768
58769     cellRE: /(?:.*?)x-grid-(?:hd|cell|csplit)-(?:[\d]+)-([\d]+)(?:.*?)/,
58770
58771     findRE: /\s?(?:x-grid-hd|x-grid-col|x-grid-csplit)\s/,
58772
58773     bind : function(ds, cm){
58774         if(this.ds){
58775             this.ds.un("load", this.onLoad, this);
58776             this.ds.un("datachanged", this.onDataChange, this);
58777             this.ds.un("add", this.onAdd, this);
58778             this.ds.un("remove", this.onRemove, this);
58779             this.ds.un("update", this.onUpdate, this);
58780             this.ds.un("clear", this.onClear, this);
58781         }
58782         if(ds){
58783             ds.on("load", this.onLoad, this);
58784             ds.on("datachanged", this.onDataChange, this);
58785             ds.on("add", this.onAdd, this);
58786             ds.on("remove", this.onRemove, this);
58787             ds.on("update", this.onUpdate, this);
58788             ds.on("clear", this.onClear, this);
58789         }
58790         this.ds = ds;
58791
58792         if(this.cm){
58793             this.cm.un("widthchange", this.onColWidthChange, this);
58794             this.cm.un("headerchange", this.onHeaderChange, this);
58795             this.cm.un("hiddenchange", this.onHiddenChange, this);
58796             this.cm.un("columnmoved", this.onColumnMove, this);
58797             this.cm.un("columnlockchange", this.onColumnLock, this);
58798         }
58799         if(cm){
58800             this.generateRules(cm);
58801             cm.on("widthchange", this.onColWidthChange, this);
58802             cm.on("headerchange", this.onHeaderChange, this);
58803             cm.on("hiddenchange", this.onHiddenChange, this);
58804             cm.on("columnmoved", this.onColumnMove, this);
58805             cm.on("columnlockchange", this.onColumnLock, this);
58806         }
58807         this.cm = cm;
58808     },
58809
58810     init: function(grid){
58811         Roo.grid.GridView.superclass.init.call(this, grid);
58812
58813         this.bind(grid.dataSource, grid.colModel);
58814
58815         grid.on("headerclick", this.handleHeaderClick, this);
58816
58817         if(grid.trackMouseOver){
58818             grid.on("mouseover", this.onRowOver, this);
58819             grid.on("mouseout", this.onRowOut, this);
58820         }
58821         grid.cancelTextSelection = function(){};
58822         this.gridId = grid.id;
58823
58824         var tpls = this.templates || {};
58825
58826         if(!tpls.master){
58827             tpls.master = new Roo.Template(
58828                '<div class="x-grid" hidefocus="true">',
58829                 '<a href="#" class="x-grid-focus" tabIndex="-1"></a>',
58830                   '<div class="x-grid-topbar"></div>',
58831                   '<div class="x-grid-scroller"><div></div></div>',
58832                   '<div class="x-grid-locked">',
58833                       '<div class="x-grid-header">{lockedHeader}</div>',
58834                       '<div class="x-grid-body">{lockedBody}</div>',
58835                   "</div>",
58836                   '<div class="x-grid-viewport">',
58837                       '<div class="x-grid-header">{header}</div>',
58838                       '<div class="x-grid-body">{body}</div>',
58839                   "</div>",
58840                   '<div class="x-grid-bottombar"></div>',
58841                  
58842                   '<div class="x-grid-resize-proxy">&#160;</div>',
58843                "</div>"
58844             );
58845             tpls.master.disableformats = true;
58846         }
58847
58848         if(!tpls.header){
58849             tpls.header = new Roo.Template(
58850                '<table border="0" cellspacing="0" cellpadding="0">',
58851                '<tbody><tr class="x-grid-hd-row">{cells}</tr></tbody>',
58852                "</table>{splits}"
58853             );
58854             tpls.header.disableformats = true;
58855         }
58856         tpls.header.compile();
58857
58858         if(!tpls.hcell){
58859             tpls.hcell = new Roo.Template(
58860                 '<td class="x-grid-hd x-grid-td-{id} {cellId}"><div title="{title}" class="x-grid-hd-inner x-grid-hd-{id}">',
58861                 '<div class="x-grid-hd-text ' + this.unselectableCls +  '" ' + this.unselectable +'>{value}<img class="x-grid-sort-icon" src="', Roo.BLANK_IMAGE_URL, '" /></div>',
58862                 "</div></td>"
58863              );
58864              tpls.hcell.disableFormats = true;
58865         }
58866         tpls.hcell.compile();
58867
58868         if(!tpls.hsplit){
58869             tpls.hsplit = new Roo.Template('<div class="x-grid-split {splitId} x-grid-split-{id}" style="{style} ' +
58870                                             this.unselectableCls +  '" ' + this.unselectable +'>&#160;</div>');
58871             tpls.hsplit.disableFormats = true;
58872         }
58873         tpls.hsplit.compile();
58874
58875         if(!tpls.body){
58876             tpls.body = new Roo.Template(
58877                '<table border="0" cellspacing="0" cellpadding="0">',
58878                "<tbody>{rows}</tbody>",
58879                "</table>"
58880             );
58881             tpls.body.disableFormats = true;
58882         }
58883         tpls.body.compile();
58884
58885         if(!tpls.row){
58886             tpls.row = new Roo.Template('<tr class="x-grid-row {alt}">{cells}</tr>');
58887             tpls.row.disableFormats = true;
58888         }
58889         tpls.row.compile();
58890
58891         if(!tpls.cell){
58892             tpls.cell = new Roo.Template(
58893                 '<td class="x-grid-col x-grid-td-{id} {cellId} {css}" tabIndex="0">',
58894                 '<div class="x-grid-col-{id} x-grid-cell-inner"><div class="x-grid-cell-text ' +
58895                     this.unselectableCls +  '" ' + this.unselectable +'" {attr}>{value}</div></div>',
58896                 "</td>"
58897             );
58898             tpls.cell.disableFormats = true;
58899         }
58900         tpls.cell.compile();
58901
58902         this.templates = tpls;
58903     },
58904
58905     // remap these for backwards compat
58906     onColWidthChange : function(){
58907         this.updateColumns.apply(this, arguments);
58908     },
58909     onHeaderChange : function(){
58910         this.updateHeaders.apply(this, arguments);
58911     }, 
58912     onHiddenChange : function(){
58913         this.handleHiddenChange.apply(this, arguments);
58914     },
58915     onColumnMove : function(){
58916         this.handleColumnMove.apply(this, arguments);
58917     },
58918     onColumnLock : function(){
58919         this.handleLockChange.apply(this, arguments);
58920     },
58921
58922     onDataChange : function(){
58923         this.refresh();
58924         this.updateHeaderSortState();
58925     },
58926
58927     onClear : function(){
58928         this.refresh();
58929     },
58930
58931     onUpdate : function(ds, record){
58932         this.refreshRow(record);
58933     },
58934
58935     refreshRow : function(record){
58936         var ds = this.ds, index;
58937         if(typeof record == 'number'){
58938             index = record;
58939             record = ds.getAt(index);
58940         }else{
58941             index = ds.indexOf(record);
58942         }
58943         this.insertRows(ds, index, index, true);
58944         this.onRemove(ds, record, index+1, true);
58945         this.syncRowHeights(index, index);
58946         this.layout();
58947         this.fireEvent("rowupdated", this, index, record);
58948     },
58949
58950     onAdd : function(ds, records, index){
58951         this.insertRows(ds, index, index + (records.length-1));
58952     },
58953
58954     onRemove : function(ds, record, index, isUpdate){
58955         if(isUpdate !== true){
58956             this.fireEvent("beforerowremoved", this, index, record);
58957         }
58958         var bt = this.getBodyTable(), lt = this.getLockedTable();
58959         if(bt.rows[index]){
58960             bt.firstChild.removeChild(bt.rows[index]);
58961         }
58962         if(lt.rows[index]){
58963             lt.firstChild.removeChild(lt.rows[index]);
58964         }
58965         if(isUpdate !== true){
58966             this.stripeRows(index);
58967             this.syncRowHeights(index, index);
58968             this.layout();
58969             this.fireEvent("rowremoved", this, index, record);
58970         }
58971     },
58972
58973     onLoad : function(){
58974         this.scrollToTop();
58975     },
58976
58977     /**
58978      * Scrolls the grid to the top
58979      */
58980     scrollToTop : function(){
58981         if(this.scroller){
58982             this.scroller.dom.scrollTop = 0;
58983             this.syncScroll();
58984         }
58985     },
58986
58987     /**
58988      * Gets a panel in the header of the grid that can be used for toolbars etc.
58989      * After modifying the contents of this panel a call to grid.autoSize() may be
58990      * required to register any changes in size.
58991      * @param {Boolean} doShow By default the header is hidden. Pass true to show the panel
58992      * @return Roo.Element
58993      */
58994     getHeaderPanel : function(doShow){
58995         if(doShow){
58996             this.headerPanel.show();
58997         }
58998         return this.headerPanel;
58999     },
59000
59001     /**
59002      * Gets a panel in the footer of the grid that can be used for toolbars etc.
59003      * After modifying the contents of this panel a call to grid.autoSize() may be
59004      * required to register any changes in size.
59005      * @param {Boolean} doShow By default the footer is hidden. Pass true to show the panel
59006      * @return Roo.Element
59007      */
59008     getFooterPanel : function(doShow){
59009         if(doShow){
59010             this.footerPanel.show();
59011         }
59012         return this.footerPanel;
59013     },
59014
59015     initElements : function(){
59016         var E = Roo.Element;
59017         var el = this.grid.getGridEl().dom.firstChild;
59018         var cs = el.childNodes;
59019
59020         this.el = new E(el);
59021         
59022          this.focusEl = new E(el.firstChild);
59023         this.focusEl.swallowEvent("click", true);
59024         
59025         this.headerPanel = new E(cs[1]);
59026         this.headerPanel.enableDisplayMode("block");
59027
59028         this.scroller = new E(cs[2]);
59029         this.scrollSizer = new E(this.scroller.dom.firstChild);
59030
59031         this.lockedWrap = new E(cs[3]);
59032         this.lockedHd = new E(this.lockedWrap.dom.firstChild);
59033         this.lockedBody = new E(this.lockedWrap.dom.childNodes[1]);
59034
59035         this.mainWrap = new E(cs[4]);
59036         this.mainHd = new E(this.mainWrap.dom.firstChild);
59037         this.mainBody = new E(this.mainWrap.dom.childNodes[1]);
59038
59039         this.footerPanel = new E(cs[5]);
59040         this.footerPanel.enableDisplayMode("block");
59041
59042         this.resizeProxy = new E(cs[6]);
59043
59044         this.headerSelector = String.format(
59045            '#{0} td.x-grid-hd, #{1} td.x-grid-hd',
59046            this.lockedHd.id, this.mainHd.id
59047         );
59048
59049         this.splitterSelector = String.format(
59050            '#{0} div.x-grid-split, #{1} div.x-grid-split',
59051            this.idToCssName(this.lockedHd.id), this.idToCssName(this.mainHd.id)
59052         );
59053     },
59054     idToCssName : function(s)
59055     {
59056         return s.replace(/[^a-z0-9]+/ig, '-');
59057     },
59058
59059     getHeaderCell : function(index){
59060         return Roo.DomQuery.select(this.headerSelector)[index];
59061     },
59062
59063     getHeaderCellMeasure : function(index){
59064         return this.getHeaderCell(index).firstChild;
59065     },
59066
59067     getHeaderCellText : function(index){
59068         return this.getHeaderCell(index).firstChild.firstChild;
59069     },
59070
59071     getLockedTable : function(){
59072         return this.lockedBody.dom.firstChild;
59073     },
59074
59075     getBodyTable : function(){
59076         return this.mainBody.dom.firstChild;
59077     },
59078
59079     getLockedRow : function(index){
59080         return this.getLockedTable().rows[index];
59081     },
59082
59083     getRow : function(index){
59084         return this.getBodyTable().rows[index];
59085     },
59086
59087     getRowComposite : function(index){
59088         if(!this.rowEl){
59089             this.rowEl = new Roo.CompositeElementLite();
59090         }
59091         var els = [], lrow, mrow;
59092         if(lrow = this.getLockedRow(index)){
59093             els.push(lrow);
59094         }
59095         if(mrow = this.getRow(index)){
59096             els.push(mrow);
59097         }
59098         this.rowEl.elements = els;
59099         return this.rowEl;
59100     },
59101     /**
59102      * Gets the 'td' of the cell
59103      * 
59104      * @param {Integer} rowIndex row to select
59105      * @param {Integer} colIndex column to select
59106      * 
59107      * @return {Object} 
59108      */
59109     getCell : function(rowIndex, colIndex){
59110         var locked = this.cm.getLockedCount();
59111         var source;
59112         if(colIndex < locked){
59113             source = this.lockedBody.dom.firstChild;
59114         }else{
59115             source = this.mainBody.dom.firstChild;
59116             colIndex -= locked;
59117         }
59118         return source.rows[rowIndex].childNodes[colIndex];
59119     },
59120
59121     getCellText : function(rowIndex, colIndex){
59122         return this.getCell(rowIndex, colIndex).firstChild.firstChild;
59123     },
59124
59125     getCellBox : function(cell){
59126         var b = this.fly(cell).getBox();
59127         if(Roo.isOpera){ // opera fails to report the Y
59128             b.y = cell.offsetTop + this.mainBody.getY();
59129         }
59130         return b;
59131     },
59132
59133     getCellIndex : function(cell){
59134         var id = String(cell.className).match(this.cellRE);
59135         if(id){
59136             return parseInt(id[1], 10);
59137         }
59138         return 0;
59139     },
59140
59141     findHeaderIndex : function(n){
59142         var r = Roo.fly(n).findParent("td." + this.hdClass, 6);
59143         return r ? this.getCellIndex(r) : false;
59144     },
59145
59146     findHeaderCell : function(n){
59147         var r = Roo.fly(n).findParent("td." + this.hdClass, 6);
59148         return r ? r : false;
59149     },
59150
59151     findRowIndex : function(n){
59152         if(!n){
59153             return false;
59154         }
59155         var r = Roo.fly(n).findParent("tr." + this.rowClass, 6);
59156         return r ? r.rowIndex : false;
59157     },
59158
59159     findCellIndex : function(node){
59160         var stop = this.el.dom;
59161         while(node && node != stop){
59162             if(this.findRE.test(node.className)){
59163                 return this.getCellIndex(node);
59164             }
59165             node = node.parentNode;
59166         }
59167         return false;
59168     },
59169
59170     getColumnId : function(index){
59171         return this.cm.getColumnId(index);
59172     },
59173
59174     getSplitters : function()
59175     {
59176         if(this.splitterSelector){
59177            return Roo.DomQuery.select(this.splitterSelector);
59178         }else{
59179             return null;
59180       }
59181     },
59182
59183     getSplitter : function(index){
59184         return this.getSplitters()[index];
59185     },
59186
59187     onRowOver : function(e, t){
59188         var row;
59189         if((row = this.findRowIndex(t)) !== false){
59190             this.getRowComposite(row).addClass("x-grid-row-over");
59191         }
59192     },
59193
59194     onRowOut : function(e, t){
59195         var row;
59196         if((row = this.findRowIndex(t)) !== false && row !== this.findRowIndex(e.getRelatedTarget())){
59197             this.getRowComposite(row).removeClass("x-grid-row-over");
59198         }
59199     },
59200
59201     renderHeaders : function(){
59202         var cm = this.cm;
59203         var ct = this.templates.hcell, ht = this.templates.header, st = this.templates.hsplit;
59204         var cb = [], lb = [], sb = [], lsb = [], p = {};
59205         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
59206             p.cellId = "x-grid-hd-0-" + i;
59207             p.splitId = "x-grid-csplit-0-" + i;
59208             p.id = cm.getColumnId(i);
59209             p.value = cm.getColumnHeader(i) || "";
59210             p.title = cm.getColumnTooltip(i) || (''+p.value).match(/\</)  ? '' :  p.value  || "";
59211             p.style = (this.grid.enableColumnResize === false || !cm.isResizable(i) || cm.isFixed(i)) ? 'cursor:default' : '';
59212             if(!cm.isLocked(i)){
59213                 cb[cb.length] = ct.apply(p);
59214                 sb[sb.length] = st.apply(p);
59215             }else{
59216                 lb[lb.length] = ct.apply(p);
59217                 lsb[lsb.length] = st.apply(p);
59218             }
59219         }
59220         return [ht.apply({cells: lb.join(""), splits:lsb.join("")}),
59221                 ht.apply({cells: cb.join(""), splits:sb.join("")})];
59222     },
59223
59224     updateHeaders : function(){
59225         var html = this.renderHeaders();
59226         this.lockedHd.update(html[0]);
59227         this.mainHd.update(html[1]);
59228     },
59229
59230     /**
59231      * Focuses the specified row.
59232      * @param {Number} row The row index
59233      */
59234     focusRow : function(row)
59235     {
59236         //Roo.log('GridView.focusRow');
59237         var x = this.scroller.dom.scrollLeft;
59238         this.focusCell(row, 0, false);
59239         this.scroller.dom.scrollLeft = x;
59240     },
59241
59242     /**
59243      * Focuses the specified cell.
59244      * @param {Number} row The row index
59245      * @param {Number} col The column index
59246      * @param {Boolean} hscroll false to disable horizontal scrolling
59247      */
59248     focusCell : function(row, col, hscroll)
59249     {
59250         //Roo.log('GridView.focusCell');
59251         var el = this.ensureVisible(row, col, hscroll);
59252         this.focusEl.alignTo(el, "tl-tl");
59253         if(Roo.isGecko){
59254             this.focusEl.focus();
59255         }else{
59256             this.focusEl.focus.defer(1, this.focusEl);
59257         }
59258     },
59259
59260     /**
59261      * Scrolls the specified cell into view
59262      * @param {Number} row The row index
59263      * @param {Number} col The column index
59264      * @param {Boolean} hscroll false to disable horizontal scrolling
59265      */
59266     ensureVisible : function(row, col, hscroll)
59267     {
59268         //Roo.log('GridView.ensureVisible,' + row + ',' + col);
59269         //return null; //disable for testing.
59270         if(typeof row != "number"){
59271             row = row.rowIndex;
59272         }
59273         if(row < 0 && row >= this.ds.getCount()){
59274             return  null;
59275         }
59276         col = (col !== undefined ? col : 0);
59277         var cm = this.grid.colModel;
59278         while(cm.isHidden(col)){
59279             col++;
59280         }
59281
59282         var el = this.getCell(row, col);
59283         if(!el){
59284             return null;
59285         }
59286         var c = this.scroller.dom;
59287
59288         var ctop = parseInt(el.offsetTop, 10);
59289         var cleft = parseInt(el.offsetLeft, 10);
59290         var cbot = ctop + el.offsetHeight;
59291         var cright = cleft + el.offsetWidth;
59292         
59293         var ch = c.clientHeight - this.mainHd.dom.offsetHeight;
59294         var stop = parseInt(c.scrollTop, 10);
59295         var sleft = parseInt(c.scrollLeft, 10);
59296         var sbot = stop + ch;
59297         var sright = sleft + c.clientWidth;
59298         /*
59299         Roo.log('GridView.ensureVisible:' +
59300                 ' ctop:' + ctop +
59301                 ' c.clientHeight:' + c.clientHeight +
59302                 ' this.mainHd.dom.offsetHeight:' + this.mainHd.dom.offsetHeight +
59303                 ' stop:' + stop +
59304                 ' cbot:' + cbot +
59305                 ' sbot:' + sbot +
59306                 ' ch:' + ch  
59307                 );
59308         */
59309         if(ctop < stop){
59310             c.scrollTop = ctop;
59311             //Roo.log("set scrolltop to ctop DISABLE?");
59312         }else if(cbot > sbot){
59313             //Roo.log("set scrolltop to cbot-ch");
59314             c.scrollTop = cbot-ch;
59315         }
59316         
59317         if(hscroll !== false){
59318             if(cleft < sleft){
59319                 c.scrollLeft = cleft;
59320             }else if(cright > sright){
59321                 c.scrollLeft = cright-c.clientWidth;
59322             }
59323         }
59324          
59325         return el;
59326     },
59327
59328     updateColumns : function(){
59329         this.grid.stopEditing();
59330         var cm = this.grid.colModel, colIds = this.getColumnIds();
59331         //var totalWidth = cm.getTotalWidth();
59332         var pos = 0;
59333         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
59334             //if(cm.isHidden(i)) continue;
59335             var w = cm.getColumnWidth(i);
59336             this.css.updateRule(this.colSelector+this.idToCssName(colIds[i]), "width", (w - this.borderWidth) + "px");
59337             this.css.updateRule(this.hdSelector+this.idToCssName(colIds[i]), "width", (w - this.borderWidth) + "px");
59338         }
59339         this.updateSplitters();
59340     },
59341
59342     generateRules : function(cm){
59343         var ruleBuf = [], rulesId = this.idToCssName(this.grid.id)+ '-cssrules';
59344         Roo.util.CSS.removeStyleSheet(rulesId);
59345         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
59346             var cid = cm.getColumnId(i);
59347             var align = '';
59348             if(cm.config[i].align){
59349                 align = 'text-align:'+cm.config[i].align+';';
59350             }
59351             var hidden = '';
59352             if(cm.isHidden(i)){
59353                 hidden = 'display:none;';
59354             }
59355             var width = "width:" + (cm.getColumnWidth(i) - this.borderWidth) + "px;";
59356             ruleBuf.push(
59357                     this.colSelector, cid, " {\n", cm.config[i].css, align, width, "\n}\n",
59358                     this.hdSelector, cid, " {\n", align, width, "}\n",
59359                     this.tdSelector, cid, " {\n",hidden,"\n}\n",
59360                     this.splitSelector, cid, " {\n", hidden , "\n}\n");
59361         }
59362         return Roo.util.CSS.createStyleSheet(ruleBuf.join(""), rulesId);
59363     },
59364
59365     updateSplitters : function(){
59366         var cm = this.cm, s = this.getSplitters();
59367         if(s){ // splitters not created yet
59368             var pos = 0, locked = true;
59369             for(var i = 0, len = cm.getColumnCount(); i < len; i++){
59370                 if(cm.isHidden(i)) {
59371                     continue;
59372                 }
59373                 var w = cm.getColumnWidth(i); // make sure it's a number
59374                 if(!cm.isLocked(i) && locked){
59375                     pos = 0;
59376                     locked = false;
59377                 }
59378                 pos += w;
59379                 s[i].style.left = (pos-this.splitOffset) + "px";
59380             }
59381         }
59382     },
59383
59384     handleHiddenChange : function(colModel, colIndex, hidden){
59385         if(hidden){
59386             this.hideColumn(colIndex);
59387         }else{
59388             this.unhideColumn(colIndex);
59389         }
59390     },
59391
59392     hideColumn : function(colIndex){
59393         var cid = this.getColumnId(colIndex);
59394         this.css.updateRule(this.tdSelector+this.idToCssName(cid), "display", "none");
59395         this.css.updateRule(this.splitSelector+this.idToCssName(cid), "display", "none");
59396         if(Roo.isSafari){
59397             this.updateHeaders();
59398         }
59399         this.updateSplitters();
59400         this.layout();
59401     },
59402
59403     unhideColumn : function(colIndex){
59404         var cid = this.getColumnId(colIndex);
59405         this.css.updateRule(this.tdSelector+this.idToCssName(cid), "display", "");
59406         this.css.updateRule(this.splitSelector+this.idToCssName(cid), "display", "");
59407
59408         if(Roo.isSafari){
59409             this.updateHeaders();
59410         }
59411         this.updateSplitters();
59412         this.layout();
59413     },
59414
59415     insertRows : function(dm, firstRow, lastRow, isUpdate){
59416         if(firstRow == 0 && lastRow == dm.getCount()-1){
59417             this.refresh();
59418         }else{
59419             if(!isUpdate){
59420                 this.fireEvent("beforerowsinserted", this, firstRow, lastRow);
59421             }
59422             var s = this.getScrollState();
59423             var markup = this.renderRows(firstRow, lastRow);
59424             this.bufferRows(markup[0], this.getLockedTable(), firstRow);
59425             this.bufferRows(markup[1], this.getBodyTable(), firstRow);
59426             this.restoreScroll(s);
59427             if(!isUpdate){
59428                 this.fireEvent("rowsinserted", this, firstRow, lastRow);
59429                 this.syncRowHeights(firstRow, lastRow);
59430                 this.stripeRows(firstRow);
59431                 this.layout();
59432             }
59433         }
59434     },
59435
59436     bufferRows : function(markup, target, index){
59437         var before = null, trows = target.rows, tbody = target.tBodies[0];
59438         if(index < trows.length){
59439             before = trows[index];
59440         }
59441         var b = document.createElement("div");
59442         b.innerHTML = "<table><tbody>"+markup+"</tbody></table>";
59443         var rows = b.firstChild.rows;
59444         for(var i = 0, len = rows.length; i < len; i++){
59445             if(before){
59446                 tbody.insertBefore(rows[0], before);
59447             }else{
59448                 tbody.appendChild(rows[0]);
59449             }
59450         }
59451         b.innerHTML = "";
59452         b = null;
59453     },
59454
59455     deleteRows : function(dm, firstRow, lastRow){
59456         if(dm.getRowCount()<1){
59457             this.fireEvent("beforerefresh", this);
59458             this.mainBody.update("");
59459             this.lockedBody.update("");
59460             this.fireEvent("refresh", this);
59461         }else{
59462             this.fireEvent("beforerowsdeleted", this, firstRow, lastRow);
59463             var bt = this.getBodyTable();
59464             var tbody = bt.firstChild;
59465             var rows = bt.rows;
59466             for(var rowIndex = firstRow; rowIndex <= lastRow; rowIndex++){
59467                 tbody.removeChild(rows[firstRow]);
59468             }
59469             this.stripeRows(firstRow);
59470             this.fireEvent("rowsdeleted", this, firstRow, lastRow);
59471         }
59472     },
59473
59474     updateRows : function(dataSource, firstRow, lastRow){
59475         var s = this.getScrollState();
59476         this.refresh();
59477         this.restoreScroll(s);
59478     },
59479
59480     handleSort : function(dataSource, sortColumnIndex, sortDir, noRefresh){
59481         if(!noRefresh){
59482            this.refresh();
59483         }
59484         this.updateHeaderSortState();
59485     },
59486
59487     getScrollState : function(){
59488         
59489         var sb = this.scroller.dom;
59490         return {left: sb.scrollLeft, top: sb.scrollTop};
59491     },
59492
59493     stripeRows : function(startRow){
59494         if(!this.grid.stripeRows || this.ds.getCount() < 1){
59495             return;
59496         }
59497         startRow = startRow || 0;
59498         var rows = this.getBodyTable().rows;
59499         var lrows = this.getLockedTable().rows;
59500         var cls = ' x-grid-row-alt ';
59501         for(var i = startRow, len = rows.length; i < len; i++){
59502             var row = rows[i], lrow = lrows[i];
59503             var isAlt = ((i+1) % 2 == 0);
59504             var hasAlt = (' '+row.className + ' ').indexOf(cls) != -1;
59505             if(isAlt == hasAlt){
59506                 continue;
59507             }
59508             if(isAlt){
59509                 row.className += " x-grid-row-alt";
59510             }else{
59511                 row.className = row.className.replace("x-grid-row-alt", "");
59512             }
59513             if(lrow){
59514                 lrow.className = row.className;
59515             }
59516         }
59517     },
59518
59519     restoreScroll : function(state){
59520         //Roo.log('GridView.restoreScroll');
59521         var sb = this.scroller.dom;
59522         sb.scrollLeft = state.left;
59523         sb.scrollTop = state.top;
59524         this.syncScroll();
59525     },
59526
59527     syncScroll : function(){
59528         //Roo.log('GridView.syncScroll');
59529         var sb = this.scroller.dom;
59530         var sh = this.mainHd.dom;
59531         var bs = this.mainBody.dom;
59532         var lv = this.lockedBody.dom;
59533         sh.scrollLeft = bs.scrollLeft = sb.scrollLeft;
59534         lv.scrollTop = bs.scrollTop = sb.scrollTop;
59535     },
59536
59537     handleScroll : function(e){
59538         this.syncScroll();
59539         var sb = this.scroller.dom;
59540         this.grid.fireEvent("bodyscroll", sb.scrollLeft, sb.scrollTop);
59541         e.stopEvent();
59542     },
59543
59544     handleWheel : function(e){
59545         var d = e.getWheelDelta();
59546         this.scroller.dom.scrollTop -= d*22;
59547         // set this here to prevent jumpy scrolling on large tables
59548         this.lockedBody.dom.scrollTop = this.mainBody.dom.scrollTop = this.scroller.dom.scrollTop;
59549         e.stopEvent();
59550     },
59551
59552     renderRows : function(startRow, endRow){
59553         // pull in all the crap needed to render rows
59554         var g = this.grid, cm = g.colModel, ds = g.dataSource, stripe = g.stripeRows;
59555         var colCount = cm.getColumnCount();
59556
59557         if(ds.getCount() < 1){
59558             return ["", ""];
59559         }
59560
59561         // build a map for all the columns
59562         var cs = [];
59563         for(var i = 0; i < colCount; i++){
59564             var name = cm.getDataIndex(i);
59565             cs[i] = {
59566                 name : typeof name == 'undefined' ? ds.fields.get(i).name : name,
59567                 renderer : cm.getRenderer(i),
59568                 id : cm.getColumnId(i),
59569                 locked : cm.isLocked(i),
59570                 has_editor : cm.isCellEditable(i)
59571             };
59572         }
59573
59574         startRow = startRow || 0;
59575         endRow = typeof endRow == "undefined"? ds.getCount()-1 : endRow;
59576
59577         // records to render
59578         var rs = ds.getRange(startRow, endRow);
59579
59580         return this.doRender(cs, rs, ds, startRow, colCount, stripe);
59581     },
59582
59583     // As much as I hate to duplicate code, this was branched because FireFox really hates
59584     // [].join("") on strings. The performance difference was substantial enough to
59585     // branch this function
59586     doRender : Roo.isGecko ?
59587             function(cs, rs, ds, startRow, colCount, stripe){
59588                 var ts = this.templates, ct = ts.cell, rt = ts.row;
59589                 // buffers
59590                 var buf = "", lbuf = "", cb, lcb, c, p = {}, rp = {}, r, rowIndex;
59591                 
59592                 var hasListener = this.grid.hasListener('rowclass');
59593                 var rowcfg = {};
59594                 for(var j = 0, len = rs.length; j < len; j++){
59595                     r = rs[j]; cb = ""; lcb = ""; rowIndex = (j+startRow);
59596                     for(var i = 0; i < colCount; i++){
59597                         c = cs[i];
59598                         p.cellId = "x-grid-cell-" + rowIndex + "-" + i;
59599                         p.id = c.id;
59600                         p.css = p.attr = "";
59601                         p.value = c.renderer(r.data[c.name], p, r, rowIndex, i, ds);
59602                         if(p.value == undefined || p.value === "") {
59603                             p.value = "&#160;";
59604                         }
59605                         if(c.has_editor){
59606                             p.css += ' x-grid-editable-cell';
59607                         }
59608                         if(c.dirty && typeof r.modified[c.name] !== 'undefined'){
59609                             p.css +=  ' x-grid-dirty-cell';
59610                         }
59611                         var markup = ct.apply(p);
59612                         if(!c.locked){
59613                             cb+= markup;
59614                         }else{
59615                             lcb+= markup;
59616                         }
59617                     }
59618                     var alt = [];
59619                     if(stripe && ((rowIndex+1) % 2 == 0)){
59620                         alt.push("x-grid-row-alt")
59621                     }
59622                     if(r.dirty){
59623                         alt.push(  " x-grid-dirty-row");
59624                     }
59625                     rp.cells = lcb;
59626                     if(this.getRowClass){
59627                         alt.push(this.getRowClass(r, rowIndex));
59628                     }
59629                     if (hasListener) {
59630                         rowcfg = {
59631                              
59632                             record: r,
59633                             rowIndex : rowIndex,
59634                             rowClass : ''
59635                         };
59636                         this.grid.fireEvent('rowclass', this, rowcfg);
59637                         alt.push(rowcfg.rowClass);
59638                     }
59639                     rp.alt = alt.join(" ");
59640                     lbuf+= rt.apply(rp);
59641                     rp.cells = cb;
59642                     buf+=  rt.apply(rp);
59643                 }
59644                 return [lbuf, buf];
59645             } :
59646             function(cs, rs, ds, startRow, colCount, stripe){
59647                 var ts = this.templates, ct = ts.cell, rt = ts.row;
59648                 // buffers
59649                 var buf = [], lbuf = [], cb, lcb, c, p = {}, rp = {}, r, rowIndex;
59650                 var hasListener = this.grid.hasListener('rowclass');
59651  
59652                 var rowcfg = {};
59653                 for(var j = 0, len = rs.length; j < len; j++){
59654                     r = rs[j]; cb = []; lcb = []; rowIndex = (j+startRow);
59655                     for(var i = 0; i < colCount; i++){
59656                         c = cs[i];
59657                         p.cellId = "x-grid-cell-" + rowIndex + "-" + i;
59658                         p.id = c.id;
59659                         p.css = p.attr = "";
59660                         p.value = c.renderer(r.data[c.name], p, r, rowIndex, i, ds);
59661                         if(p.value == undefined || p.value === "") {
59662                             p.value = "&#160;";
59663                         }
59664                         //Roo.log(c);
59665                          if(c.has_editor){
59666                             p.css += ' x-grid-editable-cell';
59667                         }
59668                         if(r.dirty && typeof r.modified[c.name] !== 'undefined'){
59669                             p.css += ' x-grid-dirty-cell' 
59670                         }
59671                         
59672                         var markup = ct.apply(p);
59673                         if(!c.locked){
59674                             cb[cb.length] = markup;
59675                         }else{
59676                             lcb[lcb.length] = markup;
59677                         }
59678                     }
59679                     var alt = [];
59680                     if(stripe && ((rowIndex+1) % 2 == 0)){
59681                         alt.push( "x-grid-row-alt");
59682                     }
59683                     if(r.dirty){
59684                         alt.push(" x-grid-dirty-row");
59685                     }
59686                     rp.cells = lcb;
59687                     if(this.getRowClass){
59688                         alt.push( this.getRowClass(r, rowIndex));
59689                     }
59690                     if (hasListener) {
59691                         rowcfg = {
59692                              
59693                             record: r,
59694                             rowIndex : rowIndex,
59695                             rowClass : ''
59696                         };
59697                         this.grid.fireEvent('rowclass', this, rowcfg);
59698                         alt.push(rowcfg.rowClass);
59699                     }
59700                     
59701                     rp.alt = alt.join(" ");
59702                     rp.cells = lcb.join("");
59703                     lbuf[lbuf.length] = rt.apply(rp);
59704                     rp.cells = cb.join("");
59705                     buf[buf.length] =  rt.apply(rp);
59706                 }
59707                 return [lbuf.join(""), buf.join("")];
59708             },
59709
59710     renderBody : function(){
59711         var markup = this.renderRows();
59712         var bt = this.templates.body;
59713         return [bt.apply({rows: markup[0]}), bt.apply({rows: markup[1]})];
59714     },
59715
59716     /**
59717      * Refreshes the grid
59718      * @param {Boolean} headersToo
59719      */
59720     refresh : function(headersToo){
59721         this.fireEvent("beforerefresh", this);
59722         this.grid.stopEditing();
59723         var result = this.renderBody();
59724         this.lockedBody.update(result[0]);
59725         this.mainBody.update(result[1]);
59726         if(headersToo === true){
59727             this.updateHeaders();
59728             this.updateColumns();
59729             this.updateSplitters();
59730             this.updateHeaderSortState();
59731         }
59732         this.syncRowHeights();
59733         this.layout();
59734         this.fireEvent("refresh", this);
59735     },
59736
59737     handleColumnMove : function(cm, oldIndex, newIndex){
59738         this.indexMap = null;
59739         var s = this.getScrollState();
59740         this.refresh(true);
59741         this.restoreScroll(s);
59742         this.afterMove(newIndex);
59743     },
59744
59745     afterMove : function(colIndex){
59746         if(this.enableMoveAnim && Roo.enableFx){
59747             this.fly(this.getHeaderCell(colIndex).firstChild).highlight(this.hlColor);
59748         }
59749         // if multisort - fix sortOrder, and reload..
59750         if (this.grid.dataSource.multiSort) {
59751             // the we can call sort again..
59752             var dm = this.grid.dataSource;
59753             var cm = this.grid.colModel;
59754             var so = [];
59755             for(var i = 0; i < cm.config.length; i++ ) {
59756                 
59757                 if ((typeof(dm.sortToggle[cm.config[i].dataIndex]) == 'undefined')) {
59758                     continue; // dont' bother, it's not in sort list or being set.
59759                 }
59760                 
59761                 so.push(cm.config[i].dataIndex);
59762             };
59763             dm.sortOrder = so;
59764             dm.load(dm.lastOptions);
59765             
59766             
59767         }
59768         
59769     },
59770
59771     updateCell : function(dm, rowIndex, dataIndex){
59772         var colIndex = this.getColumnIndexByDataIndex(dataIndex);
59773         if(typeof colIndex == "undefined"){ // not present in grid
59774             return;
59775         }
59776         var cm = this.grid.colModel;
59777         var cell = this.getCell(rowIndex, colIndex);
59778         var cellText = this.getCellText(rowIndex, colIndex);
59779
59780         var p = {
59781             cellId : "x-grid-cell-" + rowIndex + "-" + colIndex,
59782             id : cm.getColumnId(colIndex),
59783             css: colIndex == cm.getColumnCount()-1 ? "x-grid-col-last" : ""
59784         };
59785         var renderer = cm.getRenderer(colIndex);
59786         var val = renderer(dm.getValueAt(rowIndex, dataIndex), p, rowIndex, colIndex, dm);
59787         if(typeof val == "undefined" || val === "") {
59788             val = "&#160;";
59789         }
59790         cellText.innerHTML = val;
59791         cell.className = this.cellClass + " " + this.idToCssName(p.cellId) + " " + p.css;
59792         this.syncRowHeights(rowIndex, rowIndex);
59793     },
59794
59795     calcColumnWidth : function(colIndex, maxRowsToMeasure){
59796         var maxWidth = 0;
59797         if(this.grid.autoSizeHeaders){
59798             var h = this.getHeaderCellMeasure(colIndex);
59799             maxWidth = Math.max(maxWidth, h.scrollWidth);
59800         }
59801         var tb, index;
59802         if(this.cm.isLocked(colIndex)){
59803             tb = this.getLockedTable();
59804             index = colIndex;
59805         }else{
59806             tb = this.getBodyTable();
59807             index = colIndex - this.cm.getLockedCount();
59808         }
59809         if(tb && tb.rows){
59810             var rows = tb.rows;
59811             var stopIndex = Math.min(maxRowsToMeasure || rows.length, rows.length);
59812             for(var i = 0; i < stopIndex; i++){
59813                 var cell = rows[i].childNodes[index].firstChild;
59814                 maxWidth = Math.max(maxWidth, cell.scrollWidth);
59815             }
59816         }
59817         return maxWidth + /*margin for error in IE*/ 5;
59818     },
59819     /**
59820      * Autofit a column to its content.
59821      * @param {Number} colIndex
59822      * @param {Boolean} forceMinSize true to force the column to go smaller if possible
59823      */
59824      autoSizeColumn : function(colIndex, forceMinSize, suppressEvent){
59825          if(this.cm.isHidden(colIndex)){
59826              return; // can't calc a hidden column
59827          }
59828         if(forceMinSize){
59829             var cid = this.cm.getColumnId(colIndex);
59830             this.css.updateRule(this.colSelector +this.idToCssName( cid), "width", this.grid.minColumnWidth + "px");
59831            if(this.grid.autoSizeHeaders){
59832                this.css.updateRule(this.hdSelector + this.idToCssName(cid), "width", this.grid.minColumnWidth + "px");
59833            }
59834         }
59835         var newWidth = this.calcColumnWidth(colIndex);
59836         this.cm.setColumnWidth(colIndex,
59837             Math.max(this.grid.minColumnWidth, newWidth), suppressEvent);
59838         if(!suppressEvent){
59839             this.grid.fireEvent("columnresize", colIndex, newWidth);
59840         }
59841     },
59842
59843     /**
59844      * Autofits all columns to their content and then expands to fit any extra space in the grid
59845      */
59846      autoSizeColumns : function(){
59847         var cm = this.grid.colModel;
59848         var colCount = cm.getColumnCount();
59849         for(var i = 0; i < colCount; i++){
59850             this.autoSizeColumn(i, true, true);
59851         }
59852         if(cm.getTotalWidth() < this.scroller.dom.clientWidth){
59853             this.fitColumns();
59854         }else{
59855             this.updateColumns();
59856             this.layout();
59857         }
59858     },
59859
59860     /**
59861      * Autofits all columns to the grid's width proportionate with their current size
59862      * @param {Boolean} reserveScrollSpace Reserve space for a scrollbar
59863      */
59864     fitColumns : function(reserveScrollSpace){
59865         var cm = this.grid.colModel;
59866         var colCount = cm.getColumnCount();
59867         var cols = [];
59868         var width = 0;
59869         var i, w;
59870         for (i = 0; i < colCount; i++){
59871             if(!cm.isHidden(i) && !cm.isFixed(i)){
59872                 w = cm.getColumnWidth(i);
59873                 cols.push(i);
59874                 cols.push(w);
59875                 width += w;
59876             }
59877         }
59878         var avail = Math.min(this.scroller.dom.clientWidth, this.el.getWidth());
59879         if(reserveScrollSpace){
59880             avail -= 17;
59881         }
59882         var frac = (avail - cm.getTotalWidth())/width;
59883         while (cols.length){
59884             w = cols.pop();
59885             i = cols.pop();
59886             cm.setColumnWidth(i, Math.floor(w + w*frac), true);
59887         }
59888         this.updateColumns();
59889         this.layout();
59890     },
59891
59892     onRowSelect : function(rowIndex){
59893         var row = this.getRowComposite(rowIndex);
59894         row.addClass("x-grid-row-selected");
59895     },
59896
59897     onRowDeselect : function(rowIndex){
59898         var row = this.getRowComposite(rowIndex);
59899         row.removeClass("x-grid-row-selected");
59900     },
59901
59902     onCellSelect : function(row, col){
59903         var cell = this.getCell(row, col);
59904         if(cell){
59905             Roo.fly(cell).addClass("x-grid-cell-selected");
59906         }
59907     },
59908
59909     onCellDeselect : function(row, col){
59910         var cell = this.getCell(row, col);
59911         if(cell){
59912             Roo.fly(cell).removeClass("x-grid-cell-selected");
59913         }
59914     },
59915
59916     updateHeaderSortState : function(){
59917         
59918         // sort state can be single { field: xxx, direction : yyy}
59919         // or   { xxx=>ASC , yyy : DESC ..... }
59920         
59921         var mstate = {};
59922         if (!this.ds.multiSort) { 
59923             var state = this.ds.getSortState();
59924             if(!state){
59925                 return;
59926             }
59927             mstate[state.field] = state.direction;
59928             // FIXME... - this is not used here.. but might be elsewhere..
59929             this.sortState = state;
59930             
59931         } else {
59932             mstate = this.ds.sortToggle;
59933         }
59934         //remove existing sort classes..
59935         
59936         var sc = this.sortClasses;
59937         var hds = this.el.select(this.headerSelector).removeClass(sc);
59938         
59939         for(var f in mstate) {
59940         
59941             var sortColumn = this.cm.findColumnIndex(f);
59942             
59943             if(sortColumn != -1){
59944                 var sortDir = mstate[f];        
59945                 hds.item(sortColumn).addClass(sc[sortDir == "DESC" ? 1 : 0]);
59946             }
59947         }
59948         
59949          
59950         
59951     },
59952
59953
59954     handleHeaderClick : function(g, index,e){
59955         
59956         Roo.log("header click");
59957         
59958         if (Roo.isTouch) {
59959             // touch events on header are handled by context
59960             this.handleHdCtx(g,index,e);
59961             return;
59962         }
59963         
59964         
59965         if(this.headersDisabled){
59966             return;
59967         }
59968         var dm = g.dataSource, cm = g.colModel;
59969         if(!cm.isSortable(index)){
59970             return;
59971         }
59972         g.stopEditing();
59973         
59974         if (dm.multiSort) {
59975             // update the sortOrder
59976             var so = [];
59977             for(var i = 0; i < cm.config.length; i++ ) {
59978                 
59979                 if ((typeof(dm.sortToggle[cm.config[i].dataIndex]) == 'undefined') && (index != i)) {
59980                     continue; // dont' bother, it's not in sort list or being set.
59981                 }
59982                 
59983                 so.push(cm.config[i].dataIndex);
59984             };
59985             dm.sortOrder = so;
59986         }
59987         
59988         
59989         dm.sort(cm.getDataIndex(index));
59990     },
59991
59992
59993     destroy : function(){
59994         if(this.colMenu){
59995             this.colMenu.removeAll();
59996             Roo.menu.MenuMgr.unregister(this.colMenu);
59997             this.colMenu.getEl().remove();
59998             delete this.colMenu;
59999         }
60000         if(this.hmenu){
60001             this.hmenu.removeAll();
60002             Roo.menu.MenuMgr.unregister(this.hmenu);
60003             this.hmenu.getEl().remove();
60004             delete this.hmenu;
60005         }
60006         if(this.grid.enableColumnMove){
60007             var dds = Roo.dd.DDM.ids['gridHeader' + this.grid.getGridEl().id];
60008             if(dds){
60009                 for(var dd in dds){
60010                     if(!dds[dd].config.isTarget && dds[dd].dragElId){
60011                         var elid = dds[dd].dragElId;
60012                         dds[dd].unreg();
60013                         Roo.get(elid).remove();
60014                     } else if(dds[dd].config.isTarget){
60015                         dds[dd].proxyTop.remove();
60016                         dds[dd].proxyBottom.remove();
60017                         dds[dd].unreg();
60018                     }
60019                     if(Roo.dd.DDM.locationCache[dd]){
60020                         delete Roo.dd.DDM.locationCache[dd];
60021                     }
60022                 }
60023                 delete Roo.dd.DDM.ids['gridHeader' + this.grid.getGridEl().id];
60024             }
60025         }
60026         Roo.util.CSS.removeStyleSheet(this.idToCssName(this.grid.id) + '-cssrules');
60027         this.bind(null, null);
60028         Roo.EventManager.removeResizeListener(this.onWindowResize, this);
60029     },
60030
60031     handleLockChange : function(){
60032         this.refresh(true);
60033     },
60034
60035     onDenyColumnLock : function(){
60036
60037     },
60038
60039     onDenyColumnHide : function(){
60040
60041     },
60042
60043     handleHdMenuClick : function(item){
60044         var index = this.hdCtxIndex;
60045         var cm = this.cm, ds = this.ds;
60046         switch(item.id){
60047             case "asc":
60048                 ds.sort(cm.getDataIndex(index), "ASC");
60049                 break;
60050             case "desc":
60051                 ds.sort(cm.getDataIndex(index), "DESC");
60052                 break;
60053             case "lock":
60054                 var lc = cm.getLockedCount();
60055                 if(cm.getColumnCount(true) <= lc+1){
60056                     this.onDenyColumnLock();
60057                     return;
60058                 }
60059                 if(lc != index){
60060                     cm.setLocked(index, true, true);
60061                     cm.moveColumn(index, lc);
60062                     this.grid.fireEvent("columnmove", index, lc);
60063                 }else{
60064                     cm.setLocked(index, true);
60065                 }
60066             break;
60067             case "unlock":
60068                 var lc = cm.getLockedCount();
60069                 if((lc-1) != index){
60070                     cm.setLocked(index, false, true);
60071                     cm.moveColumn(index, lc-1);
60072                     this.grid.fireEvent("columnmove", index, lc-1);
60073                 }else{
60074                     cm.setLocked(index, false);
60075                 }
60076             break;
60077             case 'wider': // used to expand cols on touch..
60078             case 'narrow':
60079                 var cw = cm.getColumnWidth(index);
60080                 cw += (item.id == 'wider' ? 1 : -1) * 50;
60081                 cw = Math.max(0, cw);
60082                 cw = Math.min(cw,4000);
60083                 cm.setColumnWidth(index, cw);
60084                 break;
60085                 
60086             default:
60087                 index = cm.getIndexById(item.id.substr(4));
60088                 if(index != -1){
60089                     if(item.checked && cm.getColumnCount(true) <= 1){
60090                         this.onDenyColumnHide();
60091                         return false;
60092                     }
60093                     cm.setHidden(index, item.checked);
60094                 }
60095         }
60096         return true;
60097     },
60098
60099     beforeColMenuShow : function(){
60100         var cm = this.cm,  colCount = cm.getColumnCount();
60101         this.colMenu.removeAll();
60102         for(var i = 0; i < colCount; i++){
60103             this.colMenu.add(new Roo.menu.CheckItem({
60104                 id: "col-"+cm.getColumnId(i),
60105                 text: cm.getColumnHeader(i),
60106                 checked: !cm.isHidden(i),
60107                 hideOnClick:false
60108             }));
60109         }
60110     },
60111
60112     handleHdCtx : function(g, index, e){
60113         e.stopEvent();
60114         var hd = this.getHeaderCell(index);
60115         this.hdCtxIndex = index;
60116         var ms = this.hmenu.items, cm = this.cm;
60117         ms.get("asc").setDisabled(!cm.isSortable(index));
60118         ms.get("desc").setDisabled(!cm.isSortable(index));
60119         if(this.grid.enableColLock !== false){
60120             ms.get("lock").setDisabled(cm.isLocked(index));
60121             ms.get("unlock").setDisabled(!cm.isLocked(index));
60122         }
60123         this.hmenu.show(hd, "tl-bl");
60124     },
60125
60126     handleHdOver : function(e){
60127         var hd = this.findHeaderCell(e.getTarget());
60128         if(hd && !this.headersDisabled){
60129             if(this.grid.colModel.isSortable(this.getCellIndex(hd))){
60130                this.fly(hd).addClass("x-grid-hd-over");
60131             }
60132         }
60133     },
60134
60135     handleHdOut : function(e){
60136         var hd = this.findHeaderCell(e.getTarget());
60137         if(hd){
60138             this.fly(hd).removeClass("x-grid-hd-over");
60139         }
60140     },
60141
60142     handleSplitDblClick : function(e, t){
60143         var i = this.getCellIndex(t);
60144         if(this.grid.enableColumnResize !== false && this.cm.isResizable(i) && !this.cm.isFixed(i)){
60145             this.autoSizeColumn(i, true);
60146             this.layout();
60147         }
60148     },
60149
60150     render : function(){
60151
60152         var cm = this.cm;
60153         var colCount = cm.getColumnCount();
60154
60155         if(this.grid.monitorWindowResize === true){
60156             Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
60157         }
60158         var header = this.renderHeaders();
60159         var body = this.templates.body.apply({rows:""});
60160         var html = this.templates.master.apply({
60161             lockedBody: body,
60162             body: body,
60163             lockedHeader: header[0],
60164             header: header[1]
60165         });
60166
60167         //this.updateColumns();
60168
60169         this.grid.getGridEl().dom.innerHTML = html;
60170
60171         this.initElements();
60172         
60173         // a kludge to fix the random scolling effect in webkit
60174         this.el.on("scroll", function() {
60175             this.el.dom.scrollTop=0; // hopefully not recursive..
60176         },this);
60177
60178         this.scroller.on("scroll", this.handleScroll, this);
60179         this.lockedBody.on("mousewheel", this.handleWheel, this);
60180         this.mainBody.on("mousewheel", this.handleWheel, this);
60181
60182         this.mainHd.on("mouseover", this.handleHdOver, this);
60183         this.mainHd.on("mouseout", this.handleHdOut, this);
60184         this.mainHd.on("dblclick", this.handleSplitDblClick, this,
60185                 {delegate: "."+this.splitClass});
60186
60187         this.lockedHd.on("mouseover", this.handleHdOver, this);
60188         this.lockedHd.on("mouseout", this.handleHdOut, this);
60189         this.lockedHd.on("dblclick", this.handleSplitDblClick, this,
60190                 {delegate: "."+this.splitClass});
60191
60192         if(this.grid.enableColumnResize !== false && Roo.grid.SplitDragZone){
60193             new Roo.grid.SplitDragZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
60194         }
60195
60196         this.updateSplitters();
60197
60198         if(this.grid.enableColumnMove && Roo.grid.HeaderDragZone){
60199             new Roo.grid.HeaderDragZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
60200             new Roo.grid.HeaderDropZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
60201         }
60202
60203         if(this.grid.enableCtxMenu !== false && Roo.menu.Menu){
60204             this.hmenu = new Roo.menu.Menu({id: this.grid.id + "-hctx"});
60205             this.hmenu.add(
60206                 {id:"asc", text: this.sortAscText, cls: "xg-hmenu-sort-asc"},
60207                 {id:"desc", text: this.sortDescText, cls: "xg-hmenu-sort-desc"}
60208             );
60209             if(this.grid.enableColLock !== false){
60210                 this.hmenu.add('-',
60211                     {id:"lock", text: this.lockText, cls: "xg-hmenu-lock"},
60212                     {id:"unlock", text: this.unlockText, cls: "xg-hmenu-unlock"}
60213                 );
60214             }
60215             if (Roo.isTouch) {
60216                  this.hmenu.add('-',
60217                     {id:"wider", text: this.columnsWiderText},
60218                     {id:"narrow", text: this.columnsNarrowText }
60219                 );
60220                 
60221                  
60222             }
60223             
60224             if(this.grid.enableColumnHide !== false){
60225
60226                 this.colMenu = new Roo.menu.Menu({id:this.grid.id + "-hcols-menu"});
60227                 this.colMenu.on("beforeshow", this.beforeColMenuShow, this);
60228                 this.colMenu.on("itemclick", this.handleHdMenuClick, this);
60229
60230                 this.hmenu.add('-',
60231                     {id:"columns", text: this.columnsText, menu: this.colMenu}
60232                 );
60233             }
60234             this.hmenu.on("itemclick", this.handleHdMenuClick, this);
60235
60236             this.grid.on("headercontextmenu", this.handleHdCtx, this);
60237         }
60238
60239         if((this.grid.enableDragDrop || this.grid.enableDrag) && Roo.grid.GridDragZone){
60240             this.dd = new Roo.grid.GridDragZone(this.grid, {
60241                 ddGroup : this.grid.ddGroup || 'GridDD'
60242             });
60243             
60244         }
60245
60246         /*
60247         for(var i = 0; i < colCount; i++){
60248             if(cm.isHidden(i)){
60249                 this.hideColumn(i);
60250             }
60251             if(cm.config[i].align){
60252                 this.css.updateRule(this.colSelector + i, "textAlign", cm.config[i].align);
60253                 this.css.updateRule(this.hdSelector + i, "textAlign", cm.config[i].align);
60254             }
60255         }*/
60256         
60257         this.updateHeaderSortState();
60258
60259         this.beforeInitialResize();
60260         this.layout(true);
60261
60262         // two part rendering gives faster view to the user
60263         this.renderPhase2.defer(1, this);
60264     },
60265
60266     renderPhase2 : function(){
60267         // render the rows now
60268         this.refresh();
60269         if(this.grid.autoSizeColumns){
60270             this.autoSizeColumns();
60271         }
60272     },
60273
60274     beforeInitialResize : function(){
60275
60276     },
60277
60278     onColumnSplitterMoved : function(i, w){
60279         this.userResized = true;
60280         var cm = this.grid.colModel;
60281         cm.setColumnWidth(i, w, true);
60282         var cid = cm.getColumnId(i);
60283         this.css.updateRule(this.colSelector + this.idToCssName(cid), "width", (w-this.borderWidth) + "px");
60284         this.css.updateRule(this.hdSelector + this.idToCssName(cid), "width", (w-this.borderWidth) + "px");
60285         this.updateSplitters();
60286         this.layout();
60287         this.grid.fireEvent("columnresize", i, w);
60288     },
60289
60290     syncRowHeights : function(startIndex, endIndex){
60291         if(this.grid.enableRowHeightSync === true && this.cm.getLockedCount() > 0){
60292             startIndex = startIndex || 0;
60293             var mrows = this.getBodyTable().rows;
60294             var lrows = this.getLockedTable().rows;
60295             var len = mrows.length-1;
60296             endIndex = Math.min(endIndex || len, len);
60297             for(var i = startIndex; i <= endIndex; i++){
60298                 var m = mrows[i], l = lrows[i];
60299                 var h = Math.max(m.offsetHeight, l.offsetHeight);
60300                 m.style.height = l.style.height = h + "px";
60301             }
60302         }
60303     },
60304
60305     layout : function(initialRender, is2ndPass)
60306     {
60307         var g = this.grid;
60308         var auto = g.autoHeight;
60309         var scrollOffset = 16;
60310         var c = g.getGridEl(), cm = this.cm,
60311                 expandCol = g.autoExpandColumn,
60312                 gv = this;
60313         //c.beginMeasure();
60314
60315         if(!c.dom.offsetWidth){ // display:none?
60316             if(initialRender){
60317                 this.lockedWrap.show();
60318                 this.mainWrap.show();
60319             }
60320             return;
60321         }
60322
60323         var hasLock = this.cm.isLocked(0);
60324
60325         var tbh = this.headerPanel.getHeight();
60326         var bbh = this.footerPanel.getHeight();
60327
60328         if(auto){
60329             var ch = this.getBodyTable().offsetHeight + tbh + bbh + this.mainHd.getHeight();
60330             var newHeight = ch + c.getBorderWidth("tb");
60331             if(g.maxHeight){
60332                 newHeight = Math.min(g.maxHeight, newHeight);
60333             }
60334             c.setHeight(newHeight);
60335         }
60336
60337         if(g.autoWidth){
60338             c.setWidth(cm.getTotalWidth()+c.getBorderWidth('lr'));
60339         }
60340
60341         var s = this.scroller;
60342
60343         var csize = c.getSize(true);
60344
60345         this.el.setSize(csize.width, csize.height);
60346
60347         this.headerPanel.setWidth(csize.width);
60348         this.footerPanel.setWidth(csize.width);
60349
60350         var hdHeight = this.mainHd.getHeight();
60351         var vw = csize.width;
60352         var vh = csize.height - (tbh + bbh);
60353
60354         s.setSize(vw, vh);
60355
60356         var bt = this.getBodyTable();
60357         
60358         if(cm.getLockedCount() == cm.config.length){
60359             bt = this.getLockedTable();
60360         }
60361         
60362         var ltWidth = hasLock ?
60363                       Math.max(this.getLockedTable().offsetWidth, this.lockedHd.dom.firstChild.offsetWidth) : 0;
60364
60365         var scrollHeight = bt.offsetHeight;
60366         var scrollWidth = ltWidth + bt.offsetWidth;
60367         var vscroll = false, hscroll = false;
60368
60369         this.scrollSizer.setSize(scrollWidth, scrollHeight+hdHeight);
60370
60371         var lw = this.lockedWrap, mw = this.mainWrap;
60372         var lb = this.lockedBody, mb = this.mainBody;
60373
60374         setTimeout(function(){
60375             var t = s.dom.offsetTop;
60376             var w = s.dom.clientWidth,
60377                 h = s.dom.clientHeight;
60378
60379             lw.setTop(t);
60380             lw.setSize(ltWidth, h);
60381
60382             mw.setLeftTop(ltWidth, t);
60383             mw.setSize(w-ltWidth, h);
60384
60385             lb.setHeight(h-hdHeight);
60386             mb.setHeight(h-hdHeight);
60387
60388             if(is2ndPass !== true && !gv.userResized && expandCol){
60389                 // high speed resize without full column calculation
60390                 
60391                 var ci = cm.getIndexById(expandCol);
60392                 if (ci < 0) {
60393                     ci = cm.findColumnIndex(expandCol);
60394                 }
60395                 ci = Math.max(0, ci); // make sure it's got at least the first col.
60396                 var expandId = cm.getColumnId(ci);
60397                 var  tw = cm.getTotalWidth(false);
60398                 var currentWidth = cm.getColumnWidth(ci);
60399                 var cw = Math.min(Math.max(((w-tw)+currentWidth-2)-/*scrollbar*/(w <= s.dom.offsetWidth ? 0 : 18), g.autoExpandMin), g.autoExpandMax);
60400                 if(currentWidth != cw){
60401                     cm.setColumnWidth(ci, cw, true);
60402                     gv.css.updateRule(gv.colSelector+gv.idToCssName(expandId), "width", (cw - gv.borderWidth) + "px");
60403                     gv.css.updateRule(gv.hdSelector+gv.idToCssName(expandId), "width", (cw - gv.borderWidth) + "px");
60404                     gv.updateSplitters();
60405                     gv.layout(false, true);
60406                 }
60407             }
60408
60409             if(initialRender){
60410                 lw.show();
60411                 mw.show();
60412             }
60413             //c.endMeasure();
60414         }, 10);
60415     },
60416
60417     onWindowResize : function(){
60418         if(!this.grid.monitorWindowResize || this.grid.autoHeight){
60419             return;
60420         }
60421         this.layout();
60422     },
60423
60424     appendFooter : function(parentEl){
60425         return null;
60426     },
60427
60428     sortAscText : "Sort Ascending",
60429     sortDescText : "Sort Descending",
60430     lockText : "Lock Column",
60431     unlockText : "Unlock Column",
60432     columnsText : "Columns",
60433  
60434     columnsWiderText : "Wider",
60435     columnsNarrowText : "Thinner"
60436 });
60437
60438
60439 Roo.grid.GridView.ColumnDragZone = function(grid, hd){
60440     Roo.grid.GridView.ColumnDragZone.superclass.constructor.call(this, grid, hd, null);
60441     this.proxy.el.addClass('x-grid3-col-dd');
60442 };
60443
60444 Roo.extend(Roo.grid.GridView.ColumnDragZone, Roo.grid.HeaderDragZone, {
60445     handleMouseDown : function(e){
60446
60447     },
60448
60449     callHandleMouseDown : function(e){
60450         Roo.grid.GridView.ColumnDragZone.superclass.handleMouseDown.call(this, e);
60451     }
60452 });
60453 /*
60454  * Based on:
60455  * Ext JS Library 1.1.1
60456  * Copyright(c) 2006-2007, Ext JS, LLC.
60457  *
60458  * Originally Released Under LGPL - original licence link has changed is not relivant.
60459  *
60460  * Fork - LGPL
60461  * <script type="text/javascript">
60462  */
60463  /**
60464  * @extends Roo.dd.DDProxy
60465  * @class Roo.grid.SplitDragZone
60466  * Support for Column Header resizing
60467  * @constructor
60468  * @param {Object} config
60469  */
60470 // private
60471 // This is a support class used internally by the Grid components
60472 Roo.grid.SplitDragZone = function(grid, hd, hd2){
60473     this.grid = grid;
60474     this.view = grid.getView();
60475     this.proxy = this.view.resizeProxy;
60476     Roo.grid.SplitDragZone.superclass.constructor.call(
60477         this,
60478         hd, // ID
60479         "gridSplitters" + this.grid.getGridEl().id, // SGROUP
60480         {  // CONFIG
60481             dragElId : Roo.id(this.proxy.dom),
60482             resizeFrame:false
60483         }
60484     );
60485     
60486     this.setHandleElId(Roo.id(hd));
60487     if (hd2 !== false) {
60488         this.setOuterHandleElId(Roo.id(hd2));
60489     }
60490     
60491     this.scroll = false;
60492 };
60493 Roo.extend(Roo.grid.SplitDragZone, Roo.dd.DDProxy, {
60494     fly: Roo.Element.fly,
60495
60496     b4StartDrag : function(x, y){
60497         this.view.headersDisabled = true;
60498         var h = this.view.mainWrap ? this.view.mainWrap.getHeight() : (
60499                     this.view.headEl.getHeight() + this.view.bodyEl.getHeight()
60500         );
60501         this.proxy.setHeight(h);
60502         
60503         // for old system colWidth really stored the actual width?
60504         // in bootstrap we tried using xs/ms/etc.. to do % sizing?
60505         // which in reality did not work.. - it worked only for fixed sizes
60506         // for resizable we need to use actual sizes.
60507         var w = this.cm.getColumnWidth(this.cellIndex);
60508         if (!this.view.mainWrap) {
60509             // bootstrap.
60510             w = this.view.getHeaderIndex(this.cellIndex).getWidth();
60511         }
60512         
60513         
60514         
60515         // this was w-this.grid.minColumnWidth;
60516         // doesnt really make sense? - w = thie curren width or the rendered one?
60517         var minw = Math.max(w-this.grid.minColumnWidth, 0);
60518         this.resetConstraints();
60519         this.setXConstraint(minw, 1000);
60520         this.setYConstraint(0, 0);
60521         this.minX = x - minw;
60522         this.maxX = x + 1000;
60523         this.startPos = x;
60524         if (!this.view.mainWrap) { // this is Bootstrap code..
60525             this.getDragEl().style.display='block';
60526         }
60527         
60528         Roo.dd.DDProxy.prototype.b4StartDrag.call(this, x, y);
60529     },
60530
60531
60532     handleMouseDown : function(e){
60533         ev = Roo.EventObject.setEvent(e);
60534         var t = this.fly(ev.getTarget());
60535         if(t.hasClass("x-grid-split")){
60536             this.cellIndex = this.view.getCellIndex(t.dom);
60537             this.split = t.dom;
60538             this.cm = this.grid.colModel;
60539             if(this.cm.isResizable(this.cellIndex) && !this.cm.isFixed(this.cellIndex)){
60540                 Roo.grid.SplitDragZone.superclass.handleMouseDown.apply(this, arguments);
60541             }
60542         }
60543     },
60544
60545     endDrag : function(e){
60546         this.view.headersDisabled = false;
60547         var endX = Math.max(this.minX, Roo.lib.Event.getPageX(e));
60548         var diff = endX - this.startPos;
60549         // 
60550         var w = this.cm.getColumnWidth(this.cellIndex);
60551         if (!this.view.mainWrap) {
60552             w = 0;
60553         }
60554         this.view.onColumnSplitterMoved(this.cellIndex, w+diff);
60555     },
60556
60557     autoOffset : function(){
60558         this.setDelta(0,0);
60559     }
60560 });/*
60561  * Based on:
60562  * Ext JS Library 1.1.1
60563  * Copyright(c) 2006-2007, Ext JS, LLC.
60564  *
60565  * Originally Released Under LGPL - original licence link has changed is not relivant.
60566  *
60567  * Fork - LGPL
60568  * <script type="text/javascript">
60569  */
60570  
60571 // private
60572 // This is a support class used internally by the Grid components
60573 Roo.grid.GridDragZone = function(grid, config){
60574     this.view = grid.getView();
60575     Roo.grid.GridDragZone.superclass.constructor.call(this, this.view.mainBody.dom, config);
60576     if(this.view.lockedBody){
60577         this.setHandleElId(Roo.id(this.view.mainBody.dom));
60578         this.setOuterHandleElId(Roo.id(this.view.lockedBody.dom));
60579     }
60580     this.scroll = false;
60581     this.grid = grid;
60582     this.ddel = document.createElement('div');
60583     this.ddel.className = 'x-grid-dd-wrap';
60584 };
60585
60586 Roo.extend(Roo.grid.GridDragZone, Roo.dd.DragZone, {
60587     ddGroup : "GridDD",
60588
60589     getDragData : function(e){
60590         var t = Roo.lib.Event.getTarget(e);
60591         var rowIndex = this.view.findRowIndex(t);
60592         var sm = this.grid.selModel;
60593             
60594         //Roo.log(rowIndex);
60595         
60596         if (sm.getSelectedCell) {
60597             // cell selection..
60598             if (!sm.getSelectedCell()) {
60599                 return false;
60600             }
60601             if (rowIndex != sm.getSelectedCell()[0]) {
60602                 return false;
60603             }
60604         
60605         }
60606         if (sm.getSelections && sm.getSelections().length < 1) {
60607             return false;
60608         }
60609         
60610         
60611         // before it used to all dragging of unseleted... - now we dont do that.
60612         if(rowIndex !== false){
60613             
60614             // if editorgrid.. 
60615             
60616             
60617             //Roo.log([ sm.getSelectedCell() ? sm.getSelectedCell()[0] : 'NO' , rowIndex ]);
60618                
60619             //if(!sm.isSelected(rowIndex) || e.hasModifier()){
60620               //  
60621             //}
60622             if (e.hasModifier()){
60623                 sm.handleMouseDown(e, t); // non modifier buttons are handled by row select.
60624             }
60625             
60626             Roo.log("getDragData");
60627             
60628             return {
60629                 grid: this.grid,
60630                 ddel: this.ddel,
60631                 rowIndex: rowIndex,
60632                 selections: sm.getSelections ? sm.getSelections() : (
60633                     sm.getSelectedCell() ? [ this.grid.ds.getAt(sm.getSelectedCell()[0]) ] : [])
60634             };
60635         }
60636         return false;
60637     },
60638     
60639     
60640     onInitDrag : function(e){
60641         var data = this.dragData;
60642         this.ddel.innerHTML = this.grid.getDragDropText();
60643         this.proxy.update(this.ddel);
60644         // fire start drag?
60645     },
60646
60647     afterRepair : function(){
60648         this.dragging = false;
60649     },
60650
60651     getRepairXY : function(e, data){
60652         return false;
60653     },
60654
60655     onEndDrag : function(data, e){
60656         // fire end drag?
60657     },
60658
60659     onValidDrop : function(dd, e, id){
60660         // fire drag drop?
60661         this.hideProxy();
60662     },
60663
60664     beforeInvalidDrop : function(e, id){
60665
60666     }
60667 });/*
60668  * Based on:
60669  * Ext JS Library 1.1.1
60670  * Copyright(c) 2006-2007, Ext JS, LLC.
60671  *
60672  * Originally Released Under LGPL - original licence link has changed is not relivant.
60673  *
60674  * Fork - LGPL
60675  * <script type="text/javascript">
60676  */
60677  
60678
60679 /**
60680  * @class Roo.grid.ColumnModel
60681  * @extends Roo.util.Observable
60682  * This is the default implementation of a ColumnModel used by the Grid. It defines
60683  * the columns in the grid.
60684  * <br>Usage:<br>
60685  <pre><code>
60686  var colModel = new Roo.grid.ColumnModel([
60687         {header: "Ticker", width: 60, sortable: true, locked: true},
60688         {header: "Company Name", width: 150, sortable: true},
60689         {header: "Market Cap.", width: 100, sortable: true},
60690         {header: "$ Sales", width: 100, sortable: true, renderer: money},
60691         {header: "Employees", width: 100, sortable: true, resizable: false}
60692  ]);
60693  </code></pre>
60694  * <p>
60695  
60696  * The config options listed for this class are options which may appear in each
60697  * individual column definition.
60698  * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
60699  * @constructor
60700  * @param {Object} config An Array of column config objects. See this class's
60701  * config objects for details.
60702 */
60703 Roo.grid.ColumnModel = function(config){
60704         /**
60705      * The config passed into the constructor
60706      */
60707     this.config = []; //config;
60708     this.lookup = {};
60709
60710     // if no id, create one
60711     // if the column does not have a dataIndex mapping,
60712     // map it to the order it is in the config
60713     for(var i = 0, len = config.length; i < len; i++){
60714         this.addColumn(config[i]);
60715         
60716     }
60717
60718     /**
60719      * The width of columns which have no width specified (defaults to 100)
60720      * @type Number
60721      */
60722     this.defaultWidth = 100;
60723
60724     /**
60725      * Default sortable of columns which have no sortable specified (defaults to false)
60726      * @type Boolean
60727      */
60728     this.defaultSortable = false;
60729
60730     this.addEvents({
60731         /**
60732              * @event widthchange
60733              * Fires when the width of a column changes.
60734              * @param {ColumnModel} this
60735              * @param {Number} columnIndex The column index
60736              * @param {Number} newWidth The new width
60737              */
60738             "widthchange": true,
60739         /**
60740              * @event headerchange
60741              * Fires when the text of a header changes.
60742              * @param {ColumnModel} this
60743              * @param {Number} columnIndex The column index
60744              * @param {Number} newText The new header text
60745              */
60746             "headerchange": true,
60747         /**
60748              * @event hiddenchange
60749              * Fires when a column is hidden or "unhidden".
60750              * @param {ColumnModel} this
60751              * @param {Number} columnIndex The column index
60752              * @param {Boolean} hidden true if hidden, false otherwise
60753              */
60754             "hiddenchange": true,
60755             /**
60756          * @event columnmoved
60757          * Fires when a column is moved.
60758          * @param {ColumnModel} this
60759          * @param {Number} oldIndex
60760          * @param {Number} newIndex
60761          */
60762         "columnmoved" : true,
60763         /**
60764          * @event columlockchange
60765          * Fires when a column's locked state is changed
60766          * @param {ColumnModel} this
60767          * @param {Number} colIndex
60768          * @param {Boolean} locked true if locked
60769          */
60770         "columnlockchange" : true
60771     });
60772     Roo.grid.ColumnModel.superclass.constructor.call(this);
60773 };
60774 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
60775     /**
60776      * @cfg {String} header The header text to display in the Grid view.
60777      */
60778         /**
60779      * @cfg {String} xsHeader Header at Bootsrap Extra Small width (default for all)
60780      */
60781         /**
60782      * @cfg {String} smHeader Header at Bootsrap Small width
60783      */
60784         /**
60785      * @cfg {String} mdHeader Header at Bootsrap Medium width
60786      */
60787         /**
60788      * @cfg {String} lgHeader Header at Bootsrap Large width
60789      */
60790         /**
60791      * @cfg {String} xlHeader Header at Bootsrap extra Large width
60792      */
60793     /**
60794      * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
60795      * {@link Roo.data.Record} definition from which to draw the column's value. If not
60796      * specified, the column's index is used as an index into the Record's data Array.
60797      */
60798     /**
60799      * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
60800      * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
60801      */
60802     /**
60803      * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
60804      * Defaults to the value of the {@link #defaultSortable} property.
60805      * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
60806      */
60807     /**
60808      * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid.  Defaults to false.
60809      */
60810     /**
60811      * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed.  Defaults to false.
60812      */
60813     /**
60814      * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
60815      */
60816     /**
60817      * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
60818      */
60819     /**
60820      * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
60821      * given the cell's data value. See {@link #setRenderer}. If not specified, the
60822      * default renderer returns the escaped data value. If an object is returned (bootstrap only)
60823      * then it is treated as a Roo Component object instance, and it is rendered after the initial row is rendered
60824      */
60825        /**
60826      * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor 
60827      */
60828     /**
60829      * @cfg {String} align (Optional) Set the CSS text-align property of the column.  Defaults to undefined.
60830      */
60831     /**
60832      * @cfg {String} valign (Optional) Set the CSS vertical-align property of the column (eg. middle, top, bottom etc).  Defaults to undefined.
60833      */
60834     /**
60835      * @cfg {String} cursor (Optional)
60836      */
60837     /**
60838      * @cfg {String} tooltip (Optional)
60839      */
60840     /**
60841      * @cfg {Number} xs (Optional) can be '0' for hidden at this size (number less than 12)
60842      */
60843     /**
60844      * @cfg {Number} sm (Optional) can be '0' for hidden at this size (number less than 12)
60845      */
60846     /**
60847      * @cfg {Number} md (Optional) can be '0' for hidden at this size (number less than 12)
60848      */
60849     /**
60850      * @cfg {Number} lg (Optional) can be '0' for hidden at this size (number less than 12)
60851      */
60852         /**
60853      * @cfg {Number} xl (Optional) can be '0' for hidden at this size (number less than 12)
60854      */
60855     /**
60856      * Returns the id of the column at the specified index.
60857      * @param {Number} index The column index
60858      * @return {String} the id
60859      */
60860     getColumnId : function(index){
60861         return this.config[index].id;
60862     },
60863
60864     /**
60865      * Returns the column for a specified id.
60866      * @param {String} id The column id
60867      * @return {Object} the column
60868      */
60869     getColumnById : function(id){
60870         return this.lookup[id];
60871     },
60872
60873     
60874     /**
60875      * Returns the column Object for a specified dataIndex.
60876      * @param {String} dataIndex The column dataIndex
60877      * @return {Object|Boolean} the column or false if not found
60878      */
60879     getColumnByDataIndex: function(dataIndex){
60880         var index = this.findColumnIndex(dataIndex);
60881         return index > -1 ? this.config[index] : false;
60882     },
60883     
60884     /**
60885      * Returns the index for a specified column id.
60886      * @param {String} id The column id
60887      * @return {Number} the index, or -1 if not found
60888      */
60889     getIndexById : function(id){
60890         for(var i = 0, len = this.config.length; i < len; i++){
60891             if(this.config[i].id == id){
60892                 return i;
60893             }
60894         }
60895         return -1;
60896     },
60897     
60898     /**
60899      * Returns the index for a specified column dataIndex.
60900      * @param {String} dataIndex The column dataIndex
60901      * @return {Number} the index, or -1 if not found
60902      */
60903     
60904     findColumnIndex : function(dataIndex){
60905         for(var i = 0, len = this.config.length; i < len; i++){
60906             if(this.config[i].dataIndex == dataIndex){
60907                 return i;
60908             }
60909         }
60910         return -1;
60911     },
60912     
60913     
60914     moveColumn : function(oldIndex, newIndex){
60915         var c = this.config[oldIndex];
60916         this.config.splice(oldIndex, 1);
60917         this.config.splice(newIndex, 0, c);
60918         this.dataMap = null;
60919         this.fireEvent("columnmoved", this, oldIndex, newIndex);
60920     },
60921
60922     isLocked : function(colIndex){
60923         return this.config[colIndex].locked === true;
60924     },
60925
60926     setLocked : function(colIndex, value, suppressEvent){
60927         if(this.isLocked(colIndex) == value){
60928             return;
60929         }
60930         this.config[colIndex].locked = value;
60931         if(!suppressEvent){
60932             this.fireEvent("columnlockchange", this, colIndex, value);
60933         }
60934     },
60935
60936     getTotalLockedWidth : function(){
60937         var totalWidth = 0;
60938         for(var i = 0; i < this.config.length; i++){
60939             if(this.isLocked(i) && !this.isHidden(i)){
60940                 this.totalWidth += this.getColumnWidth(i);
60941             }
60942         }
60943         return totalWidth;
60944     },
60945
60946     getLockedCount : function(){
60947         for(var i = 0, len = this.config.length; i < len; i++){
60948             if(!this.isLocked(i)){
60949                 return i;
60950             }
60951         }
60952         
60953         return this.config.length;
60954     },
60955
60956     /**
60957      * Returns the number of columns.
60958      * @return {Number}
60959      */
60960     getColumnCount : function(visibleOnly){
60961         if(visibleOnly === true){
60962             var c = 0;
60963             for(var i = 0, len = this.config.length; i < len; i++){
60964                 if(!this.isHidden(i)){
60965                     c++;
60966                 }
60967             }
60968             return c;
60969         }
60970         return this.config.length;
60971     },
60972
60973     /**
60974      * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
60975      * @param {Function} fn
60976      * @param {Object} scope (optional)
60977      * @return {Array} result
60978      */
60979     getColumnsBy : function(fn, scope){
60980         var r = [];
60981         for(var i = 0, len = this.config.length; i < len; i++){
60982             var c = this.config[i];
60983             if(fn.call(scope||this, c, i) === true){
60984                 r[r.length] = c;
60985             }
60986         }
60987         return r;
60988     },
60989
60990     /**
60991      * Returns true if the specified column is sortable.
60992      * @param {Number} col The column index
60993      * @return {Boolean}
60994      */
60995     isSortable : function(col){
60996         if(typeof this.config[col].sortable == "undefined"){
60997             return this.defaultSortable;
60998         }
60999         return this.config[col].sortable;
61000     },
61001
61002     /**
61003      * Returns the rendering (formatting) function defined for the column.
61004      * @param {Number} col The column index.
61005      * @return {Function} The function used to render the cell. See {@link #setRenderer}.
61006      */
61007     getRenderer : function(col){
61008         if(!this.config[col].renderer){
61009             return Roo.grid.ColumnModel.defaultRenderer;
61010         }
61011         return this.config[col].renderer;
61012     },
61013
61014     /**
61015      * Sets the rendering (formatting) function for a column.
61016      * @param {Number} col The column index
61017      * @param {Function} fn The function to use to process the cell's raw data
61018      * to return HTML markup for the grid view. The render function is called with
61019      * the following parameters:<ul>
61020      * <li>Data value.</li>
61021      * <li>Cell metadata. An object in which you may set the following attributes:<ul>
61022      * <li>css A CSS style string to apply to the table cell.</li>
61023      * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
61024      * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
61025      * <li>Row index</li>
61026      * <li>Column index</li>
61027      * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
61028      */
61029     setRenderer : function(col, fn){
61030         this.config[col].renderer = fn;
61031     },
61032
61033     /**
61034      * Returns the width for the specified column.
61035      * @param {Number} col The column index
61036      * @param (optional) {String} gridSize bootstrap width size.
61037      * @return {Number}
61038      */
61039     getColumnWidth : function(col, gridSize)
61040         {
61041                 var cfg = this.config[col];
61042                 
61043                 if (typeof(gridSize) == 'undefined') {
61044                         return cfg.width * 1 || this.defaultWidth;
61045                 }
61046                 if (gridSize === false) { // if we set it..
61047                         return cfg.width || false;
61048                 }
61049                 var sizes = ['xl', 'lg', 'md', 'sm', 'xs'];
61050                 
61051                 for(var i = sizes.indexOf(gridSize); i < sizes.length; i++) {
61052                         if (typeof(cfg[ sizes[i] ] ) == 'undefined') {
61053                                 continue;
61054                         }
61055                         return cfg[ sizes[i] ];
61056                 }
61057                 return 1;
61058                 
61059     },
61060
61061     /**
61062      * Sets the width for a column.
61063      * @param {Number} col The column index
61064      * @param {Number} width The new width
61065      */
61066     setColumnWidth : function(col, width, suppressEvent){
61067         this.config[col].width = width;
61068         this.totalWidth = null;
61069         if(!suppressEvent){
61070              this.fireEvent("widthchange", this, col, width);
61071         }
61072     },
61073
61074     /**
61075      * Returns the total width of all columns.
61076      * @param {Boolean} includeHidden True to include hidden column widths
61077      * @return {Number}
61078      */
61079     getTotalWidth : function(includeHidden){
61080         if(!this.totalWidth){
61081             this.totalWidth = 0;
61082             for(var i = 0, len = this.config.length; i < len; i++){
61083                 if(includeHidden || !this.isHidden(i)){
61084                     this.totalWidth += this.getColumnWidth(i);
61085                 }
61086             }
61087         }
61088         return this.totalWidth;
61089     },
61090
61091     /**
61092      * Returns the header for the specified column.
61093      * @param {Number} col The column index
61094      * @return {String}
61095      */
61096     getColumnHeader : function(col){
61097         return this.config[col].header;
61098     },
61099
61100     /**
61101      * Sets the header for a column.
61102      * @param {Number} col The column index
61103      * @param {String} header The new header
61104      */
61105     setColumnHeader : function(col, header){
61106         this.config[col].header = header;
61107         this.fireEvent("headerchange", this, col, header);
61108     },
61109
61110     /**
61111      * Returns the tooltip for the specified column.
61112      * @param {Number} col The column index
61113      * @return {String}
61114      */
61115     getColumnTooltip : function(col){
61116             return this.config[col].tooltip;
61117     },
61118     /**
61119      * Sets the tooltip for a column.
61120      * @param {Number} col The column index
61121      * @param {String} tooltip The new tooltip
61122      */
61123     setColumnTooltip : function(col, tooltip){
61124             this.config[col].tooltip = tooltip;
61125     },
61126
61127     /**
61128      * Returns the dataIndex for the specified column.
61129      * @param {Number} col The column index
61130      * @return {Number}
61131      */
61132     getDataIndex : function(col){
61133         return this.config[col].dataIndex;
61134     },
61135
61136     /**
61137      * Sets the dataIndex for a column.
61138      * @param {Number} col The column index
61139      * @param {Number} dataIndex The new dataIndex
61140      */
61141     setDataIndex : function(col, dataIndex){
61142         this.config[col].dataIndex = dataIndex;
61143     },
61144
61145     
61146     
61147     /**
61148      * Returns true if the cell is editable.
61149      * @param {Number} colIndex The column index
61150      * @param {Number} rowIndex The row index - this is nto actually used..?
61151      * @return {Boolean}
61152      */
61153     isCellEditable : function(colIndex, rowIndex){
61154         return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
61155     },
61156
61157     /**
61158      * Returns the editor defined for the cell/column.
61159      * return false or null to disable editing.
61160      * @param {Number} colIndex The column index
61161      * @param {Number} rowIndex The row index
61162      * @return {Object}
61163      */
61164     getCellEditor : function(colIndex, rowIndex){
61165         return this.config[colIndex].editor;
61166     },
61167
61168     /**
61169      * Sets if a column is editable.
61170      * @param {Number} col The column index
61171      * @param {Boolean} editable True if the column is editable
61172      */
61173     setEditable : function(col, editable){
61174         this.config[col].editable = editable;
61175     },
61176
61177
61178     /**
61179      * Returns true if the column is hidden.
61180      * @param {Number} colIndex The column index
61181      * @return {Boolean}
61182      */
61183     isHidden : function(colIndex){
61184         return this.config[colIndex].hidden;
61185     },
61186
61187
61188     /**
61189      * Returns true if the column width cannot be changed
61190      */
61191     isFixed : function(colIndex){
61192         return this.config[colIndex].fixed;
61193     },
61194
61195     /**
61196      * Returns true if the column can be resized
61197      * @return {Boolean}
61198      */
61199     isResizable : function(colIndex){
61200         return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
61201     },
61202     /**
61203      * Sets if a column is hidden.
61204      * @param {Number} colIndex The column index
61205      * @param {Boolean} hidden True if the column is hidden
61206      */
61207     setHidden : function(colIndex, hidden){
61208         this.config[colIndex].hidden = hidden;
61209         this.totalWidth = null;
61210         this.fireEvent("hiddenchange", this, colIndex, hidden);
61211     },
61212
61213     /**
61214      * Sets the editor for a column.
61215      * @param {Number} col The column index
61216      * @param {Object} editor The editor object
61217      */
61218     setEditor : function(col, editor){
61219         this.config[col].editor = editor;
61220     },
61221     /**
61222      * Add a column (experimental...) - defaults to adding to the end..
61223      * @param {Object} config 
61224     */
61225     addColumn : function(c)
61226     {
61227     
61228         var i = this.config.length;
61229         this.config[i] = c;
61230         
61231         if(typeof c.dataIndex == "undefined"){
61232             c.dataIndex = i;
61233         }
61234         if(typeof c.renderer == "string"){
61235             c.renderer = Roo.util.Format[c.renderer];
61236         }
61237         if(typeof c.id == "undefined"){
61238             c.id = Roo.id();
61239         }
61240         if(c.editor && c.editor.xtype){
61241             c.editor  = Roo.factory(c.editor, Roo.grid);
61242         }
61243         if(c.editor && c.editor.isFormField){
61244             c.editor = new Roo.grid.GridEditor(c.editor);
61245         }
61246         this.lookup[c.id] = c;
61247     }
61248     
61249 });
61250
61251 Roo.grid.ColumnModel.defaultRenderer = function(value)
61252 {
61253     if(typeof value == "object") {
61254         return value;
61255     }
61256         if(typeof value == "string" && value.length < 1){
61257             return "&#160;";
61258         }
61259     
61260         return String.format("{0}", value);
61261 };
61262
61263 // Alias for backwards compatibility
61264 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
61265 /*
61266  * Based on:
61267  * Ext JS Library 1.1.1
61268  * Copyright(c) 2006-2007, Ext JS, LLC.
61269  *
61270  * Originally Released Under LGPL - original licence link has changed is not relivant.
61271  *
61272  * Fork - LGPL
61273  * <script type="text/javascript">
61274  */
61275
61276 /**
61277  * @class Roo.grid.AbstractSelectionModel
61278  * @extends Roo.util.Observable
61279  * @abstract
61280  * Abstract base class for grid SelectionModels.  It provides the interface that should be
61281  * implemented by descendant classes.  This class should not be directly instantiated.
61282  * @constructor
61283  */
61284 Roo.grid.AbstractSelectionModel = function(){
61285     this.locked = false;
61286     Roo.grid.AbstractSelectionModel.superclass.constructor.call(this);
61287 };
61288
61289 Roo.extend(Roo.grid.AbstractSelectionModel, Roo.util.Observable,  {
61290     /** @ignore Called by the grid automatically. Do not call directly. */
61291     init : function(grid){
61292         this.grid = grid;
61293         this.initEvents();
61294     },
61295
61296     /**
61297      * Locks the selections.
61298      */
61299     lock : function(){
61300         this.locked = true;
61301     },
61302
61303     /**
61304      * Unlocks the selections.
61305      */
61306     unlock : function(){
61307         this.locked = false;
61308     },
61309
61310     /**
61311      * Returns true if the selections are locked.
61312      * @return {Boolean}
61313      */
61314     isLocked : function(){
61315         return this.locked;
61316     }
61317 });/*
61318  * Based on:
61319  * Ext JS Library 1.1.1
61320  * Copyright(c) 2006-2007, Ext JS, LLC.
61321  *
61322  * Originally Released Under LGPL - original licence link has changed is not relivant.
61323  *
61324  * Fork - LGPL
61325  * <script type="text/javascript">
61326  */
61327 /**
61328  * @extends Roo.grid.AbstractSelectionModel
61329  * @class Roo.grid.RowSelectionModel
61330  * The default SelectionModel used by {@link Roo.grid.Grid}.
61331  * It supports multiple selections and keyboard selection/navigation. 
61332  * @constructor
61333  * @param {Object} config
61334  */
61335 Roo.grid.RowSelectionModel = function(config){
61336     Roo.apply(this, config);
61337     this.selections = new Roo.util.MixedCollection(false, function(o){
61338         return o.id;
61339     });
61340
61341     this.last = false;
61342     this.lastActive = false;
61343
61344     this.addEvents({
61345         /**
61346         * @event selectionchange
61347         * Fires when the selection changes
61348         * @param {SelectionModel} this
61349         */
61350        "selectionchange" : true,
61351        /**
61352         * @event afterselectionchange
61353         * Fires after the selection changes (eg. by key press or clicking)
61354         * @param {SelectionModel} this
61355         */
61356        "afterselectionchange" : true,
61357        /**
61358         * @event beforerowselect
61359         * Fires when a row is selected being selected, return false to cancel.
61360         * @param {SelectionModel} this
61361         * @param {Number} rowIndex The selected index
61362         * @param {Boolean} keepExisting False if other selections will be cleared
61363         */
61364        "beforerowselect" : true,
61365        /**
61366         * @event rowselect
61367         * Fires when a row is selected.
61368         * @param {SelectionModel} this
61369         * @param {Number} rowIndex The selected index
61370         * @param {Roo.data.Record} r The record
61371         */
61372        "rowselect" : true,
61373        /**
61374         * @event rowdeselect
61375         * Fires when a row is deselected.
61376         * @param {SelectionModel} this
61377         * @param {Number} rowIndex The selected index
61378         */
61379         "rowdeselect" : true
61380     });
61381     Roo.grid.RowSelectionModel.superclass.constructor.call(this);
61382     this.locked = false;
61383 };
61384
61385 Roo.extend(Roo.grid.RowSelectionModel, Roo.grid.AbstractSelectionModel,  {
61386     /**
61387      * @cfg {Boolean} singleSelect
61388      * True to allow selection of only one row at a time (defaults to false)
61389      */
61390     singleSelect : false,
61391
61392     // private
61393     initEvents : function(){
61394
61395         if(!this.grid.enableDragDrop && !this.grid.enableDrag){
61396             this.grid.on("mousedown", this.handleMouseDown, this);
61397         }else{ // allow click to work like normal
61398             this.grid.on("rowclick", this.handleDragableRowClick, this);
61399         }
61400         // bootstrap does not have a view..
61401         var view = this.grid.view ? this.grid.view : this.grid;
61402         this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
61403             "up" : function(e){
61404                 if(!e.shiftKey){
61405                     this.selectPrevious(e.shiftKey);
61406                 }else if(this.last !== false && this.lastActive !== false){
61407                     var last = this.last;
61408                     this.selectRange(this.last,  this.lastActive-1);
61409                     view.focusRow(this.lastActive);
61410                     if(last !== false){
61411                         this.last = last;
61412                     }
61413                 }else{
61414                     this.selectFirstRow();
61415                 }
61416                 this.fireEvent("afterselectionchange", this);
61417             },
61418             "down" : function(e){
61419                 if(!e.shiftKey){
61420                     this.selectNext(e.shiftKey);
61421                 }else if(this.last !== false && this.lastActive !== false){
61422                     var last = this.last;
61423                     this.selectRange(this.last,  this.lastActive+1);
61424                     view.focusRow(this.lastActive);
61425                     if(last !== false){
61426                         this.last = last;
61427                     }
61428                 }else{
61429                     this.selectFirstRow();
61430                 }
61431                 this.fireEvent("afterselectionchange", this);
61432             },
61433             scope: this
61434         });
61435
61436          
61437         view.on("refresh", this.onRefresh, this);
61438         view.on("rowupdated", this.onRowUpdated, this);
61439         view.on("rowremoved", this.onRemove, this);
61440     },
61441
61442     // private
61443     onRefresh : function(){
61444         var ds = this.grid.ds, i, v = this.grid.view;
61445         var s = this.selections;
61446         s.each(function(r){
61447             if((i = ds.indexOfId(r.id)) != -1){
61448                 v.onRowSelect(i);
61449                 s.add(ds.getAt(i)); // updating the selection relate data
61450             }else{
61451                 s.remove(r);
61452             }
61453         });
61454     },
61455
61456     // private
61457     onRemove : function(v, index, r){
61458         this.selections.remove(r);
61459     },
61460
61461     // private
61462     onRowUpdated : function(v, index, r){
61463         if(this.isSelected(r)){
61464             v.onRowSelect(index);
61465         }
61466     },
61467
61468     /**
61469      * Select records.
61470      * @param {Array} records The records to select
61471      * @param {Boolean} keepExisting (optional) True to keep existing selections
61472      */
61473     selectRecords : function(records, keepExisting){
61474         if(!keepExisting){
61475             this.clearSelections();
61476         }
61477         var ds = this.grid.ds;
61478         for(var i = 0, len = records.length; i < len; i++){
61479             this.selectRow(ds.indexOf(records[i]), true);
61480         }
61481     },
61482
61483     /**
61484      * Gets the number of selected rows.
61485      * @return {Number}
61486      */
61487     getCount : function(){
61488         return this.selections.length;
61489     },
61490
61491     /**
61492      * Selects the first row in the grid.
61493      */
61494     selectFirstRow : function(){
61495         this.selectRow(0);
61496     },
61497
61498     /**
61499      * Select the last row.
61500      * @param {Boolean} keepExisting (optional) True to keep existing selections
61501      */
61502     selectLastRow : function(keepExisting){
61503         this.selectRow(this.grid.ds.getCount() - 1, keepExisting);
61504     },
61505
61506     /**
61507      * Selects the row immediately following the last selected row.
61508      * @param {Boolean} keepExisting (optional) True to keep existing selections
61509      */
61510     selectNext : function(keepExisting){
61511         if(this.last !== false && (this.last+1) < this.grid.ds.getCount()){
61512             this.selectRow(this.last+1, keepExisting);
61513             var view = this.grid.view ? this.grid.view : this.grid;
61514             view.focusRow(this.last);
61515         }
61516     },
61517
61518     /**
61519      * Selects the row that precedes the last selected row.
61520      * @param {Boolean} keepExisting (optional) True to keep existing selections
61521      */
61522     selectPrevious : function(keepExisting){
61523         if(this.last){
61524             this.selectRow(this.last-1, keepExisting);
61525             var view = this.grid.view ? this.grid.view : this.grid;
61526             view.focusRow(this.last);
61527         }
61528     },
61529
61530     /**
61531      * Returns the selected records
61532      * @return {Array} Array of selected records
61533      */
61534     getSelections : function(){
61535         return [].concat(this.selections.items);
61536     },
61537
61538     /**
61539      * Returns the first selected record.
61540      * @return {Record}
61541      */
61542     getSelected : function(){
61543         return this.selections.itemAt(0);
61544     },
61545
61546
61547     /**
61548      * Clears all selections.
61549      */
61550     clearSelections : function(fast){
61551         if(this.locked) {
61552             return;
61553         }
61554         if(fast !== true){
61555             var ds = this.grid.ds;
61556             var s = this.selections;
61557             s.each(function(r){
61558                 this.deselectRow(ds.indexOfId(r.id));
61559             }, this);
61560             s.clear();
61561         }else{
61562             this.selections.clear();
61563         }
61564         this.last = false;
61565     },
61566
61567
61568     /**
61569      * Selects all rows.
61570      */
61571     selectAll : function(){
61572         if(this.locked) {
61573             return;
61574         }
61575         this.selections.clear();
61576         for(var i = 0, len = this.grid.ds.getCount(); i < len; i++){
61577             this.selectRow(i, true);
61578         }
61579     },
61580
61581     /**
61582      * Returns True if there is a selection.
61583      * @return {Boolean}
61584      */
61585     hasSelection : function(){
61586         return this.selections.length > 0;
61587     },
61588
61589     /**
61590      * Returns True if the specified row is selected.
61591      * @param {Number/Record} record The record or index of the record to check
61592      * @return {Boolean}
61593      */
61594     isSelected : function(index){
61595         var r = typeof index == "number" ? this.grid.ds.getAt(index) : index;
61596         return (r && this.selections.key(r.id) ? true : false);
61597     },
61598
61599     /**
61600      * Returns True if the specified record id is selected.
61601      * @param {String} id The id of record to check
61602      * @return {Boolean}
61603      */
61604     isIdSelected : function(id){
61605         return (this.selections.key(id) ? true : false);
61606     },
61607
61608     // private
61609     handleMouseDown : function(e, t)
61610     {
61611         var view = this.grid.view ? this.grid.view : this.grid;
61612         var rowIndex;
61613         if(this.isLocked() || (rowIndex = view.findRowIndex(t)) === false){
61614             return;
61615         };
61616         if(e.shiftKey && this.last !== false){
61617             var last = this.last;
61618             this.selectRange(last, rowIndex, e.ctrlKey);
61619             this.last = last; // reset the last
61620             view.focusRow(rowIndex);
61621         }else{
61622             var isSelected = this.isSelected(rowIndex);
61623             if(e.button !== 0 && isSelected){
61624                 view.focusRow(rowIndex);
61625             }else if(e.ctrlKey && isSelected){
61626                 this.deselectRow(rowIndex);
61627             }else if(!isSelected){
61628                 this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
61629                 view.focusRow(rowIndex);
61630             }
61631         }
61632         this.fireEvent("afterselectionchange", this);
61633     },
61634     // private
61635     handleDragableRowClick :  function(grid, rowIndex, e) 
61636     {
61637         if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
61638             this.selectRow(rowIndex, false);
61639             var view = this.grid.view ? this.grid.view : this.grid;
61640             view.focusRow(rowIndex);
61641              this.fireEvent("afterselectionchange", this);
61642         }
61643     },
61644     
61645     /**
61646      * Selects multiple rows.
61647      * @param {Array} rows Array of the indexes of the row to select
61648      * @param {Boolean} keepExisting (optional) True to keep existing selections
61649      */
61650     selectRows : function(rows, keepExisting){
61651         if(!keepExisting){
61652             this.clearSelections();
61653         }
61654         for(var i = 0, len = rows.length; i < len; i++){
61655             this.selectRow(rows[i], true);
61656         }
61657     },
61658
61659     /**
61660      * Selects a range of rows. All rows in between startRow and endRow are also selected.
61661      * @param {Number} startRow The index of the first row in the range
61662      * @param {Number} endRow The index of the last row in the range
61663      * @param {Boolean} keepExisting (optional) True to retain existing selections
61664      */
61665     selectRange : function(startRow, endRow, keepExisting){
61666         if(this.locked) {
61667             return;
61668         }
61669         if(!keepExisting){
61670             this.clearSelections();
61671         }
61672         if(startRow <= endRow){
61673             for(var i = startRow; i <= endRow; i++){
61674                 this.selectRow(i, true);
61675             }
61676         }else{
61677             for(var i = startRow; i >= endRow; i--){
61678                 this.selectRow(i, true);
61679             }
61680         }
61681     },
61682
61683     /**
61684      * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
61685      * @param {Number} startRow The index of the first row in the range
61686      * @param {Number} endRow The index of the last row in the range
61687      */
61688     deselectRange : function(startRow, endRow, preventViewNotify){
61689         if(this.locked) {
61690             return;
61691         }
61692         for(var i = startRow; i <= endRow; i++){
61693             this.deselectRow(i, preventViewNotify);
61694         }
61695     },
61696
61697     /**
61698      * Selects a row.
61699      * @param {Number} row The index of the row to select
61700      * @param {Boolean} keepExisting (optional) True to keep existing selections
61701      */
61702     selectRow : function(index, keepExisting, preventViewNotify){
61703         if(this.locked || (index < 0 || index >= this.grid.ds.getCount())) {
61704             return;
61705         }
61706         if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
61707             if(!keepExisting || this.singleSelect){
61708                 this.clearSelections();
61709             }
61710             var r = this.grid.ds.getAt(index);
61711             this.selections.add(r);
61712             this.last = this.lastActive = index;
61713             if(!preventViewNotify){
61714                 var view = this.grid.view ? this.grid.view : this.grid;
61715                 view.onRowSelect(index);
61716             }
61717             this.fireEvent("rowselect", this, index, r);
61718             this.fireEvent("selectionchange", this);
61719         }
61720     },
61721
61722     /**
61723      * Deselects a row.
61724      * @param {Number} row The index of the row to deselect
61725      */
61726     deselectRow : function(index, preventViewNotify){
61727         if(this.locked) {
61728             return;
61729         }
61730         if(this.last == index){
61731             this.last = false;
61732         }
61733         if(this.lastActive == index){
61734             this.lastActive = false;
61735         }
61736         var r = this.grid.ds.getAt(index);
61737         this.selections.remove(r);
61738         if(!preventViewNotify){
61739             var view = this.grid.view ? this.grid.view : this.grid;
61740             view.onRowDeselect(index);
61741         }
61742         this.fireEvent("rowdeselect", this, index);
61743         this.fireEvent("selectionchange", this);
61744     },
61745
61746     // private
61747     restoreLast : function(){
61748         if(this._last){
61749             this.last = this._last;
61750         }
61751     },
61752
61753     // private
61754     acceptsNav : function(row, col, cm){
61755         return !cm.isHidden(col) && cm.isCellEditable(col, row);
61756     },
61757
61758     // private
61759     onEditorKey : function(field, e){
61760         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
61761         if(k == e.TAB){
61762             e.stopEvent();
61763             ed.completeEdit();
61764             if(e.shiftKey){
61765                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
61766             }else{
61767                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
61768             }
61769         }else if(k == e.ENTER && !e.ctrlKey){
61770             e.stopEvent();
61771             ed.completeEdit();
61772             if(e.shiftKey){
61773                 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
61774             }else{
61775                 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
61776             }
61777         }else if(k == e.ESC){
61778             ed.cancelEdit();
61779         }
61780         if(newCell){
61781             g.startEditing(newCell[0], newCell[1]);
61782         }
61783     }
61784 });/*
61785  * Based on:
61786  * Ext JS Library 1.1.1
61787  * Copyright(c) 2006-2007, Ext JS, LLC.
61788  *
61789  * Originally Released Under LGPL - original licence link has changed is not relivant.
61790  *
61791  * Fork - LGPL
61792  * <script type="text/javascript">
61793  */
61794 /**
61795  * @class Roo.grid.CellSelectionModel
61796  * @extends Roo.grid.AbstractSelectionModel
61797  * This class provides the basic implementation for cell selection in a grid.
61798  * @constructor
61799  * @param {Object} config The object containing the configuration of this model.
61800  * @cfg {Boolean} enter_is_tab Enter behaves the same as tab. (eg. goes to next cell) default: false
61801  */
61802 Roo.grid.CellSelectionModel = function(config){
61803     Roo.apply(this, config);
61804
61805     this.selection = null;
61806
61807     this.addEvents({
61808         /**
61809              * @event beforerowselect
61810              * Fires before a cell is selected.
61811              * @param {SelectionModel} this
61812              * @param {Number} rowIndex The selected row index
61813              * @param {Number} colIndex The selected cell index
61814              */
61815             "beforecellselect" : true,
61816         /**
61817              * @event cellselect
61818              * Fires when a cell is selected.
61819              * @param {SelectionModel} this
61820              * @param {Number} rowIndex The selected row index
61821              * @param {Number} colIndex The selected cell index
61822              */
61823             "cellselect" : true,
61824         /**
61825              * @event selectionchange
61826              * Fires when the active selection changes.
61827              * @param {SelectionModel} this
61828              * @param {Object} selection null for no selection or an object (o) with two properties
61829                 <ul>
61830                 <li>o.record: the record object for the row the selection is in</li>
61831                 <li>o.cell: An array of [rowIndex, columnIndex]</li>
61832                 </ul>
61833              */
61834             "selectionchange" : true,
61835         /**
61836              * @event tabend
61837              * Fires when the tab (or enter) was pressed on the last editable cell
61838              * You can use this to trigger add new row.
61839              * @param {SelectionModel} this
61840              */
61841             "tabend" : true,
61842          /**
61843              * @event beforeeditnext
61844              * Fires before the next editable sell is made active
61845              * You can use this to skip to another cell or fire the tabend
61846              *    if you set cell to false
61847              * @param {Object} eventdata object : { cell : [ row, col ] } 
61848              */
61849             "beforeeditnext" : true
61850     });
61851     Roo.grid.CellSelectionModel.superclass.constructor.call(this);
61852 };
61853
61854 Roo.extend(Roo.grid.CellSelectionModel, Roo.grid.AbstractSelectionModel,  {
61855     
61856     enter_is_tab: false,
61857
61858     /** @ignore */
61859     initEvents : function(){
61860         this.grid.on("mousedown", this.handleMouseDown, this);
61861         this.grid.getGridEl().on(Roo.isIE ? "keydown" : "keypress", this.handleKeyDown, this);
61862         var view = this.grid.view;
61863         view.on("refresh", this.onViewChange, this);
61864         view.on("rowupdated", this.onRowUpdated, this);
61865         view.on("beforerowremoved", this.clearSelections, this);
61866         view.on("beforerowsinserted", this.clearSelections, this);
61867         if(this.grid.isEditor){
61868             this.grid.on("beforeedit", this.beforeEdit,  this);
61869         }
61870     },
61871
61872         //private
61873     beforeEdit : function(e){
61874         this.select(e.row, e.column, false, true, e.record);
61875     },
61876
61877         //private
61878     onRowUpdated : function(v, index, r){
61879         if(this.selection && this.selection.record == r){
61880             v.onCellSelect(index, this.selection.cell[1]);
61881         }
61882     },
61883
61884         //private
61885     onViewChange : function(){
61886         this.clearSelections(true);
61887     },
61888
61889         /**
61890          * Returns the currently selected cell,.
61891          * @return {Array} The selected cell (row, column) or null if none selected.
61892          */
61893     getSelectedCell : function(){
61894         return this.selection ? this.selection.cell : null;
61895     },
61896
61897     /**
61898      * Clears all selections.
61899      * @param {Boolean} true to prevent the gridview from being notified about the change.
61900      */
61901     clearSelections : function(preventNotify){
61902         var s = this.selection;
61903         if(s){
61904             if(preventNotify !== true){
61905                 this.grid.view.onCellDeselect(s.cell[0], s.cell[1]);
61906             }
61907             this.selection = null;
61908             this.fireEvent("selectionchange", this, null);
61909         }
61910     },
61911
61912     /**
61913      * Returns true if there is a selection.
61914      * @return {Boolean}
61915      */
61916     hasSelection : function(){
61917         return this.selection ? true : false;
61918     },
61919
61920     /** @ignore */
61921     handleMouseDown : function(e, t){
61922         var v = this.grid.getView();
61923         if(this.isLocked()){
61924             return;
61925         };
61926         var row = v.findRowIndex(t);
61927         var cell = v.findCellIndex(t);
61928         if(row !== false && cell !== false){
61929             this.select(row, cell);
61930         }
61931     },
61932
61933     /**
61934      * Selects a cell.
61935      * @param {Number} rowIndex
61936      * @param {Number} collIndex
61937      */
61938     select : function(rowIndex, colIndex, preventViewNotify, preventFocus, /*internal*/ r){
61939         if(this.fireEvent("beforecellselect", this, rowIndex, colIndex) !== false){
61940             this.clearSelections();
61941             r = r || this.grid.dataSource.getAt(rowIndex);
61942             this.selection = {
61943                 record : r,
61944                 cell : [rowIndex, colIndex]
61945             };
61946             if(!preventViewNotify){
61947                 var v = this.grid.getView();
61948                 v.onCellSelect(rowIndex, colIndex);
61949                 if(preventFocus !== true){
61950                     v.focusCell(rowIndex, colIndex);
61951                 }
61952             }
61953             this.fireEvent("cellselect", this, rowIndex, colIndex);
61954             this.fireEvent("selectionchange", this, this.selection);
61955         }
61956     },
61957
61958         //private
61959     isSelectable : function(rowIndex, colIndex, cm){
61960         return !cm.isHidden(colIndex);
61961     },
61962
61963     /** @ignore */
61964     handleKeyDown : function(e){
61965         //Roo.log('Cell Sel Model handleKeyDown');
61966         if(!e.isNavKeyPress()){
61967             return;
61968         }
61969         var g = this.grid, s = this.selection;
61970         if(!s){
61971             e.stopEvent();
61972             var cell = g.walkCells(0, 0, 1, this.isSelectable,  this);
61973             if(cell){
61974                 this.select(cell[0], cell[1]);
61975             }
61976             return;
61977         }
61978         var sm = this;
61979         var walk = function(row, col, step){
61980             return g.walkCells(row, col, step, sm.isSelectable,  sm);
61981         };
61982         var k = e.getKey(), r = s.cell[0], c = s.cell[1];
61983         var newCell;
61984
61985       
61986
61987         switch(k){
61988             case e.TAB:
61989                 // handled by onEditorKey
61990                 if (g.isEditor && g.editing) {
61991                     return;
61992                 }
61993                 if(e.shiftKey) {
61994                     newCell = walk(r, c-1, -1);
61995                 } else {
61996                     newCell = walk(r, c+1, 1);
61997                 }
61998                 break;
61999             
62000             case e.DOWN:
62001                newCell = walk(r+1, c, 1);
62002                 break;
62003             
62004             case e.UP:
62005                 newCell = walk(r-1, c, -1);
62006                 break;
62007             
62008             case e.RIGHT:
62009                 newCell = walk(r, c+1, 1);
62010                 break;
62011             
62012             case e.LEFT:
62013                 newCell = walk(r, c-1, -1);
62014                 break;
62015             
62016             case e.ENTER:
62017                 
62018                 if(g.isEditor && !g.editing){
62019                    g.startEditing(r, c);
62020                    e.stopEvent();
62021                    return;
62022                 }
62023                 
62024                 
62025              break;
62026         };
62027         if(newCell){
62028             this.select(newCell[0], newCell[1]);
62029             e.stopEvent();
62030             
62031         }
62032     },
62033
62034     acceptsNav : function(row, col, cm){
62035         return !cm.isHidden(col) && cm.isCellEditable(col, row);
62036     },
62037     /**
62038      * Selects a cell.
62039      * @param {Number} field (not used) - as it's normally used as a listener
62040      * @param {Number} e - event - fake it by using
62041      *
62042      * var e = Roo.EventObjectImpl.prototype;
62043      * e.keyCode = e.TAB
62044      *
62045      * 
62046      */
62047     onEditorKey : function(field, e){
62048         
62049         var k = e.getKey(),
62050             newCell,
62051             g = this.grid,
62052             ed = g.activeEditor,
62053             forward = false;
62054         ///Roo.log('onEditorKey' + k);
62055         
62056         
62057         if (this.enter_is_tab && k == e.ENTER) {
62058             k = e.TAB;
62059         }
62060         
62061         if(k == e.TAB){
62062             if(e.shiftKey){
62063                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
62064             }else{
62065                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
62066                 forward = true;
62067             }
62068             
62069             e.stopEvent();
62070             
62071         } else if(k == e.ENTER &&  !e.ctrlKey){
62072             ed.completeEdit();
62073             e.stopEvent();
62074             newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
62075         
62076                 } else if(k == e.ESC){
62077             ed.cancelEdit();
62078         }
62079                 
62080         if (newCell) {
62081             var ecall = { cell : newCell, forward : forward };
62082             this.fireEvent('beforeeditnext', ecall );
62083             newCell = ecall.cell;
62084                         forward = ecall.forward;
62085         }
62086                 
62087         if(newCell){
62088             //Roo.log('next cell after edit');
62089             g.startEditing.defer(100, g, [newCell[0], newCell[1]]);
62090         } else if (forward) {
62091             // tabbed past last
62092             this.fireEvent.defer(100, this, ['tabend',this]);
62093         }
62094     }
62095 });/*
62096  * Based on:
62097  * Ext JS Library 1.1.1
62098  * Copyright(c) 2006-2007, Ext JS, LLC.
62099  *
62100  * Originally Released Under LGPL - original licence link has changed is not relivant.
62101  *
62102  * Fork - LGPL
62103  * <script type="text/javascript">
62104  */
62105  
62106 /**
62107  * @class Roo.grid.EditorGrid
62108  * @extends Roo.grid.Grid
62109  * Class for creating and editable grid.
62110  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered - 
62111  * The container MUST have some type of size defined for the grid to fill. The container will be 
62112  * automatically set to position relative if it isn't already.
62113  * @param {Object} dataSource The data model to bind to
62114  * @param {Object} colModel The column model with info about this grid's columns
62115  */
62116 Roo.grid.EditorGrid = function(container, config){
62117     Roo.grid.EditorGrid.superclass.constructor.call(this, container, config);
62118     this.getGridEl().addClass("xedit-grid");
62119
62120     if(!this.selModel){
62121         this.selModel = new Roo.grid.CellSelectionModel();
62122     }
62123
62124     this.activeEditor = null;
62125
62126         this.addEvents({
62127             /**
62128              * @event beforeedit
62129              * Fires before cell editing is triggered. The edit event object has the following properties <br />
62130              * <ul style="padding:5px;padding-left:16px;">
62131              * <li>grid - This grid</li>
62132              * <li>record - The record being edited</li>
62133              * <li>field - The field name being edited</li>
62134              * <li>value - The value for the field being edited.</li>
62135              * <li>row - The grid row index</li>
62136              * <li>column - The grid column index</li>
62137              * <li>cancel - Set this to true to cancel the edit or return false from your handler.</li>
62138              * </ul>
62139              * @param {Object} e An edit event (see above for description)
62140              */
62141             "beforeedit" : true,
62142             /**
62143              * @event afteredit
62144              * Fires after a cell is edited. <br />
62145              * <ul style="padding:5px;padding-left:16px;">
62146              * <li>grid - This grid</li>
62147              * <li>record - The record being edited</li>
62148              * <li>field - The field name being edited</li>
62149              * <li>value - The value being set</li>
62150              * <li>originalValue - The original value for the field, before the edit.</li>
62151              * <li>row - The grid row index</li>
62152              * <li>column - The grid column index</li>
62153              * </ul>
62154              * @param {Object} e An edit event (see above for description)
62155              */
62156             "afteredit" : true,
62157             /**
62158              * @event validateedit
62159              * Fires after a cell is edited, but before the value is set in the record. 
62160          * You can use this to modify the value being set in the field, Return false
62161              * to cancel the change. The edit event object has the following properties <br />
62162              * <ul style="padding:5px;padding-left:16px;">
62163          * <li>editor - This editor</li>
62164              * <li>grid - This grid</li>
62165              * <li>record - The record being edited</li>
62166              * <li>field - The field name being edited</li>
62167              * <li>value - The value being set</li>
62168              * <li>originalValue - The original value for the field, before the edit.</li>
62169              * <li>row - The grid row index</li>
62170              * <li>column - The grid column index</li>
62171              * <li>cancel - Set this to true to cancel the edit or return false from your handler.</li>
62172              * </ul>
62173              * @param {Object} e An edit event (see above for description)
62174              */
62175             "validateedit" : true
62176         });
62177     this.on("bodyscroll", this.stopEditing,  this);
62178     this.on(this.clicksToEdit == 1 ? "cellclick" : "celldblclick", this.onCellDblClick,  this);
62179 };
62180
62181 Roo.extend(Roo.grid.EditorGrid, Roo.grid.Grid, {
62182     /**
62183      * @cfg {Number} clicksToEdit
62184      * The number of clicks on a cell required to display the cell's editor (defaults to 2)
62185      */
62186     clicksToEdit: 2,
62187
62188     // private
62189     isEditor : true,
62190     // private
62191     trackMouseOver: false, // causes very odd FF errors
62192
62193     onCellDblClick : function(g, row, col){
62194         this.startEditing(row, col);
62195     },
62196
62197     onEditComplete : function(ed, value, startValue){
62198         this.editing = false;
62199         this.activeEditor = null;
62200         ed.un("specialkey", this.selModel.onEditorKey, this.selModel);
62201         var r = ed.record;
62202         var field = this.colModel.getDataIndex(ed.col);
62203         var e = {
62204             grid: this,
62205             record: r,
62206             field: field,
62207             originalValue: startValue,
62208             value: value,
62209             row: ed.row,
62210             column: ed.col,
62211             cancel:false,
62212             editor: ed
62213         };
62214         var cell = Roo.get(this.view.getCell(ed.row,ed.col));
62215         cell.show();
62216           
62217         if(String(value) !== String(startValue)){
62218             
62219             if(this.fireEvent("validateedit", e) !== false && !e.cancel){
62220                 r.set(field, e.value);
62221                 // if we are dealing with a combo box..
62222                 // then we also set the 'name' colum to be the displayField
62223                 if (ed.field.displayField && ed.field.name) {
62224                     r.set(ed.field.name, ed.field.el.dom.value);
62225                 }
62226                 
62227                 delete e.cancel; //?? why!!!
62228                 this.fireEvent("afteredit", e);
62229             }
62230         } else {
62231             this.fireEvent("afteredit", e); // always fire it!
62232         }
62233         this.view.focusCell(ed.row, ed.col);
62234     },
62235
62236     /**
62237      * Starts editing the specified for the specified row/column
62238      * @param {Number} rowIndex
62239      * @param {Number} colIndex
62240      */
62241     startEditing : function(row, col){
62242         this.stopEditing();
62243         if(this.colModel.isCellEditable(col, row)){
62244             this.view.ensureVisible(row, col, true);
62245           
62246             var r = this.dataSource.getAt(row);
62247             var field = this.colModel.getDataIndex(col);
62248             var cell = Roo.get(this.view.getCell(row,col));
62249             var e = {
62250                 grid: this,
62251                 record: r,
62252                 field: field,
62253                 value: r.data[field],
62254                 row: row,
62255                 column: col,
62256                 cancel:false 
62257             };
62258             if(this.fireEvent("beforeedit", e) !== false && !e.cancel){
62259                 this.editing = true;
62260                 var ed = this.colModel.getCellEditor(col, row);
62261                 
62262                 if (!ed) {
62263                     return;
62264                 }
62265                 if(!ed.rendered){
62266                     ed.render(ed.parentEl || document.body);
62267                 }
62268                 ed.field.reset();
62269                
62270                 cell.hide();
62271                 
62272                 (function(){ // complex but required for focus issues in safari, ie and opera
62273                     ed.row = row;
62274                     ed.col = col;
62275                     ed.record = r;
62276                     ed.on("complete",   this.onEditComplete,        this,       {single: true});
62277                     ed.on("specialkey", this.selModel.onEditorKey,  this.selModel);
62278                     this.activeEditor = ed;
62279                     var v = r.data[field];
62280                     ed.startEdit(this.view.getCell(row, col), v);
62281                     // combo's with 'displayField and name set
62282                     if (ed.field.displayField && ed.field.name) {
62283                         ed.field.el.dom.value = r.data[ed.field.name];
62284                     }
62285                     
62286                     
62287                 }).defer(50, this);
62288             }
62289         }
62290     },
62291         
62292     /**
62293      * Stops any active editing
62294      */
62295     stopEditing : function(){
62296         if(this.activeEditor){
62297             this.activeEditor.completeEdit();
62298         }
62299         this.activeEditor = null;
62300     },
62301         
62302          /**
62303      * Called to get grid's drag proxy text, by default returns this.ddText.
62304      * @return {String}
62305      */
62306     getDragDropText : function(){
62307         var count = this.selModel.getSelectedCell() ? 1 : 0;
62308         return String.format(this.ddText, count, count == 1 ? '' : 's');
62309     }
62310         
62311 });/*
62312  * Based on:
62313  * Ext JS Library 1.1.1
62314  * Copyright(c) 2006-2007, Ext JS, LLC.
62315  *
62316  * Originally Released Under LGPL - original licence link has changed is not relivant.
62317  *
62318  * Fork - LGPL
62319  * <script type="text/javascript">
62320  */
62321
62322 // private - not really -- you end up using it !
62323 // This is a support class used internally by the Grid components
62324
62325 /**
62326  * @class Roo.grid.GridEditor
62327  * @extends Roo.Editor
62328  * Class for creating and editable grid elements.
62329  * @param {Object} config any settings (must include field)
62330  */
62331 Roo.grid.GridEditor = function(field, config){
62332     if (!config && field.field) {
62333         config = field;
62334         field = Roo.factory(config.field, Roo.form);
62335     }
62336     Roo.grid.GridEditor.superclass.constructor.call(this, field, config);
62337     field.monitorTab = false;
62338 };
62339
62340 Roo.extend(Roo.grid.GridEditor, Roo.Editor, {
62341     
62342     /**
62343      * @cfg {Roo.form.Field} field Field to wrap (or xtyped)
62344      */
62345     
62346     alignment: "tl-tl",
62347     autoSize: "width",
62348     hideEl : false,
62349     cls: "x-small-editor x-grid-editor",
62350     shim:false,
62351     shadow:"frame"
62352 });/*
62353  * Based on:
62354  * Ext JS Library 1.1.1
62355  * Copyright(c) 2006-2007, Ext JS, LLC.
62356  *
62357  * Originally Released Under LGPL - original licence link has changed is not relivant.
62358  *
62359  * Fork - LGPL
62360  * <script type="text/javascript">
62361  */
62362   
62363
62364   
62365 Roo.grid.PropertyRecord = Roo.data.Record.create([
62366     {name:'name',type:'string'},  'value'
62367 ]);
62368
62369
62370 Roo.grid.PropertyStore = function(grid, source){
62371     this.grid = grid;
62372     this.store = new Roo.data.Store({
62373         recordType : Roo.grid.PropertyRecord
62374     });
62375     this.store.on('update', this.onUpdate,  this);
62376     if(source){
62377         this.setSource(source);
62378     }
62379     Roo.grid.PropertyStore.superclass.constructor.call(this);
62380 };
62381
62382
62383
62384 Roo.extend(Roo.grid.PropertyStore, Roo.util.Observable, {
62385     setSource : function(o){
62386         this.source = o;
62387         this.store.removeAll();
62388         var data = [];
62389         for(var k in o){
62390             if(this.isEditableValue(o[k])){
62391                 data.push(new Roo.grid.PropertyRecord({name: k, value: o[k]}, k));
62392             }
62393         }
62394         this.store.loadRecords({records: data}, {}, true);
62395     },
62396
62397     onUpdate : function(ds, record, type){
62398         if(type == Roo.data.Record.EDIT){
62399             var v = record.data['value'];
62400             var oldValue = record.modified['value'];
62401             if(this.grid.fireEvent('beforepropertychange', this.source, record.id, v, oldValue) !== false){
62402                 this.source[record.id] = v;
62403                 record.commit();
62404                 this.grid.fireEvent('propertychange', this.source, record.id, v, oldValue);
62405             }else{
62406                 record.reject();
62407             }
62408         }
62409     },
62410
62411     getProperty : function(row){
62412        return this.store.getAt(row);
62413     },
62414
62415     isEditableValue: function(val){
62416         if(val && val instanceof Date){
62417             return true;
62418         }else if(typeof val == 'object' || typeof val == 'function'){
62419             return false;
62420         }
62421         return true;
62422     },
62423
62424     setValue : function(prop, value){
62425         this.source[prop] = value;
62426         this.store.getById(prop).set('value', value);
62427     },
62428
62429     getSource : function(){
62430         return this.source;
62431     }
62432 });
62433
62434 Roo.grid.PropertyColumnModel = function(grid, store){
62435     this.grid = grid;
62436     var g = Roo.grid;
62437     g.PropertyColumnModel.superclass.constructor.call(this, [
62438         {header: this.nameText, sortable: true, dataIndex:'name', id: 'name'},
62439         {header: this.valueText, resizable:false, dataIndex: 'value', id: 'value'}
62440     ]);
62441     this.store = store;
62442     this.bselect = Roo.DomHelper.append(document.body, {
62443         tag: 'select', style:'display:none', cls: 'x-grid-editor', children: [
62444             {tag: 'option', value: 'true', html: 'true'},
62445             {tag: 'option', value: 'false', html: 'false'}
62446         ]
62447     });
62448     Roo.id(this.bselect);
62449     var f = Roo.form;
62450     this.editors = {
62451         'date' : new g.GridEditor(new f.DateField({selectOnFocus:true})),
62452         'string' : new g.GridEditor(new f.TextField({selectOnFocus:true})),
62453         'number' : new g.GridEditor(new f.NumberField({selectOnFocus:true, style:'text-align:left;'})),
62454         'int' : new g.GridEditor(new f.NumberField({selectOnFocus:true, allowDecimals:false, style:'text-align:left;'})),
62455         'boolean' : new g.GridEditor(new f.Field({el:this.bselect,selectOnFocus:true}))
62456     };
62457     this.renderCellDelegate = this.renderCell.createDelegate(this);
62458     this.renderPropDelegate = this.renderProp.createDelegate(this);
62459 };
62460
62461 Roo.extend(Roo.grid.PropertyColumnModel, Roo.grid.ColumnModel, {
62462     
62463     
62464     nameText : 'Name',
62465     valueText : 'Value',
62466     
62467     dateFormat : 'm/j/Y',
62468     
62469     
62470     renderDate : function(dateVal){
62471         return dateVal.dateFormat(this.dateFormat);
62472     },
62473
62474     renderBool : function(bVal){
62475         return bVal ? 'true' : 'false';
62476     },
62477
62478     isCellEditable : function(colIndex, rowIndex){
62479         return colIndex == 1;
62480     },
62481
62482     getRenderer : function(col){
62483         return col == 1 ?
62484             this.renderCellDelegate : this.renderPropDelegate;
62485     },
62486
62487     renderProp : function(v){
62488         return this.getPropertyName(v);
62489     },
62490
62491     renderCell : function(val){
62492         var rv = val;
62493         if(val instanceof Date){
62494             rv = this.renderDate(val);
62495         }else if(typeof val == 'boolean'){
62496             rv = this.renderBool(val);
62497         }
62498         return Roo.util.Format.htmlEncode(rv);
62499     },
62500
62501     getPropertyName : function(name){
62502         var pn = this.grid.propertyNames;
62503         return pn && pn[name] ? pn[name] : name;
62504     },
62505
62506     getCellEditor : function(colIndex, rowIndex){
62507         var p = this.store.getProperty(rowIndex);
62508         var n = p.data['name'], val = p.data['value'];
62509         
62510         if(typeof(this.grid.customEditors[n]) == 'string'){
62511             return this.editors[this.grid.customEditors[n]];
62512         }
62513         if(typeof(this.grid.customEditors[n]) != 'undefined'){
62514             return this.grid.customEditors[n];
62515         }
62516         if(val instanceof Date){
62517             return this.editors['date'];
62518         }else if(typeof val == 'number'){
62519             return this.editors['number'];
62520         }else if(typeof val == 'boolean'){
62521             return this.editors['boolean'];
62522         }else{
62523             return this.editors['string'];
62524         }
62525     }
62526 });
62527
62528 /**
62529  * @class Roo.grid.PropertyGrid
62530  * @extends Roo.grid.EditorGrid
62531  * This class represents the  interface of a component based property grid control.
62532  * <br><br>Usage:<pre><code>
62533  var grid = new Roo.grid.PropertyGrid("my-container-id", {
62534       
62535  });
62536  // set any options
62537  grid.render();
62538  * </code></pre>
62539   
62540  * @constructor
62541  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
62542  * The container MUST have some type of size defined for the grid to fill. The container will be
62543  * automatically set to position relative if it isn't already.
62544  * @param {Object} config A config object that sets properties on this grid.
62545  */
62546 Roo.grid.PropertyGrid = function(container, config){
62547     config = config || {};
62548     var store = new Roo.grid.PropertyStore(this);
62549     this.store = store;
62550     var cm = new Roo.grid.PropertyColumnModel(this, store);
62551     store.store.sort('name', 'ASC');
62552     Roo.grid.PropertyGrid.superclass.constructor.call(this, container, Roo.apply({
62553         ds: store.store,
62554         cm: cm,
62555         enableColLock:false,
62556         enableColumnMove:false,
62557         stripeRows:false,
62558         trackMouseOver: false,
62559         clicksToEdit:1
62560     }, config));
62561     this.getGridEl().addClass('x-props-grid');
62562     this.lastEditRow = null;
62563     this.on('columnresize', this.onColumnResize, this);
62564     this.addEvents({
62565          /**
62566              * @event beforepropertychange
62567              * Fires before a property changes (return false to stop?)
62568              * @param {Roo.grid.PropertyGrid} grid property grid? (check could be store)
62569              * @param {String} id Record Id
62570              * @param {String} newval New Value
62571          * @param {String} oldval Old Value
62572              */
62573         "beforepropertychange": true,
62574         /**
62575              * @event propertychange
62576              * Fires after a property changes
62577              * @param {Roo.grid.PropertyGrid} grid property grid? (check could be store)
62578              * @param {String} id Record Id
62579              * @param {String} newval New Value
62580          * @param {String} oldval Old Value
62581              */
62582         "propertychange": true
62583     });
62584     this.customEditors = this.customEditors || {};
62585 };
62586 Roo.extend(Roo.grid.PropertyGrid, Roo.grid.EditorGrid, {
62587     
62588      /**
62589      * @cfg {Object} customEditors map of colnames=> custom editors.
62590      * the custom editor can be one of the standard ones (date|string|number|int|boolean), or a
62591      * grid editor eg. Roo.grid.GridEditor(new Roo.form.TextArea({selectOnFocus:true})),
62592      * false disables editing of the field.
62593          */
62594     
62595       /**
62596      * @cfg {Object} propertyNames map of property Names to their displayed value
62597          */
62598     
62599     render : function(){
62600         Roo.grid.PropertyGrid.superclass.render.call(this);
62601         this.autoSize.defer(100, this);
62602     },
62603
62604     autoSize : function(){
62605         Roo.grid.PropertyGrid.superclass.autoSize.call(this);
62606         if(this.view){
62607             this.view.fitColumns();
62608         }
62609     },
62610
62611     onColumnResize : function(){
62612         this.colModel.setColumnWidth(1, this.container.getWidth(true)-this.colModel.getColumnWidth(0));
62613         this.autoSize();
62614     },
62615     /**
62616      * Sets the data for the Grid
62617      * accepts a Key => Value object of all the elements avaiable.
62618      * @param {Object} data  to appear in grid.
62619      */
62620     setSource : function(source){
62621         this.store.setSource(source);
62622         //this.autoSize();
62623     },
62624     /**
62625      * Gets all the data from the grid.
62626      * @return {Object} data  data stored in grid
62627      */
62628     getSource : function(){
62629         return this.store.getSource();
62630     }
62631 });/*
62632   
62633  * Licence LGPL
62634  
62635  */
62636  
62637 /**
62638  * @class Roo.grid.Calendar
62639  * @extends Roo.grid.Grid
62640  * This class extends the Grid to provide a calendar widget
62641  * <br><br>Usage:<pre><code>
62642  var grid = new Roo.grid.Calendar("my-container-id", {
62643      ds: myDataStore,
62644      cm: myColModel,
62645      selModel: mySelectionModel,
62646      autoSizeColumns: true,
62647      monitorWindowResize: false,
62648      trackMouseOver: true
62649      eventstore : real data store..
62650  });
62651  // set any options
62652  grid.render();
62653   
62654   * @constructor
62655  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
62656  * The container MUST have some type of size defined for the grid to fill. The container will be
62657  * automatically set to position relative if it isn't already.
62658  * @param {Object} config A config object that sets properties on this grid.
62659  */
62660 Roo.grid.Calendar = function(container, config){
62661         // initialize the container
62662         this.container = Roo.get(container);
62663         this.container.update("");
62664         this.container.setStyle("overflow", "hidden");
62665     this.container.addClass('x-grid-container');
62666
62667     this.id = this.container.id;
62668
62669     Roo.apply(this, config);
62670     // check and correct shorthanded configs
62671     
62672     var rows = [];
62673     var d =1;
62674     for (var r = 0;r < 6;r++) {
62675         
62676         rows[r]=[];
62677         for (var c =0;c < 7;c++) {
62678             rows[r][c]= '';
62679         }
62680     }
62681     if (this.eventStore) {
62682         this.eventStore= Roo.factory(this.eventStore, Roo.data);
62683         this.eventStore.on('load',this.onLoad, this);
62684         this.eventStore.on('beforeload',this.clearEvents, this);
62685          
62686     }
62687     
62688     this.dataSource = new Roo.data.Store({
62689             proxy: new Roo.data.MemoryProxy(rows),
62690             reader: new Roo.data.ArrayReader({}, [
62691                    'weekday0', 'weekday1', 'weekday2', 'weekday3', 'weekday4', 'weekday5', 'weekday6' ])
62692     });
62693
62694     this.dataSource.load();
62695     this.ds = this.dataSource;
62696     this.ds.xmodule = this.xmodule || false;
62697     
62698     
62699     var cellRender = function(v,x,r)
62700     {
62701         return String.format(
62702             '<div class="fc-day  fc-widget-content"><div>' +
62703                 '<div class="fc-event-container"></div>' +
62704                 '<div class="fc-day-number">{0}</div>'+
62705                 
62706                 '<div class="fc-day-content"><div style="position:relative"></div></div>' +
62707             '</div></div>', v);
62708     
62709     }
62710     
62711     
62712     this.colModel = new Roo.grid.ColumnModel( [
62713         {
62714             xtype: 'ColumnModel',
62715             xns: Roo.grid,
62716             dataIndex : 'weekday0',
62717             header : 'Sunday',
62718             renderer : cellRender
62719         },
62720         {
62721             xtype: 'ColumnModel',
62722             xns: Roo.grid,
62723             dataIndex : 'weekday1',
62724             header : 'Monday',
62725             renderer : cellRender
62726         },
62727         {
62728             xtype: 'ColumnModel',
62729             xns: Roo.grid,
62730             dataIndex : 'weekday2',
62731             header : 'Tuesday',
62732             renderer : cellRender
62733         },
62734         {
62735             xtype: 'ColumnModel',
62736             xns: Roo.grid,
62737             dataIndex : 'weekday3',
62738             header : 'Wednesday',
62739             renderer : cellRender
62740         },
62741         {
62742             xtype: 'ColumnModel',
62743             xns: Roo.grid,
62744             dataIndex : 'weekday4',
62745             header : 'Thursday',
62746             renderer : cellRender
62747         },
62748         {
62749             xtype: 'ColumnModel',
62750             xns: Roo.grid,
62751             dataIndex : 'weekday5',
62752             header : 'Friday',
62753             renderer : cellRender
62754         },
62755         {
62756             xtype: 'ColumnModel',
62757             xns: Roo.grid,
62758             dataIndex : 'weekday6',
62759             header : 'Saturday',
62760             renderer : cellRender
62761         }
62762     ]);
62763     this.cm = this.colModel;
62764     this.cm.xmodule = this.xmodule || false;
62765  
62766         
62767           
62768     //this.selModel = new Roo.grid.CellSelectionModel();
62769     //this.sm = this.selModel;
62770     //this.selModel.init(this);
62771     
62772     
62773     if(this.width){
62774         this.container.setWidth(this.width);
62775     }
62776
62777     if(this.height){
62778         this.container.setHeight(this.height);
62779     }
62780     /** @private */
62781         this.addEvents({
62782         // raw events
62783         /**
62784          * @event click
62785          * The raw click event for the entire grid.
62786          * @param {Roo.EventObject} e
62787          */
62788         "click" : true,
62789         /**
62790          * @event dblclick
62791          * The raw dblclick event for the entire grid.
62792          * @param {Roo.EventObject} e
62793          */
62794         "dblclick" : true,
62795         /**
62796          * @event contextmenu
62797          * The raw contextmenu event for the entire grid.
62798          * @param {Roo.EventObject} e
62799          */
62800         "contextmenu" : true,
62801         /**
62802          * @event mousedown
62803          * The raw mousedown event for the entire grid.
62804          * @param {Roo.EventObject} e
62805          */
62806         "mousedown" : true,
62807         /**
62808          * @event mouseup
62809          * The raw mouseup event for the entire grid.
62810          * @param {Roo.EventObject} e
62811          */
62812         "mouseup" : true,
62813         /**
62814          * @event mouseover
62815          * The raw mouseover event for the entire grid.
62816          * @param {Roo.EventObject} e
62817          */
62818         "mouseover" : true,
62819         /**
62820          * @event mouseout
62821          * The raw mouseout event for the entire grid.
62822          * @param {Roo.EventObject} e
62823          */
62824         "mouseout" : true,
62825         /**
62826          * @event keypress
62827          * The raw keypress event for the entire grid.
62828          * @param {Roo.EventObject} e
62829          */
62830         "keypress" : true,
62831         /**
62832          * @event keydown
62833          * The raw keydown event for the entire grid.
62834          * @param {Roo.EventObject} e
62835          */
62836         "keydown" : true,
62837
62838         // custom events
62839
62840         /**
62841          * @event cellclick
62842          * Fires when a cell is clicked
62843          * @param {Grid} this
62844          * @param {Number} rowIndex
62845          * @param {Number} columnIndex
62846          * @param {Roo.EventObject} e
62847          */
62848         "cellclick" : true,
62849         /**
62850          * @event celldblclick
62851          * Fires when a cell is double clicked
62852          * @param {Grid} this
62853          * @param {Number} rowIndex
62854          * @param {Number} columnIndex
62855          * @param {Roo.EventObject} e
62856          */
62857         "celldblclick" : true,
62858         /**
62859          * @event rowclick
62860          * Fires when a row is clicked
62861          * @param {Grid} this
62862          * @param {Number} rowIndex
62863          * @param {Roo.EventObject} e
62864          */
62865         "rowclick" : true,
62866         /**
62867          * @event rowdblclick
62868          * Fires when a row is double clicked
62869          * @param {Grid} this
62870          * @param {Number} rowIndex
62871          * @param {Roo.EventObject} e
62872          */
62873         "rowdblclick" : true,
62874         /**
62875          * @event headerclick
62876          * Fires when a header is clicked
62877          * @param {Grid} this
62878          * @param {Number} columnIndex
62879          * @param {Roo.EventObject} e
62880          */
62881         "headerclick" : true,
62882         /**
62883          * @event headerdblclick
62884          * Fires when a header cell is double clicked
62885          * @param {Grid} this
62886          * @param {Number} columnIndex
62887          * @param {Roo.EventObject} e
62888          */
62889         "headerdblclick" : true,
62890         /**
62891          * @event rowcontextmenu
62892          * Fires when a row is right clicked
62893          * @param {Grid} this
62894          * @param {Number} rowIndex
62895          * @param {Roo.EventObject} e
62896          */
62897         "rowcontextmenu" : true,
62898         /**
62899          * @event cellcontextmenu
62900          * Fires when a cell is right clicked
62901          * @param {Grid} this
62902          * @param {Number} rowIndex
62903          * @param {Number} cellIndex
62904          * @param {Roo.EventObject} e
62905          */
62906          "cellcontextmenu" : true,
62907         /**
62908          * @event headercontextmenu
62909          * Fires when a header is right clicked
62910          * @param {Grid} this
62911          * @param {Number} columnIndex
62912          * @param {Roo.EventObject} e
62913          */
62914         "headercontextmenu" : true,
62915         /**
62916          * @event bodyscroll
62917          * Fires when the body element is scrolled
62918          * @param {Number} scrollLeft
62919          * @param {Number} scrollTop
62920          */
62921         "bodyscroll" : true,
62922         /**
62923          * @event columnresize
62924          * Fires when the user resizes a column
62925          * @param {Number} columnIndex
62926          * @param {Number} newSize
62927          */
62928         "columnresize" : true,
62929         /**
62930          * @event columnmove
62931          * Fires when the user moves a column
62932          * @param {Number} oldIndex
62933          * @param {Number} newIndex
62934          */
62935         "columnmove" : true,
62936         /**
62937          * @event startdrag
62938          * Fires when row(s) start being dragged
62939          * @param {Grid} this
62940          * @param {Roo.GridDD} dd The drag drop object
62941          * @param {event} e The raw browser event
62942          */
62943         "startdrag" : true,
62944         /**
62945          * @event enddrag
62946          * Fires when a drag operation is complete
62947          * @param {Grid} this
62948          * @param {Roo.GridDD} dd The drag drop object
62949          * @param {event} e The raw browser event
62950          */
62951         "enddrag" : true,
62952         /**
62953          * @event dragdrop
62954          * Fires when dragged row(s) are dropped on a valid DD target
62955          * @param {Grid} this
62956          * @param {Roo.GridDD} dd The drag drop object
62957          * @param {String} targetId The target drag drop object
62958          * @param {event} e The raw browser event
62959          */
62960         "dragdrop" : true,
62961         /**
62962          * @event dragover
62963          * Fires while row(s) are being dragged. "targetId" is the id of the Yahoo.util.DD object the selected rows are being dragged over.
62964          * @param {Grid} this
62965          * @param {Roo.GridDD} dd The drag drop object
62966          * @param {String} targetId The target drag drop object
62967          * @param {event} e The raw browser event
62968          */
62969         "dragover" : true,
62970         /**
62971          * @event dragenter
62972          *  Fires when the dragged row(s) first cross another DD target while being dragged
62973          * @param {Grid} this
62974          * @param {Roo.GridDD} dd The drag drop object
62975          * @param {String} targetId The target drag drop object
62976          * @param {event} e The raw browser event
62977          */
62978         "dragenter" : true,
62979         /**
62980          * @event dragout
62981          * Fires when the dragged row(s) leave another DD target while being dragged
62982          * @param {Grid} this
62983          * @param {Roo.GridDD} dd The drag drop object
62984          * @param {String} targetId The target drag drop object
62985          * @param {event} e The raw browser event
62986          */
62987         "dragout" : true,
62988         /**
62989          * @event rowclass
62990          * Fires when a row is rendered, so you can change add a style to it.
62991          * @param {GridView} gridview   The grid view
62992          * @param {Object} rowcfg   contains record  rowIndex and rowClass - set rowClass to add a style.
62993          */
62994         'rowclass' : true,
62995
62996         /**
62997          * @event render
62998          * Fires when the grid is rendered
62999          * @param {Grid} grid
63000          */
63001         'render' : true,
63002             /**
63003              * @event select
63004              * Fires when a date is selected
63005              * @param {DatePicker} this
63006              * @param {Date} date The selected date
63007              */
63008         'select': true,
63009         /**
63010              * @event monthchange
63011              * Fires when the displayed month changes 
63012              * @param {DatePicker} this
63013              * @param {Date} date The selected month
63014              */
63015         'monthchange': true,
63016         /**
63017              * @event evententer
63018              * Fires when mouse over an event
63019              * @param {Calendar} this
63020              * @param {event} Event
63021              */
63022         'evententer': true,
63023         /**
63024              * @event eventleave
63025              * Fires when the mouse leaves an
63026              * @param {Calendar} this
63027              * @param {event}
63028              */
63029         'eventleave': true,
63030         /**
63031              * @event eventclick
63032              * Fires when the mouse click an
63033              * @param {Calendar} this
63034              * @param {event}
63035              */
63036         'eventclick': true,
63037         /**
63038              * @event eventrender
63039              * Fires before each cell is rendered, so you can modify the contents, like cls / title / qtip
63040              * @param {Calendar} this
63041              * @param {data} data to be modified
63042              */
63043         'eventrender': true
63044         
63045     });
63046
63047     Roo.grid.Grid.superclass.constructor.call(this);
63048     this.on('render', function() {
63049         this.view.el.addClass('x-grid-cal'); 
63050         
63051         (function() { this.setDate(new Date()); }).defer(100,this); //default today..
63052
63053     },this);
63054     
63055     if (!Roo.grid.Calendar.style) {
63056         Roo.grid.Calendar.style = Roo.util.CSS.createStyleSheet({
63057             
63058             
63059             '.x-grid-cal .x-grid-col' :  {
63060                 height: 'auto !important',
63061                 'vertical-align': 'top'
63062             },
63063             '.x-grid-cal  .fc-event-hori' : {
63064                 height: '14px'
63065             }
63066              
63067             
63068         }, Roo.id());
63069     }
63070
63071     
63072     
63073 };
63074 Roo.extend(Roo.grid.Calendar, Roo.grid.Grid, {
63075     /**
63076      * @cfg {Store} eventStore The store that loads events.
63077      */
63078     eventStore : 25,
63079
63080      
63081     activeDate : false,
63082     startDay : 0,
63083     autoWidth : true,
63084     monitorWindowResize : false,
63085
63086     
63087     resizeColumns : function() {
63088         var col = (this.view.el.getWidth() / 7) - 3;
63089         // loop through cols, and setWidth
63090         for(var i =0 ; i < 7 ; i++){
63091             this.cm.setColumnWidth(i, col);
63092         }
63093     },
63094      setDate :function(date) {
63095         
63096         Roo.log('setDate?');
63097         
63098         this.resizeColumns();
63099         var vd = this.activeDate;
63100         this.activeDate = date;
63101 //        if(vd && this.el){
63102 //            var t = date.getTime();
63103 //            if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
63104 //                Roo.log('using add remove');
63105 //                
63106 //                this.fireEvent('monthchange', this, date);
63107 //                
63108 //                this.cells.removeClass("fc-state-highlight");
63109 //                this.cells.each(function(c){
63110 //                   if(c.dateValue == t){
63111 //                       c.addClass("fc-state-highlight");
63112 //                       setTimeout(function(){
63113 //                            try{c.dom.firstChild.focus();}catch(e){}
63114 //                       }, 50);
63115 //                       return false;
63116 //                   }
63117 //                   return true;
63118 //                });
63119 //                return;
63120 //            }
63121 //        }
63122         
63123         var days = date.getDaysInMonth();
63124         
63125         var firstOfMonth = date.getFirstDateOfMonth();
63126         var startingPos = firstOfMonth.getDay()-this.startDay;
63127         
63128         if(startingPos < this.startDay){
63129             startingPos += 7;
63130         }
63131         
63132         var pm = date.add(Date.MONTH, -1);
63133         var prevStart = pm.getDaysInMonth()-startingPos;
63134 //        
63135         
63136         
63137         this.cells = this.view.el.select('.x-grid-row .x-grid-col',true);
63138         
63139         this.textNodes = this.view.el.query('.x-grid-row .x-grid-col .x-grid-cell-text');
63140         //this.cells.addClassOnOver('fc-state-hover');
63141         
63142         var cells = this.cells.elements;
63143         var textEls = this.textNodes;
63144         
63145         //Roo.each(cells, function(cell){
63146         //    cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
63147         //});
63148         
63149         days += startingPos;
63150
63151         // convert everything to numbers so it's fast
63152         var day = 86400000;
63153         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
63154         //Roo.log(d);
63155         //Roo.log(pm);
63156         //Roo.log(prevStart);
63157         
63158         var today = new Date().clearTime().getTime();
63159         var sel = date.clearTime().getTime();
63160         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
63161         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
63162         var ddMatch = this.disabledDatesRE;
63163         var ddText = this.disabledDatesText;
63164         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
63165         var ddaysText = this.disabledDaysText;
63166         var format = this.format;
63167         
63168         var setCellClass = function(cal, cell){
63169             
63170             //Roo.log('set Cell Class');
63171             cell.title = "";
63172             var t = d.getTime();
63173             
63174             //Roo.log(d);
63175             
63176             
63177             cell.dateValue = t;
63178             if(t == today){
63179                 cell.className += " fc-today";
63180                 cell.className += " fc-state-highlight";
63181                 cell.title = cal.todayText;
63182             }
63183             if(t == sel){
63184                 // disable highlight in other month..
63185                 cell.className += " fc-state-highlight";
63186                 
63187             }
63188             // disabling
63189             if(t < min) {
63190                 //cell.className = " fc-state-disabled";
63191                 cell.title = cal.minText;
63192                 return;
63193             }
63194             if(t > max) {
63195                 //cell.className = " fc-state-disabled";
63196                 cell.title = cal.maxText;
63197                 return;
63198             }
63199             if(ddays){
63200                 if(ddays.indexOf(d.getDay()) != -1){
63201                     // cell.title = ddaysText;
63202                    // cell.className = " fc-state-disabled";
63203                 }
63204             }
63205             if(ddMatch && format){
63206                 var fvalue = d.dateFormat(format);
63207                 if(ddMatch.test(fvalue)){
63208                     cell.title = ddText.replace("%0", fvalue);
63209                    cell.className = " fc-state-disabled";
63210                 }
63211             }
63212             
63213             if (!cell.initialClassName) {
63214                 cell.initialClassName = cell.dom.className;
63215             }
63216             
63217             cell.dom.className = cell.initialClassName  + ' ' +  cell.className;
63218         };
63219
63220         var i = 0;
63221         
63222         for(; i < startingPos; i++) {
63223             cells[i].dayName =  (++prevStart);
63224             Roo.log(textEls[i]);
63225             d.setDate(d.getDate()+1);
63226             
63227             //cells[i].className = "fc-past fc-other-month";
63228             setCellClass(this, cells[i]);
63229         }
63230         
63231         var intDay = 0;
63232         
63233         for(; i < days; i++){
63234             intDay = i - startingPos + 1;
63235             cells[i].dayName =  (intDay);
63236             d.setDate(d.getDate()+1);
63237             
63238             cells[i].className = ''; // "x-date-active";
63239             setCellClass(this, cells[i]);
63240         }
63241         var extraDays = 0;
63242         
63243         for(; i < 42; i++) {
63244             //textEls[i].innerHTML = (++extraDays);
63245             
63246             d.setDate(d.getDate()+1);
63247             cells[i].dayName = (++extraDays);
63248             cells[i].className = "fc-future fc-other-month";
63249             setCellClass(this, cells[i]);
63250         }
63251         
63252         //this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
63253         
63254         var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
63255         
63256         // this will cause all the cells to mis
63257         var rows= [];
63258         var i =0;
63259         for (var r = 0;r < 6;r++) {
63260             for (var c =0;c < 7;c++) {
63261                 this.ds.getAt(r).set('weekday' + c ,cells[i++].dayName );
63262             }    
63263         }
63264         
63265         this.cells = this.view.el.select('.x-grid-row .x-grid-col',true);
63266         for(i=0;i<cells.length;i++) {
63267             
63268             this.cells.elements[i].dayName = cells[i].dayName ;
63269             this.cells.elements[i].className = cells[i].className;
63270             this.cells.elements[i].initialClassName = cells[i].initialClassName ;
63271             this.cells.elements[i].title = cells[i].title ;
63272             this.cells.elements[i].dateValue = cells[i].dateValue ;
63273         }
63274         
63275         
63276         
63277         
63278         //this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
63279         //this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
63280         
63281         ////if(totalRows != 6){
63282             //this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
63283            // this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
63284        // }
63285         
63286         this.fireEvent('monthchange', this, date);
63287         
63288         
63289     },
63290  /**
63291      * Returns the grid's SelectionModel.
63292      * @return {SelectionModel}
63293      */
63294     getSelectionModel : function(){
63295         if(!this.selModel){
63296             this.selModel = new Roo.grid.CellSelectionModel();
63297         }
63298         return this.selModel;
63299     },
63300
63301     load: function() {
63302         this.eventStore.load()
63303         
63304         
63305         
63306     },
63307     
63308     findCell : function(dt) {
63309         dt = dt.clearTime().getTime();
63310         var ret = false;
63311         this.cells.each(function(c){
63312             //Roo.log("check " +c.dateValue + '?=' + dt);
63313             if(c.dateValue == dt){
63314                 ret = c;
63315                 return false;
63316             }
63317             return true;
63318         });
63319         
63320         return ret;
63321     },
63322     
63323     findCells : function(rec) {
63324         var s = rec.data.start_dt.clone().clearTime().getTime();
63325        // Roo.log(s);
63326         var e= rec.data.end_dt.clone().clearTime().getTime();
63327        // Roo.log(e);
63328         var ret = [];
63329         this.cells.each(function(c){
63330              ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
63331             
63332             if(c.dateValue > e){
63333                 return ;
63334             }
63335             if(c.dateValue < s){
63336                 return ;
63337             }
63338             ret.push(c);
63339         });
63340         
63341         return ret;    
63342     },
63343     
63344     findBestRow: function(cells)
63345     {
63346         var ret = 0;
63347         
63348         for (var i =0 ; i < cells.length;i++) {
63349             ret  = Math.max(cells[i].rows || 0,ret);
63350         }
63351         return ret;
63352         
63353     },
63354     
63355     
63356     addItem : function(rec)
63357     {
63358         // look for vertical location slot in
63359         var cells = this.findCells(rec);
63360         
63361         rec.row = this.findBestRow(cells);
63362         
63363         // work out the location.
63364         
63365         var crow = false;
63366         var rows = [];
63367         for(var i =0; i < cells.length; i++) {
63368             if (!crow) {
63369                 crow = {
63370                     start : cells[i],
63371                     end :  cells[i]
63372                 };
63373                 continue;
63374             }
63375             if (crow.start.getY() == cells[i].getY()) {
63376                 // on same row.
63377                 crow.end = cells[i];
63378                 continue;
63379             }
63380             // different row.
63381             rows.push(crow);
63382             crow = {
63383                 start: cells[i],
63384                 end : cells[i]
63385             };
63386             
63387         }
63388         
63389         rows.push(crow);
63390         rec.els = [];
63391         rec.rows = rows;
63392         rec.cells = cells;
63393         for (var i = 0; i < cells.length;i++) {
63394             cells[i].rows = Math.max(cells[i].rows || 0 , rec.row + 1 );
63395             
63396         }
63397         
63398         
63399     },
63400     
63401     clearEvents: function() {
63402         
63403         if (!this.eventStore.getCount()) {
63404             return;
63405         }
63406         // reset number of rows in cells.
63407         Roo.each(this.cells.elements, function(c){
63408             c.rows = 0;
63409         });
63410         
63411         this.eventStore.each(function(e) {
63412             this.clearEvent(e);
63413         },this);
63414         
63415     },
63416     
63417     clearEvent : function(ev)
63418     {
63419         if (ev.els) {
63420             Roo.each(ev.els, function(el) {
63421                 el.un('mouseenter' ,this.onEventEnter, this);
63422                 el.un('mouseleave' ,this.onEventLeave, this);
63423                 el.remove();
63424             },this);
63425             ev.els = [];
63426         }
63427     },
63428     
63429     
63430     renderEvent : function(ev,ctr) {
63431         if (!ctr) {
63432              ctr = this.view.el.select('.fc-event-container',true).first();
63433         }
63434         
63435          
63436         this.clearEvent(ev);
63437             //code
63438        
63439         
63440         
63441         ev.els = [];
63442         var cells = ev.cells;
63443         var rows = ev.rows;
63444         this.fireEvent('eventrender', this, ev);
63445         
63446         for(var i =0; i < rows.length; i++) {
63447             
63448             cls = '';
63449             if (i == 0) {
63450                 cls += ' fc-event-start';
63451             }
63452             if ((i+1) == rows.length) {
63453                 cls += ' fc-event-end';
63454             }
63455             
63456             //Roo.log(ev.data);
63457             // how many rows should it span..
63458             var cg = this.eventTmpl.append(ctr,Roo.apply({
63459                 fccls : cls
63460                 
63461             }, ev.data) , true);
63462             
63463             
63464             cg.on('mouseenter' ,this.onEventEnter, this, ev);
63465             cg.on('mouseleave' ,this.onEventLeave, this, ev);
63466             cg.on('click', this.onEventClick, this, ev);
63467             
63468             ev.els.push(cg);
63469             
63470             var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
63471             var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
63472             //Roo.log(cg);
63473              
63474             cg.setXY([sbox.x +2, sbox.y +(ev.row * 20)]);    
63475             cg.setWidth(ebox.right - sbox.x -2);
63476         }
63477     },
63478     
63479     renderEvents: function()
63480     {   
63481         // first make sure there is enough space..
63482         
63483         if (!this.eventTmpl) {
63484             this.eventTmpl = new Roo.Template(
63485                 '<div class="roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable {fccls} {cls}"  style="position: absolute" unselectable="on">' +
63486                     '<div class="fc-event-inner">' +
63487                         '<span class="fc-event-time">{time}</span>' +
63488                         '<span class="fc-event-title" qtip="{qtip}">{title}</span>' +
63489                     '</div>' +
63490                     '<div class="ui-resizable-heandle ui-resizable-e">&nbsp;&nbsp;&nbsp;</div>' +
63491                 '</div>'
63492             );
63493                 
63494         }
63495                
63496         
63497         
63498         this.cells.each(function(c) {
63499             //Roo.log(c.select('.fc-day-content div',true).first());
63500             c.select('.fc-day-content div',true).first().setHeight(Math.max(34, (c.rows || 1) * 20));
63501         });
63502         
63503         var ctr = this.view.el.select('.fc-event-container',true).first();
63504         
63505         var cls;
63506         this.eventStore.each(function(ev){
63507             
63508             this.renderEvent(ev);
63509              
63510              
63511         }, this);
63512         this.view.layout();
63513         
63514     },
63515     
63516     onEventEnter: function (e, el,event,d) {
63517         this.fireEvent('evententer', this, el, event);
63518     },
63519     
63520     onEventLeave: function (e, el,event,d) {
63521         this.fireEvent('eventleave', this, el, event);
63522     },
63523     
63524     onEventClick: function (e, el,event,d) {
63525         this.fireEvent('eventclick', this, el, event);
63526     },
63527     
63528     onMonthChange: function () {
63529         this.store.load();
63530     },
63531     
63532     onLoad: function () {
63533         
63534         //Roo.log('calendar onload');
63535 //         
63536         if(this.eventStore.getCount() > 0){
63537             
63538            
63539             
63540             this.eventStore.each(function(d){
63541                 
63542                 
63543                 // FIXME..
63544                 var add =   d.data;
63545                 if (typeof(add.end_dt) == 'undefined')  {
63546                     Roo.log("Missing End time in calendar data: ");
63547                     Roo.log(d);
63548                     return;
63549                 }
63550                 if (typeof(add.start_dt) == 'undefined')  {
63551                     Roo.log("Missing Start time in calendar data: ");
63552                     Roo.log(d);
63553                     return;
63554                 }
63555                 add.start_dt = typeof(add.start_dt) == 'string' ? Date.parseDate(add.start_dt,'Y-m-d H:i:s') : add.start_dt,
63556                 add.end_dt = typeof(add.end_dt) == 'string' ? Date.parseDate(add.end_dt,'Y-m-d H:i:s') : add.end_dt,
63557                 add.id = add.id || d.id;
63558                 add.title = add.title || '??';
63559                 
63560                 this.addItem(d);
63561                 
63562              
63563             },this);
63564         }
63565         
63566         this.renderEvents();
63567     }
63568     
63569
63570 });
63571 /*
63572  grid : {
63573                 xtype: 'Grid',
63574                 xns: Roo.grid,
63575                 listeners : {
63576                     render : function ()
63577                     {
63578                         _this.grid = this;
63579                         
63580                         if (!this.view.el.hasClass('course-timesheet')) {
63581                             this.view.el.addClass('course-timesheet');
63582                         }
63583                         if (this.tsStyle) {
63584                             this.ds.load({});
63585                             return; 
63586                         }
63587                         Roo.log('width');
63588                         Roo.log(_this.grid.view.el.getWidth());
63589                         
63590                         
63591                         this.tsStyle =  Roo.util.CSS.createStyleSheet({
63592                             '.course-timesheet .x-grid-row' : {
63593                                 height: '80px'
63594                             },
63595                             '.x-grid-row td' : {
63596                                 'vertical-align' : 0
63597                             },
63598                             '.course-edit-link' : {
63599                                 'color' : 'blue',
63600                                 'text-overflow' : 'ellipsis',
63601                                 'overflow' : 'hidden',
63602                                 'white-space' : 'nowrap',
63603                                 'cursor' : 'pointer'
63604                             },
63605                             '.sub-link' : {
63606                                 'color' : 'green'
63607                             },
63608                             '.de-act-sup-link' : {
63609                                 'color' : 'purple',
63610                                 'text-decoration' : 'line-through'
63611                             },
63612                             '.de-act-link' : {
63613                                 'color' : 'red',
63614                                 'text-decoration' : 'line-through'
63615                             },
63616                             '.course-timesheet .course-highlight' : {
63617                                 'border-top-style': 'dashed !important',
63618                                 'border-bottom-bottom': 'dashed !important'
63619                             },
63620                             '.course-timesheet .course-item' : {
63621                                 'font-family'   : 'tahoma, arial, helvetica',
63622                                 'font-size'     : '11px',
63623                                 'overflow'      : 'hidden',
63624                                 'padding-left'  : '10px',
63625                                 'padding-right' : '10px',
63626                                 'padding-top' : '10px' 
63627                             }
63628                             
63629                         }, Roo.id());
63630                                 this.ds.load({});
63631                     }
63632                 },
63633                 autoWidth : true,
63634                 monitorWindowResize : false,
63635                 cellrenderer : function(v,x,r)
63636                 {
63637                     return v;
63638                 },
63639                 sm : {
63640                     xtype: 'CellSelectionModel',
63641                     xns: Roo.grid
63642                 },
63643                 dataSource : {
63644                     xtype: 'Store',
63645                     xns: Roo.data,
63646                     listeners : {
63647                         beforeload : function (_self, options)
63648                         {
63649                             options.params = options.params || {};
63650                             options.params._month = _this.monthField.getValue();
63651                             options.params.limit = 9999;
63652                             options.params['sort'] = 'when_dt';    
63653                             options.params['dir'] = 'ASC';    
63654                             this.proxy.loadResponse = this.loadResponse;
63655                             Roo.log("load?");
63656                             //this.addColumns();
63657                         },
63658                         load : function (_self, records, options)
63659                         {
63660                             _this.grid.view.el.select('.course-edit-link', true).on('click', function() {
63661                                 // if you click on the translation.. you can edit it...
63662                                 var el = Roo.get(this);
63663                                 var id = el.dom.getAttribute('data-id');
63664                                 var d = el.dom.getAttribute('data-date');
63665                                 var t = el.dom.getAttribute('data-time');
63666                                 //var id = this.child('span').dom.textContent;
63667                                 
63668                                 //Roo.log(this);
63669                                 Pman.Dialog.CourseCalendar.show({
63670                                     id : id,
63671                                     when_d : d,
63672                                     when_t : t,
63673                                     productitem_active : id ? 1 : 0
63674                                 }, function() {
63675                                     _this.grid.ds.load({});
63676                                 });
63677                            
63678                            });
63679                            
63680                            _this.panel.fireEvent('resize', [ '', '' ]);
63681                         }
63682                     },
63683                     loadResponse : function(o, success, response){
63684                             // this is overridden on before load..
63685                             
63686                             Roo.log("our code?");       
63687                             //Roo.log(success);
63688                             //Roo.log(response)
63689                             delete this.activeRequest;
63690                             if(!success){
63691                                 this.fireEvent("loadexception", this, o, response);
63692                                 o.request.callback.call(o.request.scope, null, o.request.arg, false);
63693                                 return;
63694                             }
63695                             var result;
63696                             try {
63697                                 result = o.reader.read(response);
63698                             }catch(e){
63699                                 Roo.log("load exception?");
63700                                 this.fireEvent("loadexception", this, o, response, e);
63701                                 o.request.callback.call(o.request.scope, null, o.request.arg, false);
63702                                 return;
63703                             }
63704                             Roo.log("ready...");        
63705                             // loop through result.records;
63706                             // and set this.tdate[date] = [] << array of records..
63707                             _this.tdata  = {};
63708                             Roo.each(result.records, function(r){
63709                                 //Roo.log(r.data);
63710                                 if(typeof(_this.tdata[r.data.when_dt.format('j')]) == 'undefined'){
63711                                     _this.tdata[r.data.when_dt.format('j')] = [];
63712                                 }
63713                                 _this.tdata[r.data.when_dt.format('j')].push(r.data);
63714                             });
63715                             
63716                             //Roo.log(_this.tdata);
63717                             
63718                             result.records = [];
63719                             result.totalRecords = 6;
63720                     
63721                             // let's generate some duumy records for the rows.
63722                             //var st = _this.dateField.getValue();
63723                             
63724                             // work out monday..
63725                             //st = st.add(Date.DAY, -1 * st.format('w'));
63726                             
63727                             var date = Date.parseDate(_this.monthField.getValue(), "Y-m-d");
63728                             
63729                             var firstOfMonth = date.getFirstDayOfMonth();
63730                             var days = date.getDaysInMonth();
63731                             var d = 1;
63732                             var firstAdded = false;
63733                             for (var i = 0; i < result.totalRecords ; i++) {
63734                                 //var d= st.add(Date.DAY, i);
63735                                 var row = {};
63736                                 var added = 0;
63737                                 for(var w = 0 ; w < 7 ; w++){
63738                                     if(!firstAdded && firstOfMonth != w){
63739                                         continue;
63740                                     }
63741                                     if(d > days){
63742                                         continue;
63743                                     }
63744                                     firstAdded = true;
63745                                     var dd = (d > 0 && d < 10) ? "0"+d : d;
63746                                     row['weekday'+w] = String.format(
63747                                                     '<span style="font-size: 16px;"><b>{0}</b></span>'+
63748                                                     '<span class="course-edit-link" style="color:blue;" data-id="0" data-date="{1}"> Add New</span>',
63749                                                     d,
63750                                                     date.format('Y-m-')+dd
63751                                                 );
63752                                     added++;
63753                                     if(typeof(_this.tdata[d]) != 'undefined'){
63754                                         Roo.each(_this.tdata[d], function(r){
63755                                             var is_sub = '';
63756                                             var deactive = '';
63757                                             var id = r.id;
63758                                             var desc = (r.productitem_id_descrip) ? r.productitem_id_descrip : '';
63759                                             if(r.parent_id*1>0){
63760                                                 is_sub = (r.productitem_id_visible*1 < 1) ? 'de-act-sup-link' :'sub-link';
63761                                                 id = r.parent_id;
63762                                             }
63763                                             if(r.productitem_id_visible*1 < 1 && r.parent_id*1 < 1){
63764                                                 deactive = 'de-act-link';
63765                                             }
63766                                             
63767                                             row['weekday'+w] += String.format(
63768                                                     '<br /><span class="course-edit-link {3} {4}" qtip="{5}" data-id="{0}">{2} - {1}</span>',
63769                                                     id, //0
63770                                                     r.product_id_name, //1
63771                                                     r.when_dt.format('h:ia'), //2
63772                                                     is_sub, //3
63773                                                     deactive, //4
63774                                                     desc // 5
63775                                             );
63776                                         });
63777                                     }
63778                                     d++;
63779                                 }
63780                                 
63781                                 // only do this if something added..
63782                                 if(added > 0){ 
63783                                     result.records.push(_this.grid.dataSource.reader.newRow(row));
63784                                 }
63785                                 
63786                                 
63787                                 // push it twice. (second one with an hour..
63788                                 
63789                             }
63790                             //Roo.log(result);
63791                             this.fireEvent("load", this, o, o.request.arg);
63792                             o.request.callback.call(o.request.scope, result, o.request.arg, true);
63793                         },
63794                     sortInfo : {field: 'when_dt', direction : 'ASC' },
63795                     proxy : {
63796                         xtype: 'HttpProxy',
63797                         xns: Roo.data,
63798                         method : 'GET',
63799                         url : baseURL + '/Roo/Shop_course.php'
63800                     },
63801                     reader : {
63802                         xtype: 'JsonReader',
63803                         xns: Roo.data,
63804                         id : 'id',
63805                         fields : [
63806                             {
63807                                 'name': 'id',
63808                                 'type': 'int'
63809                             },
63810                             {
63811                                 'name': 'when_dt',
63812                                 'type': 'string'
63813                             },
63814                             {
63815                                 'name': 'end_dt',
63816                                 'type': 'string'
63817                             },
63818                             {
63819                                 'name': 'parent_id',
63820                                 'type': 'int'
63821                             },
63822                             {
63823                                 'name': 'product_id',
63824                                 'type': 'int'
63825                             },
63826                             {
63827                                 'name': 'productitem_id',
63828                                 'type': 'int'
63829                             },
63830                             {
63831                                 'name': 'guid',
63832                                 'type': 'int'
63833                             }
63834                         ]
63835                     }
63836                 },
63837                 toolbar : {
63838                     xtype: 'Toolbar',
63839                     xns: Roo,
63840                     items : [
63841                         {
63842                             xtype: 'Button',
63843                             xns: Roo.Toolbar,
63844                             listeners : {
63845                                 click : function (_self, e)
63846                                 {
63847                                     var sd = Date.parseDate(_this.monthField.getValue(), "Y-m-d");
63848                                     sd.setMonth(sd.getMonth()-1);
63849                                     _this.monthField.setValue(sd.format('Y-m-d'));
63850                                     _this.grid.ds.load({});
63851                                 }
63852                             },
63853                             text : "Back"
63854                         },
63855                         {
63856                             xtype: 'Separator',
63857                             xns: Roo.Toolbar
63858                         },
63859                         {
63860                             xtype: 'MonthField',
63861                             xns: Roo.form,
63862                             listeners : {
63863                                 render : function (_self)
63864                                 {
63865                                     _this.monthField = _self;
63866                                    // _this.monthField.set  today
63867                                 },
63868                                 select : function (combo, date)
63869                                 {
63870                                     _this.grid.ds.load({});
63871                                 }
63872                             },
63873                             value : (function() { return new Date(); })()
63874                         },
63875                         {
63876                             xtype: 'Separator',
63877                             xns: Roo.Toolbar
63878                         },
63879                         {
63880                             xtype: 'TextItem',
63881                             xns: Roo.Toolbar,
63882                             text : "Blue: in-active, green: in-active sup-event, red: de-active, purple: de-active sup-event"
63883                         },
63884                         {
63885                             xtype: 'Fill',
63886                             xns: Roo.Toolbar
63887                         },
63888                         {
63889                             xtype: 'Button',
63890                             xns: Roo.Toolbar,
63891                             listeners : {
63892                                 click : function (_self, e)
63893                                 {
63894                                     var sd = Date.parseDate(_this.monthField.getValue(), "Y-m-d");
63895                                     sd.setMonth(sd.getMonth()+1);
63896                                     _this.monthField.setValue(sd.format('Y-m-d'));
63897                                     _this.grid.ds.load({});
63898                                 }
63899                             },
63900                             text : "Next"
63901                         }
63902                     ]
63903                 },
63904                  
63905             }
63906         };
63907         
63908         *//*
63909  * Based on:
63910  * Ext JS Library 1.1.1
63911  * Copyright(c) 2006-2007, Ext JS, LLC.
63912  *
63913  * Originally Released Under LGPL - original licence link has changed is not relivant.
63914  *
63915  * Fork - LGPL
63916  * <script type="text/javascript">
63917  */
63918  
63919 /**
63920  * @class Roo.LoadMask
63921  * A simple utility class for generically masking elements while loading data.  If the element being masked has
63922  * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
63923  * process and the mask element will be cached for reuse.  For all other elements, this mask will replace the
63924  * element's UpdateManager load indicator and will be destroyed after the initial load.
63925  * @constructor
63926  * Create a new LoadMask
63927  * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
63928  * @param {Object} config The config object
63929  */
63930 Roo.LoadMask = function(el, config){
63931     this.el = Roo.get(el);
63932     Roo.apply(this, config);
63933     if(this.store){
63934         this.store.on('beforeload', this.onBeforeLoad, this);
63935         this.store.on('load', this.onLoad, this);
63936         this.store.on('loadexception', this.onLoadException, this);
63937         this.removeMask = false;
63938     }else{
63939         var um = this.el.getUpdateManager();
63940         um.showLoadIndicator = false; // disable the default indicator
63941         um.on('beforeupdate', this.onBeforeLoad, this);
63942         um.on('update', this.onLoad, this);
63943         um.on('failure', this.onLoad, this);
63944         this.removeMask = true;
63945     }
63946 };
63947
63948 Roo.LoadMask.prototype = {
63949     /**
63950      * @cfg {Boolean} removeMask
63951      * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
63952      * False to persist the mask element reference for multiple uses (e.g., for paged data widgets).  Defaults to false.
63953      */
63954     removeMask : false,
63955     /**
63956      * @cfg {String} msg
63957      * The text to display in a centered loading message box (defaults to 'Loading...')
63958      */
63959     msg : 'Loading...',
63960     /**
63961      * @cfg {String} msgCls
63962      * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
63963      */
63964     msgCls : 'x-mask-loading',
63965
63966     /**
63967      * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
63968      * @type Boolean
63969      */
63970     disabled: false,
63971
63972     /**
63973      * Disables the mask to prevent it from being displayed
63974      */
63975     disable : function(){
63976        this.disabled = true;
63977     },
63978
63979     /**
63980      * Enables the mask so that it can be displayed
63981      */
63982     enable : function(){
63983         this.disabled = false;
63984     },
63985     
63986     onLoadException : function()
63987     {
63988         Roo.log(arguments);
63989         
63990         if (typeof(arguments[3]) != 'undefined') {
63991             Roo.MessageBox.alert("Error loading",arguments[3]);
63992         } 
63993         /*
63994         try {
63995             if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
63996                 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
63997             }   
63998         } catch(e) {
63999             
64000         }
64001         */
64002     
64003         (function() { this.el.unmask(this.removeMask); }).defer(50, this);
64004     },
64005     // private
64006     onLoad : function()
64007     {
64008         (function() { this.el.unmask(this.removeMask); }).defer(50, this);
64009     },
64010
64011     // private
64012     onBeforeLoad : function(){
64013         if(!this.disabled){
64014             (function() { this.el.mask(this.msg, this.msgCls); }).defer(50, this);
64015         }
64016     },
64017
64018     // private
64019     destroy : function(){
64020         if(this.store){
64021             this.store.un('beforeload', this.onBeforeLoad, this);
64022             this.store.un('load', this.onLoad, this);
64023             this.store.un('loadexception', this.onLoadException, this);
64024         }else{
64025             var um = this.el.getUpdateManager();
64026             um.un('beforeupdate', this.onBeforeLoad, this);
64027             um.un('update', this.onLoad, this);
64028             um.un('failure', this.onLoad, this);
64029         }
64030     }
64031 };/*
64032  * Based on:
64033  * Ext JS Library 1.1.1
64034  * Copyright(c) 2006-2007, Ext JS, LLC.
64035  *
64036  * Originally Released Under LGPL - original licence link has changed is not relivant.
64037  *
64038  * Fork - LGPL
64039  * <script type="text/javascript">
64040  */
64041
64042
64043 /**
64044  * @class Roo.XTemplate
64045  * @extends Roo.Template
64046  * Provides a template that can have nested templates for loops or conditionals. The syntax is:
64047 <pre><code>
64048 var t = new Roo.XTemplate(
64049         '&lt;select name="{name}"&gt;',
64050                 '&lt;tpl for="options"&gt;&lt;option value="{value:trim}"&gt;{text:ellipsis(10)}&lt;/option&gt;&lt;/tpl&gt;',
64051         '&lt;/select&gt;'
64052 );
64053  
64054 // then append, applying the master template values
64055  </code></pre>
64056  *
64057  * Supported features:
64058  *
64059  *  Tags:
64060
64061 <pre><code>
64062       {a_variable} - output encoded.
64063       {a_variable.format:("Y-m-d")} - call a method on the variable
64064       {a_variable:raw} - unencoded output
64065       {a_variable:toFixed(1,2)} - Roo.util.Format."toFixed"
64066       {a_variable:this.method_on_template(...)} - call a method on the template object.
64067  
64068 </code></pre>
64069  *  The tpl tag:
64070 <pre><code>
64071         &lt;tpl for="a_variable or condition.."&gt;&lt;/tpl&gt;
64072         &lt;tpl if="a_variable or condition"&gt;&lt;/tpl&gt;
64073         &lt;tpl exec="some javascript"&gt;&lt;/tpl&gt;
64074         &lt;tpl name="named_template"&gt;&lt;/tpl&gt; (experimental)
64075   
64076         &lt;tpl for="."&gt;&lt;/tpl&gt; - just iterate the property..
64077         &lt;tpl for=".."&gt;&lt;/tpl&gt; - iterates with the parent (probably the template) 
64078 </code></pre>
64079  *      
64080  */
64081 Roo.XTemplate = function()
64082 {
64083     Roo.XTemplate.superclass.constructor.apply(this, arguments);
64084     if (this.html) {
64085         this.compile();
64086     }
64087 };
64088
64089
64090 Roo.extend(Roo.XTemplate, Roo.Template, {
64091
64092     /**
64093      * The various sub templates
64094      */
64095     tpls : false,
64096     /**
64097      *
64098      * basic tag replacing syntax
64099      * WORD:WORD()
64100      *
64101      * // you can fake an object call by doing this
64102      *  x.t:(test,tesT) 
64103      * 
64104      */
64105     re : /\{([\w-\.]+)(?:\:([\w\.]*)(?:\((.*?)?\))?)?\}/g,
64106
64107     /**
64108      * compile the template
64109      *
64110      * This is not recursive, so I'm not sure how nested templates are really going to be handled..
64111      *
64112      */
64113     compile: function()
64114     {
64115         var s = this.html;
64116      
64117         s = ['<tpl>', s, '</tpl>'].join('');
64118     
64119         var re     = /<tpl\b[^>]*>((?:(?=([^<]+))\2|<(?!tpl\b[^>]*>))*?)<\/tpl>/,
64120             nameRe = /^<tpl\b[^>]*?for="(.*?)"/,
64121             ifRe   = /^<tpl\b[^>]*?if="(.*?)"/,
64122             execRe = /^<tpl\b[^>]*?exec="(.*?)"/,
64123             namedRe = /^<tpl\b[^>]*?name="(\w+)"/,  // named templates..
64124             m,
64125             id     = 0,
64126             tpls   = [];
64127     
64128         while(true == !!(m = s.match(re))){
64129             var forMatch   = m[0].match(nameRe),
64130                 ifMatch   = m[0].match(ifRe),
64131                 execMatch   = m[0].match(execRe),
64132                 namedMatch   = m[0].match(namedRe),
64133                 
64134                 exp  = null, 
64135                 fn   = null,
64136                 exec = null,
64137                 name = forMatch && forMatch[1] ? forMatch[1] : '';
64138                 
64139             if (ifMatch) {
64140                 // if - puts fn into test..
64141                 exp = ifMatch && ifMatch[1] ? ifMatch[1] : null;
64142                 if(exp){
64143                    fn = new Function('values', 'parent', 'with(values){ return '+(Roo.util.Format.htmlDecode(exp))+'; }');
64144                 }
64145             }
64146             
64147             if (execMatch) {
64148                 // exec - calls a function... returns empty if true is  returned.
64149                 exp = execMatch && execMatch[1] ? execMatch[1] : null;
64150                 if(exp){
64151                    exec = new Function('values', 'parent', 'with(values){ '+(Roo.util.Format.htmlDecode(exp))+'; }');
64152                 }
64153             }
64154             
64155             
64156             if (name) {
64157                 // for = 
64158                 switch(name){
64159                     case '.':  name = new Function('values', 'parent', 'with(values){ return values; }'); break;
64160                     case '..': name = new Function('values', 'parent', 'with(values){ return parent; }'); break;
64161                     default:   name = new Function('values', 'parent', 'with(values){ return '+name+'; }');
64162                 }
64163             }
64164             var uid = namedMatch ? namedMatch[1] : id;
64165             
64166             
64167             tpls.push({
64168                 id:     namedMatch ? namedMatch[1] : id,
64169                 target: name,
64170                 exec:   exec,
64171                 test:   fn,
64172                 body:   m[1] || ''
64173             });
64174             if (namedMatch) {
64175                 s = s.replace(m[0], '');
64176             } else { 
64177                 s = s.replace(m[0], '{xtpl'+ id + '}');
64178             }
64179             ++id;
64180         }
64181         this.tpls = [];
64182         for(var i = tpls.length-1; i >= 0; --i){
64183             this.compileTpl(tpls[i]);
64184             this.tpls[tpls[i].id] = tpls[i];
64185         }
64186         this.master = tpls[tpls.length-1];
64187         return this;
64188     },
64189     /**
64190      * same as applyTemplate, except it's done to one of the subTemplates
64191      * when using named templates, you can do:
64192      *
64193      * var str = pl.applySubTemplate('your-name', values);
64194      *
64195      * 
64196      * @param {Number} id of the template
64197      * @param {Object} values to apply to template
64198      * @param {Object} parent (normaly the instance of this object)
64199      */
64200     applySubTemplate : function(id, values, parent)
64201     {
64202         
64203         
64204         var t = this.tpls[id];
64205         
64206         
64207         try { 
64208             if(t.test && !t.test.call(this, values, parent)){
64209                 return '';
64210             }
64211         } catch(e) {
64212             Roo.log("Xtemplate.applySubTemplate 'test': Exception thrown");
64213             Roo.log(e.toString());
64214             Roo.log(t.test);
64215             return ''
64216         }
64217         try { 
64218             
64219             if(t.exec && t.exec.call(this, values, parent)){
64220                 return '';
64221             }
64222         } catch(e) {
64223             Roo.log("Xtemplate.applySubTemplate 'exec': Exception thrown");
64224             Roo.log(e.toString());
64225             Roo.log(t.exec);
64226             return ''
64227         }
64228         try {
64229             var vs = t.target ? t.target.call(this, values, parent) : values;
64230             parent = t.target ? values : parent;
64231             if(t.target && vs instanceof Array){
64232                 var buf = [];
64233                 for(var i = 0, len = vs.length; i < len; i++){
64234                     buf[buf.length] = t.compiled.call(this, vs[i], parent);
64235                 }
64236                 return buf.join('');
64237             }
64238             return t.compiled.call(this, vs, parent);
64239         } catch (e) {
64240             Roo.log("Xtemplate.applySubTemplate : Exception thrown");
64241             Roo.log(e.toString());
64242             Roo.log(t.compiled);
64243             return '';
64244         }
64245     },
64246
64247     compileTpl : function(tpl)
64248     {
64249         var fm = Roo.util.Format;
64250         var useF = this.disableFormats !== true;
64251         var sep = Roo.isGecko ? "+" : ",";
64252         var undef = function(str) {
64253             Roo.log("Property not found :"  + str);
64254             return '';
64255         };
64256         
64257         var fn = function(m, name, format, args)
64258         {
64259             //Roo.log(arguments);
64260             args = args ? args.replace(/\\'/g,"'") : args;
64261             //["{TEST:(a,b,c)}", "TEST", "", "a,b,c", 0, "{TEST:(a,b,c)}"]
64262             if (typeof(format) == 'undefined') {
64263                 format= 'htmlEncode';
64264             }
64265             if (format == 'raw' ) {
64266                 format = false;
64267             }
64268             
64269             if(name.substr(0, 4) == 'xtpl'){
64270                 return "'"+ sep +'this.applySubTemplate('+name.substr(4)+', values, parent)'+sep+"'";
64271             }
64272             
64273             // build an array of options to determine if value is undefined..
64274             
64275             // basically get 'xxxx.yyyy' then do
64276             // (typeof(xxxx) == 'undefined' || typeof(xxx.yyyy) == 'undefined') ?
64277             //    (function () { Roo.log("Property not found"); return ''; })() :
64278             //    ......
64279             
64280             var udef_ar = [];
64281             var lookfor = '';
64282             Roo.each(name.split('.'), function(st) {
64283                 lookfor += (lookfor.length ? '.': '') + st;
64284                 udef_ar.push(  "(typeof(" + lookfor + ") == 'undefined')"  );
64285             });
64286             
64287             var udef_st = '((' + udef_ar.join(" || ") +") ? undef('" + name + "') : "; // .. needs )
64288             
64289             
64290             if(format && useF){
64291                 
64292                 args = args ? ',' + args : "";
64293                  
64294                 if(format.substr(0, 5) != "this."){
64295                     format = "fm." + format + '(';
64296                 }else{
64297                     format = 'this.call("'+ format.substr(5) + '", ';
64298                     args = ", values";
64299                 }
64300                 
64301                 return "'"+ sep +   udef_st   +    format + name + args + "))"+sep+"'";
64302             }
64303              
64304             if (args.length) {
64305                 // called with xxyx.yuu:(test,test)
64306                 // change to ()
64307                 return "'"+ sep + udef_st  + name + '(' +  args + "))"+sep+"'";
64308             }
64309             // raw.. - :raw modifier..
64310             return "'"+ sep + udef_st  + name + ")"+sep+"'";
64311             
64312         };
64313         var body;
64314         // branched to use + in gecko and [].join() in others
64315         if(Roo.isGecko){
64316             body = "tpl.compiled = function(values, parent){  with(values) { return '" +
64317                    tpl.body.replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn) +
64318                     "';};};";
64319         }else{
64320             body = ["tpl.compiled = function(values, parent){  with (values) { return ['"];
64321             body.push(tpl.body.replace(/(\r\n|\n)/g,
64322                             '\\n').replace(/'/g, "\\'").replace(this.re, fn));
64323             body.push("'].join('');};};");
64324             body = body.join('');
64325         }
64326         
64327         Roo.debug && Roo.log(body.replace(/\\n/,'\n'));
64328        
64329         /** eval:var:tpl eval:var:fm eval:var:useF eval:var:undef  */
64330         eval(body);
64331         
64332         return this;
64333     },
64334
64335     applyTemplate : function(values){
64336         return this.master.compiled.call(this, values, {});
64337         //var s = this.subs;
64338     },
64339
64340     apply : function(){
64341         return this.applyTemplate.apply(this, arguments);
64342     }
64343
64344  });
64345
64346 Roo.XTemplate.from = function(el){
64347     el = Roo.getDom(el);
64348     return new Roo.XTemplate(el.value || el.innerHTML);
64349 };