roojs-ui.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     return {
5150     /** True to force the use of DOM instead of html fragments @type Boolean */
5151     useDom : false,
5152
5153     /**
5154      * Returns the markup for the passed Element(s) config
5155      * @param {Object} o The Dom object spec (and children)
5156      * @return {String}
5157      */
5158     markup : function(o){
5159         return createHtml(o);
5160     },
5161
5162     /**
5163      * Applies a style specification to an element
5164      * @param {String/HTMLElement} el The element to apply styles to
5165      * @param {String/Object/Function} styles A style specification string eg "width:100px", or object in the form {width:"100px"}, or
5166      * a function which returns such a specification.
5167      */
5168     applyStyles : function(el, styles){
5169         if(styles){
5170            el = Roo.fly(el);
5171            if(typeof styles == "string"){
5172                var re = /\s?([a-z\-]*)\:\s?([^;]*);?/gi;
5173                var matches;
5174                while ((matches = re.exec(styles)) != null){
5175                    el.setStyle(matches[1], matches[2]);
5176                }
5177            }else if (typeof styles == "object"){
5178                for (var style in styles){
5179                   el.setStyle(style, styles[style]);
5180                }
5181            }else if (typeof styles == "function"){
5182                 Roo.DomHelper.applyStyles(el, styles.call());
5183            }
5184         }
5185     },
5186
5187     /**
5188      * Inserts an HTML fragment into the Dom
5189      * @param {String} where Where to insert the html in relation to el - beforeBegin, afterBegin, beforeEnd, afterEnd.
5190      * @param {HTMLElement} el The context element
5191      * @param {String} html The HTML fragmenet
5192      * @return {HTMLElement} The new node
5193      */
5194     insertHtml : function(where, el, html){
5195         where = where.toLowerCase();
5196         if(el.insertAdjacentHTML){
5197             if(tableRe.test(el.tagName)){
5198                 var rs;
5199                 if(rs = insertIntoTable(el.tagName.toLowerCase(), where, el, html)){
5200                     return rs;
5201                 }
5202             }
5203             switch(where){
5204                 case "beforebegin":
5205                     el.insertAdjacentHTML('BeforeBegin', html);
5206                     return el.previousSibling;
5207                 case "afterbegin":
5208                     el.insertAdjacentHTML('AfterBegin', html);
5209                     return el.firstChild;
5210                 case "beforeend":
5211                     el.insertAdjacentHTML('BeforeEnd', html);
5212                     return el.lastChild;
5213                 case "afterend":
5214                     el.insertAdjacentHTML('AfterEnd', html);
5215                     return el.nextSibling;
5216             }
5217             throw 'Illegal insertion point -> "' + where + '"';
5218         }
5219         var range = el.ownerDocument.createRange();
5220         var frag;
5221         switch(where){
5222              case "beforebegin":
5223                 range.setStartBefore(el);
5224                 frag = range.createContextualFragment(html);
5225                 el.parentNode.insertBefore(frag, el);
5226                 return el.previousSibling;
5227              case "afterbegin":
5228                 if(el.firstChild){
5229                     range.setStartBefore(el.firstChild);
5230                     frag = range.createContextualFragment(html);
5231                     el.insertBefore(frag, el.firstChild);
5232                     return el.firstChild;
5233                 }else{
5234                     el.innerHTML = html;
5235                     return el.firstChild;
5236                 }
5237             case "beforeend":
5238                 if(el.lastChild){
5239                     range.setStartAfter(el.lastChild);
5240                     frag = range.createContextualFragment(html);
5241                     el.appendChild(frag);
5242                     return el.lastChild;
5243                 }else{
5244                     el.innerHTML = html;
5245                     return el.lastChild;
5246                 }
5247             case "afterend":
5248                 range.setStartAfter(el);
5249                 frag = range.createContextualFragment(html);
5250                 el.parentNode.insertBefore(frag, el.nextSibling);
5251                 return el.nextSibling;
5252             }
5253             throw 'Illegal insertion point -> "' + where + '"';
5254     },
5255
5256     /**
5257      * Creates new Dom element(s) and inserts them before el
5258      * @param {String/HTMLElement/Element} el The context element
5259      * @param {Object/String} o The Dom object spec (and children) or raw HTML blob
5260      * @param {Boolean} returnElement (optional) true to return a Roo.Element
5261      * @return {HTMLElement/Roo.Element} The new node
5262      */
5263     insertBefore : function(el, o, returnElement){
5264         return this.doInsert(el, o, returnElement, "beforeBegin");
5265     },
5266
5267     /**
5268      * Creates new Dom element(s) and inserts them after el
5269      * @param {String/HTMLElement/Element} el The context element
5270      * @param {Object} o The Dom object spec (and children)
5271      * @param {Boolean} returnElement (optional) true to return a Roo.Element
5272      * @return {HTMLElement/Roo.Element} The new node
5273      */
5274     insertAfter : function(el, o, returnElement){
5275         return this.doInsert(el, o, returnElement, "afterEnd", "nextSibling");
5276     },
5277
5278     /**
5279      * Creates new Dom element(s) and inserts them as the first child of el
5280      * @param {String/HTMLElement/Element} el The context element
5281      * @param {Object/String} o The Dom object spec (and children) or raw HTML blob
5282      * @param {Boolean} returnElement (optional) true to return a Roo.Element
5283      * @return {HTMLElement/Roo.Element} The new node
5284      */
5285     insertFirst : function(el, o, returnElement){
5286         return this.doInsert(el, o, returnElement, "afterBegin");
5287     },
5288
5289     // private
5290     doInsert : function(el, o, returnElement, pos, sibling){
5291         el = Roo.getDom(el);
5292         var newNode;
5293         if(this.useDom || o.ns){
5294             newNode = createDom(o, null);
5295             el.parentNode.insertBefore(newNode, sibling ? el[sibling] : el);
5296         }else{
5297             var html = createHtml(o);
5298             newNode = this.insertHtml(pos, el, html);
5299         }
5300         return returnElement ? Roo.get(newNode, true) : newNode;
5301     },
5302
5303     /**
5304      * Creates new Dom element(s) and appends them to el
5305      * @param {String/HTMLElement/Element} el The context element
5306      * @param {Object/String} o The Dom object spec (and children) or raw HTML blob
5307      * @param {Boolean} returnElement (optional) true to return a Roo.Element
5308      * @return {HTMLElement/Roo.Element} The new node
5309      */
5310     append : function(el, o, returnElement){
5311         el = Roo.getDom(el);
5312         var newNode;
5313         if(this.useDom || o.ns){
5314             newNode = createDom(o, null);
5315             el.appendChild(newNode);
5316         }else{
5317             var html = createHtml(o);
5318             newNode = this.insertHtml("beforeEnd", el, html);
5319         }
5320         return returnElement ? Roo.get(newNode, true) : newNode;
5321     },
5322
5323     /**
5324      * Creates new Dom element(s) and overwrites the contents of el with them
5325      * @param {String/HTMLElement/Element} el The context element
5326      * @param {Object/String} o The Dom object spec (and children) or raw HTML blob
5327      * @param {Boolean} returnElement (optional) true to return a Roo.Element
5328      * @return {HTMLElement/Roo.Element} The new node
5329      */
5330     overwrite : function(el, o, returnElement){
5331         el = Roo.getDom(el);
5332         if (o.ns) {
5333           
5334             while (el.childNodes.length) {
5335                 el.removeChild(el.firstChild);
5336             }
5337             createDom(o, el);
5338         } else {
5339             el.innerHTML = createHtml(o);   
5340         }
5341         
5342         return returnElement ? Roo.get(el.firstChild, true) : el.firstChild;
5343     },
5344
5345     /**
5346      * Creates a new Roo.DomHelper.Template from the Dom object spec
5347      * @param {Object} o The Dom object spec (and children)
5348      * @return {Roo.DomHelper.Template} The new template
5349      */
5350     createTemplate : function(o){
5351         var html = createHtml(o);
5352         return new Roo.Template(html);
5353     }
5354     };
5355 }();
5356 /*
5357  * Based on:
5358  * Ext JS Library 1.1.1
5359  * Copyright(c) 2006-2007, Ext JS, LLC.
5360  *
5361  * Originally Released Under LGPL - original licence link has changed is not relivant.
5362  *
5363  * Fork - LGPL
5364  * <script type="text/javascript">
5365  */
5366  
5367 /**
5368 * @class Roo.Template
5369 * Represents an HTML fragment template. Templates can be precompiled for greater performance.
5370 * For a list of available format functions, see {@link Roo.util.Format}.<br />
5371 * Usage:
5372 <pre><code>
5373 var t = new Roo.Template({
5374     html :  '&lt;div name="{id}"&gt;' + 
5375         '&lt;span class="{cls}"&gt;{name:trim} {someval:this.myformat}{value:ellipsis(10)}&lt;/span&gt;' +
5376         '&lt;/div&gt;',
5377     myformat: function (value, allValues) {
5378         return 'XX' + value;
5379     }
5380 });
5381 t.append('some-element', {id: 'myid', cls: 'myclass', name: 'foo', value: 'bar'});
5382 </code></pre>
5383 * For more information see this blog post with examples:
5384 *  <a href="http://www.cnitblog.com/seeyeah/archive/2011/12/30/38728.html/">DomHelper
5385      - Create Elements using DOM, HTML fragments and Templates</a>. 
5386 * @constructor
5387 * @param {Object} cfg - Configuration object.
5388 */
5389 Roo.Template = function(cfg){
5390     // BC!
5391     if(cfg instanceof Array){
5392         cfg = cfg.join("");
5393     }else if(arguments.length > 1){
5394         cfg = Array.prototype.join.call(arguments, "");
5395     }
5396     
5397     
5398     if (typeof(cfg) == 'object') {
5399         Roo.apply(this,cfg)
5400     } else {
5401         // bc
5402         this.html = cfg;
5403     }
5404     if (this.url) {
5405         this.load();
5406     }
5407     
5408 };
5409 Roo.Template.prototype = {
5410     
5411     /**
5412      * @cfg {Function} onLoad Called after the template has been loaded and complied (usually from a remove source)
5413      */
5414     onLoad : false,
5415     
5416     
5417     /**
5418      * @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..
5419      *                    it should be fixed so that template is observable...
5420      */
5421     url : false,
5422     /**
5423      * @cfg {String} html  The HTML fragment or an array of fragments to join("") or multiple arguments to join("")
5424      */
5425     html : '',
5426     
5427     
5428     compiled : false,
5429     loaded : false,
5430     /**
5431      * Returns an HTML fragment of this template with the specified values applied.
5432      * @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'})
5433      * @return {String} The HTML fragment
5434      */
5435     
5436    
5437     
5438     applyTemplate : function(values){
5439         //Roo.log(["applyTemplate", values]);
5440         try {
5441            
5442             if(this.compiled){
5443                 return this.compiled(values);
5444             }
5445             var useF = this.disableFormats !== true;
5446             var fm = Roo.util.Format, tpl = this;
5447             var fn = function(m, name, format, args){
5448                 if(format && useF){
5449                     if(format.substr(0, 5) == "this."){
5450                         return tpl.call(format.substr(5), values[name], values);
5451                     }else{
5452                         if(args){
5453                             // quoted values are required for strings in compiled templates, 
5454                             // but for non compiled we need to strip them
5455                             // quoted reversed for jsmin
5456                             var re = /^\s*['"](.*)["']\s*$/;
5457                             args = args.split(',');
5458                             for(var i = 0, len = args.length; i < len; i++){
5459                                 args[i] = args[i].replace(re, "$1");
5460                             }
5461                             args = [values[name]].concat(args);
5462                         }else{
5463                             args = [values[name]];
5464                         }
5465                         return fm[format].apply(fm, args);
5466                     }
5467                 }else{
5468                     return values[name] !== undefined ? values[name] : "";
5469                 }
5470             };
5471             return this.html.replace(this.re, fn);
5472         } catch (e) {
5473             Roo.log(e);
5474             throw e;
5475         }
5476          
5477     },
5478     
5479     loading : false,
5480       
5481     load : function ()
5482     {
5483          
5484         if (this.loading) {
5485             return;
5486         }
5487         var _t = this;
5488         
5489         this.loading = true;
5490         this.compiled = false;
5491         
5492         var cx = new Roo.data.Connection();
5493         cx.request({
5494             url : this.url,
5495             method : 'GET',
5496             success : function (response) {
5497                 _t.loading = false;
5498                 _t.url = false;
5499                 
5500                 _t.set(response.responseText,true);
5501                 _t.loaded = true;
5502                 if (_t.onLoad) {
5503                     _t.onLoad();
5504                 }
5505              },
5506             failure : function(response) {
5507                 Roo.log("Template failed to load from " + _t.url);
5508                 _t.loading = false;
5509             }
5510         });
5511     },
5512
5513     /**
5514      * Sets the HTML used as the template and optionally compiles it.
5515      * @param {String} html
5516      * @param {Boolean} compile (optional) True to compile the template (defaults to undefined)
5517      * @return {Roo.Template} this
5518      */
5519     set : function(html, compile){
5520         this.html = html;
5521         this.compiled = false;
5522         if(compile){
5523             this.compile();
5524         }
5525         return this;
5526     },
5527     
5528     /**
5529      * True to disable format functions (defaults to false)
5530      * @type Boolean
5531      */
5532     disableFormats : false,
5533     
5534     /**
5535     * The regular expression used to match template variables 
5536     * @type RegExp
5537     * @property 
5538     */
5539     re : /\{([\w-]+)(?:\:([\w\.]*)(?:\((.*?)?\))?)?\}/g,
5540     
5541     /**
5542      * Compiles the template into an internal function, eliminating the RegEx overhead.
5543      * @return {Roo.Template} this
5544      */
5545     compile : function(){
5546         var fm = Roo.util.Format;
5547         var useF = this.disableFormats !== true;
5548         var sep = Roo.isGecko ? "+" : ",";
5549         var fn = function(m, name, format, args){
5550             if(format && useF){
5551                 args = args ? ',' + args : "";
5552                 if(format.substr(0, 5) != "this."){
5553                     format = "fm." + format + '(';
5554                 }else{
5555                     format = 'this.call("'+ format.substr(5) + '", ';
5556                     args = ", values";
5557                 }
5558             }else{
5559                 args= ''; format = "(values['" + name + "'] == undefined ? '' : ";
5560             }
5561             return "'"+ sep + format + "values['" + name + "']" + args + ")"+sep+"'";
5562         };
5563         var body;
5564         // branched to use + in gecko and [].join() in others
5565         if(Roo.isGecko){
5566             body = "this.compiled = function(values){ return '" +
5567                    this.html.replace(/\\/g, '\\\\').replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn) +
5568                     "';};";
5569         }else{
5570             body = ["this.compiled = function(values){ return ['"];
5571             body.push(this.html.replace(/\\/g, '\\\\').replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn));
5572             body.push("'].join('');};");
5573             body = body.join('');
5574         }
5575         /**
5576          * eval:var:values
5577          * eval:var:fm
5578          */
5579         eval(body);
5580         return this;
5581     },
5582     
5583     // private function used to call members
5584     call : function(fnName, value, allValues){
5585         return this[fnName](value, allValues);
5586     },
5587     
5588     /**
5589      * Applies the supplied values to the template and inserts the new node(s) as the first child of el.
5590      * @param {String/HTMLElement/Roo.Element} el The context element
5591      * @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'})
5592      * @param {Boolean} returnElement (optional) true to return a Roo.Element (defaults to undefined)
5593      * @return {HTMLElement/Roo.Element} The new node or Element
5594      */
5595     insertFirst: function(el, values, returnElement){
5596         return this.doInsert('afterBegin', el, values, returnElement);
5597     },
5598
5599     /**
5600      * Applies the supplied values to the template and inserts the new node(s) before el.
5601      * @param {String/HTMLElement/Roo.Element} el The context element
5602      * @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'})
5603      * @param {Boolean} returnElement (optional) true to return a Roo.Element (defaults to undefined)
5604      * @return {HTMLElement/Roo.Element} The new node or Element
5605      */
5606     insertBefore: function(el, values, returnElement){
5607         return this.doInsert('beforeBegin', el, values, returnElement);
5608     },
5609
5610     /**
5611      * Applies the supplied values to the template and inserts the new node(s) after el.
5612      * @param {String/HTMLElement/Roo.Element} el The context element
5613      * @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'})
5614      * @param {Boolean} returnElement (optional) true to return a Roo.Element (defaults to undefined)
5615      * @return {HTMLElement/Roo.Element} The new node or Element
5616      */
5617     insertAfter : function(el, values, returnElement){
5618         return this.doInsert('afterEnd', el, values, returnElement);
5619     },
5620     
5621     /**
5622      * Applies the supplied values to the template and appends the new node(s) to el.
5623      * @param {String/HTMLElement/Roo.Element} el The context element
5624      * @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'})
5625      * @param {Boolean} returnElement (optional) true to return a Roo.Element (defaults to undefined)
5626      * @return {HTMLElement/Roo.Element} The new node or Element
5627      */
5628     append : function(el, values, returnElement){
5629         return this.doInsert('beforeEnd', el, values, returnElement);
5630     },
5631
5632     doInsert : function(where, el, values, returnEl){
5633         el = Roo.getDom(el);
5634         var newNode = Roo.DomHelper.insertHtml(where, el, this.applyTemplate(values));
5635         return returnEl ? Roo.get(newNode, true) : newNode;
5636     },
5637
5638     /**
5639      * Applies the supplied values to the template and overwrites the content of el with the new node(s).
5640      * @param {String/HTMLElement/Roo.Element} el The context element
5641      * @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'})
5642      * @param {Boolean} returnElement (optional) true to return a Roo.Element (defaults to undefined)
5643      * @return {HTMLElement/Roo.Element} The new node or Element
5644      */
5645     overwrite : function(el, values, returnElement){
5646         el = Roo.getDom(el);
5647         el.innerHTML = this.applyTemplate(values);
5648         return returnElement ? Roo.get(el.firstChild, true) : el.firstChild;
5649     }
5650 };
5651 /**
5652  * Alias for {@link #applyTemplate}
5653  * @method
5654  */
5655 Roo.Template.prototype.apply = Roo.Template.prototype.applyTemplate;
5656
5657 // backwards compat
5658 Roo.DomHelper.Template = Roo.Template;
5659
5660 /**
5661  * Creates a template from the passed element's value (<i>display:none</i> textarea, preferred) or innerHTML.
5662  * @param {String/HTMLElement} el A DOM element or its id
5663  * @returns {Roo.Template} The created template
5664  * @static
5665  */
5666 Roo.Template.from = function(el){
5667     el = Roo.getDom(el);
5668     return new Roo.Template(el.value || el.innerHTML);
5669 };/*
5670  * Based on:
5671  * Ext JS Library 1.1.1
5672  * Copyright(c) 2006-2007, Ext JS, LLC.
5673  *
5674  * Originally Released Under LGPL - original licence link has changed is not relivant.
5675  *
5676  * Fork - LGPL
5677  * <script type="text/javascript">
5678  */
5679  
5680
5681 /*
5682  * This is code is also distributed under MIT license for use
5683  * with jQuery and prototype JavaScript libraries.
5684  */
5685 /**
5686  * @class Roo.DomQuery
5687 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).
5688 <p>
5689 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>
5690
5691 <p>
5692 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.
5693 </p>
5694 <h4>Element Selectors:</h4>
5695 <ul class="list">
5696     <li> <b>*</b> any element</li>
5697     <li> <b>E</b> an element with the tag E</li>
5698     <li> <b>E F</b> All descendent elements of E that have the tag F</li>
5699     <li> <b>E > F</b> or <b>E/F</b> all direct children elements of E that have the tag F</li>
5700     <li> <b>E + F</b> all elements with the tag F that are immediately preceded by an element with the tag E</li>
5701     <li> <b>E ~ F</b> all elements with the tag F that are preceded by a sibling element with the tag E</li>
5702 </ul>
5703 <h4>Attribute Selectors:</h4>
5704 <p>The use of @ and quotes are optional. For example, div[@foo='bar'] is also a valid attribute selector.</p>
5705 <ul class="list">
5706     <li> <b>E[foo]</b> has an attribute "foo"</li>
5707     <li> <b>E[foo=bar]</b> has an attribute "foo" that equals "bar"</li>
5708     <li> <b>E[foo^=bar]</b> has an attribute "foo" that starts with "bar"</li>
5709     <li> <b>E[foo$=bar]</b> has an attribute "foo" that ends with "bar"</li>
5710     <li> <b>E[foo*=bar]</b> has an attribute "foo" that contains the substring "bar"</li>
5711     <li> <b>E[foo%=2]</b> has an attribute "foo" that is evenly divisible by 2</li>
5712     <li> <b>E[foo!=bar]</b> has an attribute "foo" that does not equal "bar"</li>
5713 </ul>
5714 <h4>Pseudo Classes:</h4>
5715 <ul class="list">
5716     <li> <b>E:first-child</b> E is the first child of its parent</li>
5717     <li> <b>E:last-child</b> E is the last child of its parent</li>
5718     <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>
5719     <li> <b>E:nth-child(odd)</b> E is an odd child of its parent</li>
5720     <li> <b>E:nth-child(even)</b> E is an even child of its parent</li>
5721     <li> <b>E:only-child</b> E is the only child of its parent</li>
5722     <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>
5723     <li> <b>E:first</b> the first E in the resultset</li>
5724     <li> <b>E:last</b> the last E in the resultset</li>
5725     <li> <b>E:nth(<i>n</i>)</b> the <i>n</i>th E in the resultset (1 based)</li>
5726     <li> <b>E:odd</b> shortcut for :nth-child(odd)</li>
5727     <li> <b>E:even</b> shortcut for :nth-child(even)</li>
5728     <li> <b>E:contains(foo)</b> E's innerHTML contains the substring "foo"</li>
5729     <li> <b>E:nodeValue(foo)</b> E contains a textNode with a nodeValue that equals "foo"</li>
5730     <li> <b>E:not(S)</b> an E element that does not match simple selector S</li>
5731     <li> <b>E:has(S)</b> an E element that has a descendent that matches simple selector S</li>
5732     <li> <b>E:next(S)</b> an E element whose next sibling matches simple selector S</li>
5733     <li> <b>E:prev(S)</b> an E element whose previous sibling matches simple selector S</li>
5734 </ul>
5735 <h4>CSS Value Selectors:</h4>
5736 <ul class="list">
5737     <li> <b>E{display=none}</b> css value "display" that equals "none"</li>
5738     <li> <b>E{display^=none}</b> css value "display" that starts with "none"</li>
5739     <li> <b>E{display$=none}</b> css value "display" that ends with "none"</li>
5740     <li> <b>E{display*=none}</b> css value "display" that contains the substring "none"</li>
5741     <li> <b>E{display%=2}</b> css value "display" that is evenly divisible by 2</li>
5742     <li> <b>E{display!=none}</b> css value "display" that does not equal "none"</li>
5743 </ul>
5744  * @static
5745  */
5746 Roo.DomQuery = function(){
5747     var cache = {}, simpleCache = {}, valueCache = {};
5748     var nonSpace = /\S/;
5749     var trimRe = /^\s+|\s+$/g;
5750     var tplRe = /\{(\d+)\}/g;
5751     var modeRe = /^(\s?[\/>+~]\s?|\s|$)/;
5752     var tagTokenRe = /^(#)?([\w-\*]+)/;
5753     var nthRe = /(\d*)n\+?(\d*)/, nthRe2 = /\D/;
5754
5755     function child(p, index){
5756         var i = 0;
5757         var n = p.firstChild;
5758         while(n){
5759             if(n.nodeType == 1){
5760                if(++i == index){
5761                    return n;
5762                }
5763             }
5764             n = n.nextSibling;
5765         }
5766         return null;
5767     };
5768
5769     function next(n){
5770         while((n = n.nextSibling) && n.nodeType != 1);
5771         return n;
5772     };
5773
5774     function prev(n){
5775         while((n = n.previousSibling) && n.nodeType != 1);
5776         return n;
5777     };
5778
5779     function children(d){
5780         var n = d.firstChild, ni = -1;
5781             while(n){
5782                 var nx = n.nextSibling;
5783                 if(n.nodeType == 3 && !nonSpace.test(n.nodeValue)){
5784                     d.removeChild(n);
5785                 }else{
5786                     n.nodeIndex = ++ni;
5787                 }
5788                 n = nx;
5789             }
5790             return this;
5791         };
5792
5793     function byClassName(c, a, v){
5794         if(!v){
5795             return c;
5796         }
5797         var r = [], ri = -1, cn;
5798         for(var i = 0, ci; ci = c[i]; i++){
5799             
5800             
5801             if((' '+
5802                 ( (ci instanceof SVGElement) ? ci.className.baseVal : ci.className)
5803                  +' ').indexOf(v) != -1){
5804                 r[++ri] = ci;
5805             }
5806         }
5807         return r;
5808     };
5809
5810     function attrValue(n, attr){
5811         if(!n.tagName && typeof n.length != "undefined"){
5812             n = n[0];
5813         }
5814         if(!n){
5815             return null;
5816         }
5817         if(attr == "for"){
5818             return n.htmlFor;
5819         }
5820         if(attr == "class" || attr == "className"){
5821             return (n instanceof SVGElement) ? n.className.baseVal : n.className;
5822         }
5823         return n.getAttribute(attr) || n[attr];
5824
5825     };
5826
5827     function getNodes(ns, mode, tagName){
5828         var result = [], ri = -1, cs;
5829         if(!ns){
5830             return result;
5831         }
5832         tagName = tagName || "*";
5833         if(typeof ns.getElementsByTagName != "undefined"){
5834             ns = [ns];
5835         }
5836         if(!mode){
5837             for(var i = 0, ni; ni = ns[i]; i++){
5838                 cs = ni.getElementsByTagName(tagName);
5839                 for(var j = 0, ci; ci = cs[j]; j++){
5840                     result[++ri] = ci;
5841                 }
5842             }
5843         }else if(mode == "/" || mode == ">"){
5844             var utag = tagName.toUpperCase();
5845             for(var i = 0, ni, cn; ni = ns[i]; i++){
5846                 cn = ni.children || ni.childNodes;
5847                 for(var j = 0, cj; cj = cn[j]; j++){
5848                     if(cj.nodeName == utag || cj.nodeName == tagName  || tagName == '*'){
5849                         result[++ri] = cj;
5850                     }
5851                 }
5852             }
5853         }else if(mode == "+"){
5854             var utag = tagName.toUpperCase();
5855             for(var i = 0, n; n = ns[i]; i++){
5856                 while((n = n.nextSibling) && n.nodeType != 1);
5857                 if(n && (n.nodeName == utag || n.nodeName == tagName || tagName == '*')){
5858                     result[++ri] = n;
5859                 }
5860             }
5861         }else if(mode == "~"){
5862             for(var i = 0, n; n = ns[i]; i++){
5863                 while((n = n.nextSibling) && (n.nodeType != 1 || (tagName == '*' || n.tagName.toLowerCase()!=tagName)));
5864                 if(n){
5865                     result[++ri] = n;
5866                 }
5867             }
5868         }
5869         return result;
5870     };
5871
5872     function concat(a, b){
5873         if(b.slice){
5874             return a.concat(b);
5875         }
5876         for(var i = 0, l = b.length; i < l; i++){
5877             a[a.length] = b[i];
5878         }
5879         return a;
5880     }
5881
5882     function byTag(cs, tagName){
5883         if(cs.tagName || cs == document){
5884             cs = [cs];
5885         }
5886         if(!tagName){
5887             return cs;
5888         }
5889         var r = [], ri = -1;
5890         tagName = tagName.toLowerCase();
5891         for(var i = 0, ci; ci = cs[i]; i++){
5892             if(ci.nodeType == 1 && ci.tagName.toLowerCase()==tagName){
5893                 r[++ri] = ci;
5894             }
5895         }
5896         return r;
5897     };
5898
5899     function byId(cs, attr, id){
5900         if(cs.tagName || cs == document){
5901             cs = [cs];
5902         }
5903         if(!id){
5904             return cs;
5905         }
5906         var r = [], ri = -1;
5907         for(var i = 0,ci; ci = cs[i]; i++){
5908             if(ci && ci.id == id){
5909                 r[++ri] = ci;
5910                 return r;
5911             }
5912         }
5913         return r;
5914     };
5915
5916     function byAttribute(cs, attr, value, op, custom){
5917         var r = [], ri = -1, st = custom=="{";
5918         var f = Roo.DomQuery.operators[op];
5919         for(var i = 0, ci; ci = cs[i]; i++){
5920             var a;
5921             if(st){
5922                 a = Roo.DomQuery.getStyle(ci, attr);
5923             }
5924             else if(attr == "class" || attr == "className"){
5925                 a = (ci instanceof SVGElement) ? ci.className.baseVal : ci.className;
5926             }else if(attr == "for"){
5927                 a = ci.htmlFor;
5928             }else if(attr == "href"){
5929                 a = ci.getAttribute("href", 2);
5930             }else{
5931                 a = ci.getAttribute(attr);
5932             }
5933             if((f && f(a, value)) || (!f && a)){
5934                 r[++ri] = ci;
5935             }
5936         }
5937         return r;
5938     };
5939
5940     function byPseudo(cs, name, value){
5941         return Roo.DomQuery.pseudos[name](cs, value);
5942     };
5943
5944     // This is for IE MSXML which does not support expandos.
5945     // IE runs the same speed using setAttribute, however FF slows way down
5946     // and Safari completely fails so they need to continue to use expandos.
5947     var isIE = window.ActiveXObject ? true : false;
5948
5949     // this eval is stop the compressor from
5950     // renaming the variable to something shorter
5951     
5952     /** eval:var:batch */
5953     var batch = 30803; 
5954
5955     var key = 30803;
5956
5957     function nodupIEXml(cs){
5958         var d = ++key;
5959         cs[0].setAttribute("_nodup", d);
5960         var r = [cs[0]];
5961         for(var i = 1, len = cs.length; i < len; i++){
5962             var c = cs[i];
5963             if(!c.getAttribute("_nodup") != d){
5964                 c.setAttribute("_nodup", d);
5965                 r[r.length] = c;
5966             }
5967         }
5968         for(var i = 0, len = cs.length; i < len; i++){
5969             cs[i].removeAttribute("_nodup");
5970         }
5971         return r;
5972     }
5973
5974     function nodup(cs){
5975         if(!cs){
5976             return [];
5977         }
5978         var len = cs.length, c, i, r = cs, cj, ri = -1;
5979         if(!len || typeof cs.nodeType != "undefined" || len == 1){
5980             return cs;
5981         }
5982         if(isIE && typeof cs[0].selectSingleNode != "undefined"){
5983             return nodupIEXml(cs);
5984         }
5985         var d = ++key;
5986         cs[0]._nodup = d;
5987         for(i = 1; c = cs[i]; i++){
5988             if(c._nodup != d){
5989                 c._nodup = d;
5990             }else{
5991                 r = [];
5992                 for(var j = 0; j < i; j++){
5993                     r[++ri] = cs[j];
5994                 }
5995                 for(j = i+1; cj = cs[j]; j++){
5996                     if(cj._nodup != d){
5997                         cj._nodup = d;
5998                         r[++ri] = cj;
5999                     }
6000                 }
6001                 return r;
6002             }
6003         }
6004         return r;
6005     }
6006
6007     function quickDiffIEXml(c1, c2){
6008         var d = ++key;
6009         for(var i = 0, len = c1.length; i < len; i++){
6010             c1[i].setAttribute("_qdiff", d);
6011         }
6012         var r = [];
6013         for(var i = 0, len = c2.length; i < len; i++){
6014             if(c2[i].getAttribute("_qdiff") != d){
6015                 r[r.length] = c2[i];
6016             }
6017         }
6018         for(var i = 0, len = c1.length; i < len; i++){
6019            c1[i].removeAttribute("_qdiff");
6020         }
6021         return r;
6022     }
6023
6024     function quickDiff(c1, c2){
6025         var len1 = c1.length;
6026         if(!len1){
6027             return c2;
6028         }
6029         if(isIE && c1[0].selectSingleNode){
6030             return quickDiffIEXml(c1, c2);
6031         }
6032         var d = ++key;
6033         for(var i = 0; i < len1; i++){
6034             c1[i]._qdiff = d;
6035         }
6036         var r = [];
6037         for(var i = 0, len = c2.length; i < len; i++){
6038             if(c2[i]._qdiff != d){
6039                 r[r.length] = c2[i];
6040             }
6041         }
6042         return r;
6043     }
6044
6045     function quickId(ns, mode, root, id){
6046         if(ns == root){
6047            var d = root.ownerDocument || root;
6048            return d.getElementById(id);
6049         }
6050         ns = getNodes(ns, mode, "*");
6051         return byId(ns, null, id);
6052     }
6053
6054     return {
6055         getStyle : function(el, name){
6056             return Roo.fly(el).getStyle(name);
6057         },
6058         /**
6059          * Compiles a selector/xpath query into a reusable function. The returned function
6060          * takes one parameter "root" (optional), which is the context node from where the query should start.
6061          * @param {String} selector The selector/xpath query
6062          * @param {String} type (optional) Either "select" (the default) or "simple" for a simple selector match
6063          * @return {Function}
6064          */
6065         compile : function(path, type){
6066             type = type || "select";
6067             
6068             var fn = ["var f = function(root){\n var mode; ++batch; var n = root || document;\n"];
6069             var q = path, mode, lq;
6070             var tk = Roo.DomQuery.matchers;
6071             var tklen = tk.length;
6072             var mm;
6073
6074             // accept leading mode switch
6075             var lmode = q.match(modeRe);
6076             if(lmode && lmode[1]){
6077                 fn[fn.length] = 'mode="'+lmode[1].replace(trimRe, "")+'";';
6078                 q = q.replace(lmode[1], "");
6079             }
6080             // strip leading slashes
6081             while(path.substr(0, 1)=="/"){
6082                 path = path.substr(1);
6083             }
6084
6085             while(q && lq != q){
6086                 lq = q;
6087                 var tm = q.match(tagTokenRe);
6088                 if(type == "select"){
6089                     if(tm){
6090                         if(tm[1] == "#"){
6091                             fn[fn.length] = 'n = quickId(n, mode, root, "'+tm[2]+'");';
6092                         }else{
6093                             fn[fn.length] = 'n = getNodes(n, mode, "'+tm[2]+'");';
6094                         }
6095                         q = q.replace(tm[0], "");
6096                     }else if(q.substr(0, 1) != '@'){
6097                         fn[fn.length] = 'n = getNodes(n, mode, "*");';
6098                     }
6099                 }else{
6100                     if(tm){
6101                         if(tm[1] == "#"){
6102                             fn[fn.length] = 'n = byId(n, null, "'+tm[2]+'");';
6103                         }else{
6104                             fn[fn.length] = 'n = byTag(n, "'+tm[2]+'");';
6105                         }
6106                         q = q.replace(tm[0], "");
6107                     }
6108                 }
6109                 while(!(mm = q.match(modeRe))){
6110                     var matched = false;
6111                     for(var j = 0; j < tklen; j++){
6112                         var t = tk[j];
6113                         var m = q.match(t.re);
6114                         if(m){
6115                             fn[fn.length] = t.select.replace(tplRe, function(x, i){
6116                                                     return m[i];
6117                                                 });
6118                             q = q.replace(m[0], "");
6119                             matched = true;
6120                             break;
6121                         }
6122                     }
6123                     // prevent infinite loop on bad selector
6124                     if(!matched){
6125                         throw 'Error parsing selector, parsing failed at "' + q + '"';
6126                     }
6127                 }
6128                 if(mm[1]){
6129                     fn[fn.length] = 'mode="'+mm[1].replace(trimRe, "")+'";';
6130                     q = q.replace(mm[1], "");
6131                 }
6132             }
6133             fn[fn.length] = "return nodup(n);\n}";
6134             
6135              /** 
6136               * list of variables that need from compression as they are used by eval.
6137              *  eval:var:batch 
6138              *  eval:var:nodup
6139              *  eval:var:byTag
6140              *  eval:var:ById
6141              *  eval:var:getNodes
6142              *  eval:var:quickId
6143              *  eval:var:mode
6144              *  eval:var:root
6145              *  eval:var:n
6146              *  eval:var:byClassName
6147              *  eval:var:byPseudo
6148              *  eval:var:byAttribute
6149              *  eval:var:attrValue
6150              * 
6151              **/ 
6152             eval(fn.join(""));
6153             return f;
6154         },
6155
6156         /**
6157          * Selects a group of elements.
6158          * @param {String} selector The selector/xpath query (can be a comma separated list of selectors)
6159          * @param {Node} root (optional) The start of the query (defaults to document).
6160          * @return {Array}
6161          */
6162         select : function(path, root, type){
6163             if(!root || root == document){
6164                 root = document;
6165             }
6166             if(typeof root == "string"){
6167                 root = document.getElementById(root);
6168             }
6169             var paths = path.split(",");
6170             var results = [];
6171             for(var i = 0, len = paths.length; i < len; i++){
6172                 var p = paths[i].replace(trimRe, "");
6173                 if(!cache[p]){
6174                     cache[p] = Roo.DomQuery.compile(p);
6175                     if(!cache[p]){
6176                         throw p + " is not a valid selector";
6177                     }
6178                 }
6179                 var result = cache[p](root);
6180                 if(result && result != document){
6181                     results = results.concat(result);
6182                 }
6183             }
6184             if(paths.length > 1){
6185                 return nodup(results);
6186             }
6187             return results;
6188         },
6189
6190         /**
6191          * Selects a single element.
6192          * @param {String} selector The selector/xpath query
6193          * @param {Node} root (optional) The start of the query (defaults to document).
6194          * @return {Element}
6195          */
6196         selectNode : function(path, root){
6197             return Roo.DomQuery.select(path, root)[0];
6198         },
6199
6200         /**
6201          * Selects the value of a node, optionally replacing null with the defaultValue.
6202          * @param {String} selector The selector/xpath query
6203          * @param {Node} root (optional) The start of the query (defaults to document).
6204          * @param {String} defaultValue
6205          */
6206         selectValue : function(path, root, defaultValue){
6207             path = path.replace(trimRe, "");
6208             if(!valueCache[path]){
6209                 valueCache[path] = Roo.DomQuery.compile(path, "select");
6210             }
6211             var n = valueCache[path](root);
6212             n = n[0] ? n[0] : n;
6213             var v = (n && n.firstChild ? n.firstChild.nodeValue : null);
6214             return ((v === null||v === undefined||v==='') ? defaultValue : v);
6215         },
6216
6217         /**
6218          * Selects the value of a node, parsing integers and floats.
6219          * @param {String} selector The selector/xpath query
6220          * @param {Node} root (optional) The start of the query (defaults to document).
6221          * @param {Number} defaultValue
6222          * @return {Number}
6223          */
6224         selectNumber : function(path, root, defaultValue){
6225             var v = Roo.DomQuery.selectValue(path, root, defaultValue || 0);
6226             return parseFloat(v);
6227         },
6228
6229         /**
6230          * Returns true if the passed element(s) match the passed simple selector (e.g. div.some-class or span:first-child)
6231          * @param {String/HTMLElement/Array} el An element id, element or array of elements
6232          * @param {String} selector The simple selector to test
6233          * @return {Boolean}
6234          */
6235         is : function(el, ss){
6236             if(typeof el == "string"){
6237                 el = document.getElementById(el);
6238             }
6239             var isArray = (el instanceof Array);
6240             var result = Roo.DomQuery.filter(isArray ? el : [el], ss);
6241             return isArray ? (result.length == el.length) : (result.length > 0);
6242         },
6243
6244         /**
6245          * Filters an array of elements to only include matches of a simple selector (e.g. div.some-class or span:first-child)
6246          * @param {Array} el An array of elements to filter
6247          * @param {String} selector The simple selector to test
6248          * @param {Boolean} nonMatches If true, it returns the elements that DON'T match
6249          * the selector instead of the ones that match
6250          * @return {Array}
6251          */
6252         filter : function(els, ss, nonMatches){
6253             ss = ss.replace(trimRe, "");
6254             if(!simpleCache[ss]){
6255                 simpleCache[ss] = Roo.DomQuery.compile(ss, "simple");
6256             }
6257             var result = simpleCache[ss](els);
6258             return nonMatches ? quickDiff(result, els) : result;
6259         },
6260
6261         /**
6262          * Collection of matching regular expressions and code snippets.
6263          */
6264         matchers : [{
6265                 re: /^\.([\w-]+)/,
6266                 select: 'n = byClassName(n, null, " {1} ");'
6267             }, {
6268                 re: /^\:([\w-]+)(?:\(((?:[^\s>\/]*|.*?))\))?/,
6269                 select: 'n = byPseudo(n, "{1}", "{2}");'
6270             },{
6271                 re: /^(?:([\[\{])(?:@)?([\w-]+)\s?(?:(=|.=)\s?['"]?(.*?)["']?)?[\]\}])/,
6272                 select: 'n = byAttribute(n, "{2}", "{4}", "{3}", "{1}");'
6273             }, {
6274                 re: /^#([\w-]+)/,
6275                 select: 'n = byId(n, null, "{1}");'
6276             },{
6277                 re: /^@([\w-]+)/,
6278                 select: 'return {firstChild:{nodeValue:attrValue(n, "{1}")}};'
6279             }
6280         ],
6281
6282         /**
6283          * Collection of operator comparison functions. The default operators are =, !=, ^=, $=, *=, %=, |= and ~=.
6284          * 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;.
6285          */
6286         operators : {
6287             "=" : function(a, v){
6288                 return a == v;
6289             },
6290             "!=" : function(a, v){
6291                 return a != v;
6292             },
6293             "^=" : function(a, v){
6294                 return a && a.substr(0, v.length) == v;
6295             },
6296             "$=" : function(a, v){
6297                 return a && a.substr(a.length-v.length) == v;
6298             },
6299             "*=" : function(a, v){
6300                 return a && a.indexOf(v) !== -1;
6301             },
6302             "%=" : function(a, v){
6303                 return (a % v) == 0;
6304             },
6305             "|=" : function(a, v){
6306                 return a && (a == v || a.substr(0, v.length+1) == v+'-');
6307             },
6308             "~=" : function(a, v){
6309                 return a && (' '+a+' ').indexOf(' '+v+' ') != -1;
6310             }
6311         },
6312
6313         /**
6314          * Collection of "pseudo class" processors. Each processor is passed the current nodeset (array)
6315          * and the argument (if any) supplied in the selector.
6316          */
6317         pseudos : {
6318             "first-child" : function(c){
6319                 var r = [], ri = -1, n;
6320                 for(var i = 0, ci; ci = n = c[i]; i++){
6321                     while((n = n.previousSibling) && n.nodeType != 1);
6322                     if(!n){
6323                         r[++ri] = ci;
6324                     }
6325                 }
6326                 return r;
6327             },
6328
6329             "last-child" : function(c){
6330                 var r = [], ri = -1, n;
6331                 for(var i = 0, ci; ci = n = c[i]; i++){
6332                     while((n = n.nextSibling) && n.nodeType != 1);
6333                     if(!n){
6334                         r[++ri] = ci;
6335                     }
6336                 }
6337                 return r;
6338             },
6339
6340             "nth-child" : function(c, a) {
6341                 var r = [], ri = -1;
6342                 var m = nthRe.exec(a == "even" && "2n" || a == "odd" && "2n+1" || !nthRe2.test(a) && "n+" + a || a);
6343                 var f = (m[1] || 1) - 0, l = m[2] - 0;
6344                 for(var i = 0, n; n = c[i]; i++){
6345                     var pn = n.parentNode;
6346                     if (batch != pn._batch) {
6347                         var j = 0;
6348                         for(var cn = pn.firstChild; cn; cn = cn.nextSibling){
6349                             if(cn.nodeType == 1){
6350                                cn.nodeIndex = ++j;
6351                             }
6352                         }
6353                         pn._batch = batch;
6354                     }
6355                     if (f == 1) {
6356                         if (l == 0 || n.nodeIndex == l){
6357                             r[++ri] = n;
6358                         }
6359                     } else if ((n.nodeIndex + l) % f == 0){
6360                         r[++ri] = n;
6361                     }
6362                 }
6363
6364                 return r;
6365             },
6366
6367             "only-child" : function(c){
6368                 var r = [], ri = -1;;
6369                 for(var i = 0, ci; ci = c[i]; i++){
6370                     if(!prev(ci) && !next(ci)){
6371                         r[++ri] = ci;
6372                     }
6373                 }
6374                 return r;
6375             },
6376
6377             "empty" : function(c){
6378                 var r = [], ri = -1;
6379                 for(var i = 0, ci; ci = c[i]; i++){
6380                     var cns = ci.childNodes, j = 0, cn, empty = true;
6381                     while(cn = cns[j]){
6382                         ++j;
6383                         if(cn.nodeType == 1 || cn.nodeType == 3){
6384                             empty = false;
6385                             break;
6386                         }
6387                     }
6388                     if(empty){
6389                         r[++ri] = ci;
6390                     }
6391                 }
6392                 return r;
6393             },
6394
6395             "contains" : function(c, v){
6396                 var r = [], ri = -1;
6397                 for(var i = 0, ci; ci = c[i]; i++){
6398                     if((ci.textContent||ci.innerText||'').indexOf(v) != -1){
6399                         r[++ri] = ci;
6400                     }
6401                 }
6402                 return r;
6403             },
6404
6405             "nodeValue" : function(c, v){
6406                 var r = [], ri = -1;
6407                 for(var i = 0, ci; ci = c[i]; i++){
6408                     if(ci.firstChild && ci.firstChild.nodeValue == v){
6409                         r[++ri] = ci;
6410                     }
6411                 }
6412                 return r;
6413             },
6414
6415             "checked" : function(c){
6416                 var r = [], ri = -1;
6417                 for(var i = 0, ci; ci = c[i]; i++){
6418                     if(ci.checked == true){
6419                         r[++ri] = ci;
6420                     }
6421                 }
6422                 return r;
6423             },
6424
6425             "not" : function(c, ss){
6426                 return Roo.DomQuery.filter(c, ss, true);
6427             },
6428
6429             "odd" : function(c){
6430                 return this["nth-child"](c, "odd");
6431             },
6432
6433             "even" : function(c){
6434                 return this["nth-child"](c, "even");
6435             },
6436
6437             "nth" : function(c, a){
6438                 return c[a-1] || [];
6439             },
6440
6441             "first" : function(c){
6442                 return c[0] || [];
6443             },
6444
6445             "last" : function(c){
6446                 return c[c.length-1] || [];
6447             },
6448
6449             "has" : function(c, ss){
6450                 var s = Roo.DomQuery.select;
6451                 var r = [], ri = -1;
6452                 for(var i = 0, ci; ci = c[i]; i++){
6453                     if(s(ss, ci).length > 0){
6454                         r[++ri] = ci;
6455                     }
6456                 }
6457                 return r;
6458             },
6459
6460             "next" : function(c, ss){
6461                 var is = Roo.DomQuery.is;
6462                 var r = [], ri = -1;
6463                 for(var i = 0, ci; ci = c[i]; i++){
6464                     var n = next(ci);
6465                     if(n && is(n, ss)){
6466                         r[++ri] = ci;
6467                     }
6468                 }
6469                 return r;
6470             },
6471
6472             "prev" : function(c, ss){
6473                 var is = Roo.DomQuery.is;
6474                 var r = [], ri = -1;
6475                 for(var i = 0, ci; ci = c[i]; i++){
6476                     var n = prev(ci);
6477                     if(n && is(n, ss)){
6478                         r[++ri] = ci;
6479                     }
6480                 }
6481                 return r;
6482             }
6483         }
6484     };
6485 }();
6486
6487 /**
6488  * Selects an array of DOM nodes by CSS/XPath selector. Shorthand of {@link Roo.DomQuery#select}
6489  * @param {String} path The selector/xpath query
6490  * @param {Node} root (optional) The start of the query (defaults to document).
6491  * @return {Array}
6492  * @member Roo
6493  * @method query
6494  */
6495 Roo.query = Roo.DomQuery.select;
6496 /*
6497  * Based on:
6498  * Ext JS Library 1.1.1
6499  * Copyright(c) 2006-2007, Ext JS, LLC.
6500  *
6501  * Originally Released Under LGPL - original licence link has changed is not relivant.
6502  *
6503  * Fork - LGPL
6504  * <script type="text/javascript">
6505  */
6506
6507 /**
6508  * @class Roo.util.Observable
6509  * Base class that provides a common interface for publishing events. Subclasses are expected to
6510  * to have a property "events" with all the events defined.<br>
6511  * For example:
6512  * <pre><code>
6513  Employee = function(name){
6514     this.name = name;
6515     this.addEvents({
6516         "fired" : true,
6517         "quit" : true
6518     });
6519  }
6520  Roo.extend(Employee, Roo.util.Observable);
6521 </code></pre>
6522  * @param {Object} config properties to use (incuding events / listeners)
6523  */
6524
6525 Roo.util.Observable = function(cfg){
6526     
6527     cfg = cfg|| {};
6528     this.addEvents(cfg.events || {});
6529     if (cfg.events) {
6530         delete cfg.events; // make sure
6531     }
6532      
6533     Roo.apply(this, cfg);
6534     
6535     if(this.listeners){
6536         this.on(this.listeners);
6537         delete this.listeners;
6538     }
6539 };
6540 Roo.util.Observable.prototype = {
6541     /** 
6542  * @cfg {Object} listeners  list of events and functions to call for this object, 
6543  * For example :
6544  * <pre><code>
6545     listeners :  { 
6546        'click' : function(e) {
6547            ..... 
6548         } ,
6549         .... 
6550     } 
6551   </code></pre>
6552  */
6553     
6554     
6555     /**
6556      * Fires the specified event with the passed parameters (minus the event name).
6557      * @param {String} eventName
6558      * @param {Object...} args Variable number of parameters are passed to handlers
6559      * @return {Boolean} returns false if any of the handlers return false otherwise it returns true
6560      */
6561     fireEvent : function(){
6562         var ce = this.events[arguments[0].toLowerCase()];
6563         if(typeof ce == "object"){
6564             return ce.fire.apply(ce, Array.prototype.slice.call(arguments, 1));
6565         }else{
6566             return true;
6567         }
6568     },
6569
6570     // private
6571     filterOptRe : /^(?:scope|delay|buffer|single)$/,
6572
6573     /**
6574      * Appends an event handler to this component
6575      * @param {String}   eventName The type of event to listen for
6576      * @param {Function} handler The method the event invokes
6577      * @param {Object}   scope (optional) The scope in which to execute the handler
6578      * function. The handler function's "this" context.
6579      * @param {Object}   options (optional) An object containing handler configuration
6580      * properties. This may contain any of the following properties:<ul>
6581      * <li>scope {Object} The scope in which to execute the handler function. The handler function's "this" context.</li>
6582      * <li>delay {Number} The number of milliseconds to delay the invocation of the handler after te event fires.</li>
6583      * <li>single {Boolean} True to add a handler to handle just the next firing of the event, and then remove itself.</li>
6584      * <li>buffer {Number} Causes the handler to be scheduled to run in an {@link Roo.util.DelayedTask} delayed
6585      * by the specified number of milliseconds. If the event fires again within that time, the original
6586      * handler is <em>not</em> invoked, but the new handler is scheduled in its place.</li>
6587      * </ul><br>
6588      * <p>
6589      * <b>Combining Options</b><br>
6590      * Using the options argument, it is possible to combine different types of listeners:<br>
6591      * <br>
6592      * A normalized, delayed, one-time listener that auto stops the event and passes a custom argument (forumId)
6593                 <pre><code>
6594                 el.on('click', this.onClick, this, {
6595                         single: true,
6596                 delay: 100,
6597                 forumId: 4
6598                 });
6599                 </code></pre>
6600      * <p>
6601      * <b>Attaching multiple handlers in 1 call</b><br>
6602      * The method also allows for a single argument to be passed which is a config object containing properties
6603      * which specify multiple handlers.
6604      * <pre><code>
6605                 el.on({
6606                         'click': {
6607                         fn: this.onClick,
6608                         scope: this,
6609                         delay: 100
6610                 }, 
6611                 'mouseover': {
6612                         fn: this.onMouseOver,
6613                         scope: this
6614                 },
6615                 'mouseout': {
6616                         fn: this.onMouseOut,
6617                         scope: this
6618                 }
6619                 });
6620                 </code></pre>
6621      * <p>
6622      * Or a shorthand syntax which passes the same scope object to all handlers:
6623         <pre><code>
6624                 el.on({
6625                         'click': this.onClick,
6626                 'mouseover': this.onMouseOver,
6627                 'mouseout': this.onMouseOut,
6628                 scope: this
6629                 });
6630                 </code></pre>
6631      */
6632     addListener : function(eventName, fn, scope, o){
6633         if(typeof eventName == "object"){
6634             o = eventName;
6635             for(var e in o){
6636                 if(this.filterOptRe.test(e)){
6637                     continue;
6638                 }
6639                 if(typeof o[e] == "function"){
6640                     // shared options
6641                     this.addListener(e, o[e], o.scope,  o);
6642                 }else{
6643                     // individual options
6644                     this.addListener(e, o[e].fn, o[e].scope, o[e]);
6645                 }
6646             }
6647             return;
6648         }
6649         o = (!o || typeof o == "boolean") ? {} : o;
6650         eventName = eventName.toLowerCase();
6651         var ce = this.events[eventName] || true;
6652         if(typeof ce == "boolean"){
6653             ce = new Roo.util.Event(this, eventName);
6654             this.events[eventName] = ce;
6655         }
6656         ce.addListener(fn, scope, o);
6657     },
6658
6659     /**
6660      * Removes a listener
6661      * @param {String}   eventName     The type of event to listen for
6662      * @param {Function} handler        The handler to remove
6663      * @param {Object}   scope  (optional) The scope (this object) for the handler
6664      */
6665     removeListener : function(eventName, fn, scope){
6666         var ce = this.events[eventName.toLowerCase()];
6667         if(typeof ce == "object"){
6668             ce.removeListener(fn, scope);
6669         }
6670     },
6671
6672     /**
6673      * Removes all listeners for this object
6674      */
6675     purgeListeners : function(){
6676         for(var evt in this.events){
6677             if(typeof this.events[evt] == "object"){
6678                  this.events[evt].clearListeners();
6679             }
6680         }
6681     },
6682
6683     relayEvents : function(o, events){
6684         var createHandler = function(ename){
6685             return function(){
6686                  
6687                 return this.fireEvent.apply(this, Roo.combine(ename, Array.prototype.slice.call(arguments, 0)));
6688             };
6689         };
6690         for(var i = 0, len = events.length; i < len; i++){
6691             var ename = events[i];
6692             if(!this.events[ename]){
6693                 this.events[ename] = true;
6694             };
6695             o.on(ename, createHandler(ename), this);
6696         }
6697     },
6698
6699     /**
6700      * Used to define events on this Observable
6701      * @param {Object} object The object with the events defined
6702      */
6703     addEvents : function(o){
6704         if(!this.events){
6705             this.events = {};
6706         }
6707         Roo.applyIf(this.events, o);
6708     },
6709
6710     /**
6711      * Checks to see if this object has any listeners for a specified event
6712      * @param {String} eventName The name of the event to check for
6713      * @return {Boolean} True if the event is being listened for, else false
6714      */
6715     hasListener : function(eventName){
6716         var e = this.events[eventName];
6717         return typeof e == "object" && e.listeners.length > 0;
6718     }
6719 };
6720 /**
6721  * Appends an event handler to this element (shorthand for addListener)
6722  * @param {String}   eventName     The type of event to listen for
6723  * @param {Function} handler        The method the event invokes
6724  * @param {Object}   scope (optional) The scope in which to execute the handler
6725  * function. The handler function's "this" context.
6726  * @param {Object}   options  (optional)
6727  * @method
6728  */
6729 Roo.util.Observable.prototype.on = Roo.util.Observable.prototype.addListener;
6730 /**
6731  * Removes a listener (shorthand for removeListener)
6732  * @param {String}   eventName     The type of event to listen for
6733  * @param {Function} handler        The handler to remove
6734  * @param {Object}   scope  (optional) The scope (this object) for the handler
6735  * @method
6736  */
6737 Roo.util.Observable.prototype.un = Roo.util.Observable.prototype.removeListener;
6738
6739 /**
6740  * Starts capture on the specified Observable. All events will be passed
6741  * to the supplied function with the event name + standard signature of the event
6742  * <b>before</b> the event is fired. If the supplied function returns false,
6743  * the event will not fire.
6744  * @param {Observable} o The Observable to capture
6745  * @param {Function} fn The function to call
6746  * @param {Object} scope (optional) The scope (this object) for the fn
6747  * @static
6748  */
6749 Roo.util.Observable.capture = function(o, fn, scope){
6750     o.fireEvent = o.fireEvent.createInterceptor(fn, scope);
6751 };
6752
6753 /**
6754  * Removes <b>all</b> added captures from the Observable.
6755  * @param {Observable} o The Observable to release
6756  * @static
6757  */
6758 Roo.util.Observable.releaseCapture = function(o){
6759     o.fireEvent = Roo.util.Observable.prototype.fireEvent;
6760 };
6761
6762 (function(){
6763
6764     var createBuffered = function(h, o, scope){
6765         var task = new Roo.util.DelayedTask();
6766         return function(){
6767             task.delay(o.buffer, h, scope, Array.prototype.slice.call(arguments, 0));
6768         };
6769     };
6770
6771     var createSingle = function(h, e, fn, scope){
6772         return function(){
6773             e.removeListener(fn, scope);
6774             return h.apply(scope, arguments);
6775         };
6776     };
6777
6778     var createDelayed = function(h, o, scope){
6779         return function(){
6780             var args = Array.prototype.slice.call(arguments, 0);
6781             setTimeout(function(){
6782                 h.apply(scope, args);
6783             }, o.delay || 10);
6784         };
6785     };
6786
6787     Roo.util.Event = function(obj, name){
6788         this.name = name;
6789         this.obj = obj;
6790         this.listeners = [];
6791     };
6792
6793     Roo.util.Event.prototype = {
6794         addListener : function(fn, scope, options){
6795             var o = options || {};
6796             scope = scope || this.obj;
6797             if(!this.isListening(fn, scope)){
6798                 var l = {fn: fn, scope: scope, options: o};
6799                 var h = fn;
6800                 if(o.delay){
6801                     h = createDelayed(h, o, scope);
6802                 }
6803                 if(o.single){
6804                     h = createSingle(h, this, fn, scope);
6805                 }
6806                 if(o.buffer){
6807                     h = createBuffered(h, o, scope);
6808                 }
6809                 l.fireFn = h;
6810                 if(!this.firing){ // if we are currently firing this event, don't disturb the listener loop
6811                     this.listeners.push(l);
6812                 }else{
6813                     this.listeners = this.listeners.slice(0);
6814                     this.listeners.push(l);
6815                 }
6816             }
6817         },
6818
6819         findListener : function(fn, scope){
6820             scope = scope || this.obj;
6821             var ls = this.listeners;
6822             for(var i = 0, len = ls.length; i < len; i++){
6823                 var l = ls[i];
6824                 if(l.fn == fn && l.scope == scope){
6825                     return i;
6826                 }
6827             }
6828             return -1;
6829         },
6830
6831         isListening : function(fn, scope){
6832             return this.findListener(fn, scope) != -1;
6833         },
6834
6835         removeListener : function(fn, scope){
6836             var index;
6837             if((index = this.findListener(fn, scope)) != -1){
6838                 if(!this.firing){
6839                     this.listeners.splice(index, 1);
6840                 }else{
6841                     this.listeners = this.listeners.slice(0);
6842                     this.listeners.splice(index, 1);
6843                 }
6844                 return true;
6845             }
6846             return false;
6847         },
6848
6849         clearListeners : function(){
6850             this.listeners = [];
6851         },
6852
6853         fire : function(){
6854             var ls = this.listeners, scope, len = ls.length;
6855             if(len > 0){
6856                 this.firing = true;
6857                 var args = Array.prototype.slice.call(arguments, 0);                
6858                 for(var i = 0; i < len; i++){
6859                     var l = ls[i];
6860                     if(l.fireFn.apply(l.scope||this.obj||window, args) === false){
6861                         this.firing = false;
6862                         return false;
6863                     }
6864                 }
6865                 this.firing = false;
6866             }
6867             return true;
6868         }
6869     };
6870 })();/*
6871  * RooJS Library 
6872  * Copyright(c) 2007-2017, Roo J Solutions Ltd
6873  *
6874  * Licence LGPL 
6875  *
6876  */
6877  
6878 /**
6879  * @class Roo.Document
6880  * @extends Roo.util.Observable
6881  * This is a convience class to wrap up the main document loading code.. , rather than adding Roo.onReady(......)
6882  * 
6883  * @param {Object} config the methods and properties of the 'base' class for the application.
6884  * 
6885  *  Generic Page handler - implement this to start your app..
6886  * 
6887  * eg.
6888  *  MyProject = new Roo.Document({
6889         events : {
6890             'load' : true // your events..
6891         },
6892         listeners : {
6893             'ready' : function() {
6894                 // fired on Roo.onReady()
6895             }
6896         }
6897  * 
6898  */
6899 Roo.Document = function(cfg) {
6900      
6901     this.addEvents({ 
6902         'ready' : true
6903     });
6904     Roo.util.Observable.call(this,cfg);
6905     
6906     var _this = this;
6907     
6908     Roo.onReady(function() {
6909         _this.fireEvent('ready');
6910     },null,false);
6911     
6912     
6913 }
6914
6915 Roo.extend(Roo.Document, Roo.util.Observable, {});/*
6916  * Based on:
6917  * Ext JS Library 1.1.1
6918  * Copyright(c) 2006-2007, Ext JS, LLC.
6919  *
6920  * Originally Released Under LGPL - original licence link has changed is not relivant.
6921  *
6922  * Fork - LGPL
6923  * <script type="text/javascript">
6924  */
6925
6926 /**
6927  * @class Roo.EventManager
6928  * Registers event handlers that want to receive a normalized EventObject instead of the standard browser event and provides 
6929  * several useful events directly.
6930  * See {@link Roo.EventObject} for more details on normalized event objects.
6931  * @static
6932  */
6933 Roo.EventManager = function(){
6934     var docReadyEvent, docReadyProcId, docReadyState = false;
6935     var resizeEvent, resizeTask, textEvent, textSize;
6936     var E = Roo.lib.Event;
6937     var D = Roo.lib.Dom;
6938
6939     
6940     
6941
6942     var fireDocReady = function(){
6943         if(!docReadyState){
6944             docReadyState = true;
6945             Roo.isReady = true;
6946             if(docReadyProcId){
6947                 clearInterval(docReadyProcId);
6948             }
6949             if(Roo.isGecko || Roo.isOpera) {
6950                 document.removeEventListener("DOMContentLoaded", fireDocReady, false);
6951             }
6952             if(Roo.isIE){
6953                 var defer = document.getElementById("ie-deferred-loader");
6954                 if(defer){
6955                     defer.onreadystatechange = null;
6956                     defer.parentNode.removeChild(defer);
6957                 }
6958             }
6959             if(docReadyEvent){
6960                 docReadyEvent.fire();
6961                 docReadyEvent.clearListeners();
6962             }
6963         }
6964     };
6965     
6966     var initDocReady = function(){
6967         docReadyEvent = new Roo.util.Event();
6968         if(Roo.isGecko || Roo.isOpera) {
6969             document.addEventListener("DOMContentLoaded", fireDocReady, false);
6970         }else if(Roo.isIE){
6971             document.write("<s"+'cript id="ie-deferred-loader" defer="defer" src="/'+'/:"></s'+"cript>");
6972             var defer = document.getElementById("ie-deferred-loader");
6973             defer.onreadystatechange = function(){
6974                 if(this.readyState == "complete"){
6975                     fireDocReady();
6976                 }
6977             };
6978         }else if(Roo.isSafari){ 
6979             docReadyProcId = setInterval(function(){
6980                 var rs = document.readyState;
6981                 if(rs == "complete") {
6982                     fireDocReady();     
6983                  }
6984             }, 10);
6985         }
6986         // no matter what, make sure it fires on load
6987         E.on(window, "load", fireDocReady);
6988     };
6989
6990     var createBuffered = function(h, o){
6991         var task = new Roo.util.DelayedTask(h);
6992         return function(e){
6993             // create new event object impl so new events don't wipe out properties
6994             e = new Roo.EventObjectImpl(e);
6995             task.delay(o.buffer, h, null, [e]);
6996         };
6997     };
6998
6999     var createSingle = function(h, el, ename, fn){
7000         return function(e){
7001             Roo.EventManager.removeListener(el, ename, fn);
7002             h(e);
7003         };
7004     };
7005
7006     var createDelayed = function(h, o){
7007         return function(e){
7008             // create new event object impl so new events don't wipe out properties
7009             e = new Roo.EventObjectImpl(e);
7010             setTimeout(function(){
7011                 h(e);
7012             }, o.delay || 10);
7013         };
7014     };
7015     var transitionEndVal = false;
7016     
7017     var transitionEnd = function()
7018     {
7019         if (transitionEndVal) {
7020             return transitionEndVal;
7021         }
7022         var el = document.createElement('div');
7023
7024         var transEndEventNames = {
7025             WebkitTransition : 'webkitTransitionEnd',
7026             MozTransition    : 'transitionend',
7027             OTransition      : 'oTransitionEnd otransitionend',
7028             transition       : 'transitionend'
7029         };
7030     
7031         for (var name in transEndEventNames) {
7032             if (el.style[name] !== undefined) {
7033                 transitionEndVal = transEndEventNames[name];
7034                 return  transitionEndVal ;
7035             }
7036         }
7037     }
7038     
7039   
7040
7041     var listen = function(element, ename, opt, fn, scope)
7042     {
7043         var o = (!opt || typeof opt == "boolean") ? {} : opt;
7044         fn = fn || o.fn; scope = scope || o.scope;
7045         var el = Roo.getDom(element);
7046         
7047         
7048         if(!el){
7049             throw "Error listening for \"" + ename + '\". Element "' + element + '" doesn\'t exist.';
7050         }
7051         
7052         if (ename == 'transitionend') {
7053             ename = transitionEnd();
7054         }
7055         var h = function(e){
7056             e = Roo.EventObject.setEvent(e);
7057             var t;
7058             if(o.delegate){
7059                 t = e.getTarget(o.delegate, el);
7060                 if(!t){
7061                     return;
7062                 }
7063             }else{
7064                 t = e.target;
7065             }
7066             if(o.stopEvent === true){
7067                 e.stopEvent();
7068             }
7069             if(o.preventDefault === true){
7070                e.preventDefault();
7071             }
7072             if(o.stopPropagation === true){
7073                 e.stopPropagation();
7074             }
7075
7076             if(o.normalized === false){
7077                 e = e.browserEvent;
7078             }
7079
7080             fn.call(scope || el, e, t, o);
7081         };
7082         if(o.delay){
7083             h = createDelayed(h, o);
7084         }
7085         if(o.single){
7086             h = createSingle(h, el, ename, fn);
7087         }
7088         if(o.buffer){
7089             h = createBuffered(h, o);
7090         }
7091         
7092         fn._handlers = fn._handlers || [];
7093         
7094         
7095         fn._handlers.push([Roo.id(el), ename, h]);
7096         
7097         
7098          
7099         E.on(el, ename, h); // this adds the actuall listener to the object..
7100         
7101         
7102         if(ename == "mousewheel" && el.addEventListener){ // workaround for jQuery
7103             el.addEventListener("DOMMouseScroll", h, false);
7104             E.on(window, 'unload', function(){
7105                 el.removeEventListener("DOMMouseScroll", h, false);
7106             });
7107         }
7108         if(ename == "mousedown" && el == document){ // fix stopped mousedowns on the document
7109             Roo.EventManager.stoppedMouseDownEvent.addListener(h);
7110         }
7111         return h;
7112     };
7113
7114     var stopListening = function(el, ename, fn){
7115         var id = Roo.id(el), hds = fn._handlers, hd = fn;
7116         if(hds){
7117             for(var i = 0, len = hds.length; i < len; i++){
7118                 var h = hds[i];
7119                 if(h[0] == id && h[1] == ename){
7120                     hd = h[2];
7121                     hds.splice(i, 1);
7122                     break;
7123                 }
7124             }
7125         }
7126         E.un(el, ename, hd);
7127         el = Roo.getDom(el);
7128         if(ename == "mousewheel" && el.addEventListener){
7129             el.removeEventListener("DOMMouseScroll", hd, false);
7130         }
7131         if(ename == "mousedown" && el == document){ // fix stopped mousedowns on the document
7132             Roo.EventManager.stoppedMouseDownEvent.removeListener(hd);
7133         }
7134     };
7135
7136     var propRe = /^(?:scope|delay|buffer|single|stopEvent|preventDefault|stopPropagation|normalized|args|delegate)$/;
7137     
7138     var pub = {
7139         
7140         
7141         /** 
7142          * Fix for doc tools
7143          * @scope Roo.EventManager
7144          */
7145         
7146         
7147         /** 
7148          * This is no longer needed and is deprecated. Places a simple wrapper around an event handler to override the browser event
7149          * object with a Roo.EventObject
7150          * @param {Function} fn        The method the event invokes
7151          * @param {Object}   scope    An object that becomes the scope of the handler
7152          * @param {boolean}  override If true, the obj passed in becomes
7153          *                             the execution scope of the listener
7154          * @return {Function} The wrapped function
7155          * @deprecated
7156          */
7157         wrap : function(fn, scope, override){
7158             return function(e){
7159                 Roo.EventObject.setEvent(e);
7160                 fn.call(override ? scope || window : window, Roo.EventObject, scope);
7161             };
7162         },
7163         
7164         /**
7165      * Appends an event handler to an element (shorthand for addListener)
7166      * @param {String/HTMLElement}   element        The html element or id to assign the
7167      * @param {String}   eventName The type of event to listen for
7168      * @param {Function} handler The method the event invokes
7169      * @param {Object}   scope (optional) The scope in which to execute the handler
7170      * function. The handler function's "this" context.
7171      * @param {Object}   options (optional) An object containing handler configuration
7172      * properties. This may contain any of the following properties:<ul>
7173      * <li>scope {Object} The scope in which to execute the handler function. The handler function's "this" context.</li>
7174      * <li>delegate {String} A simple selector to filter the target or look for a descendant of the target</li>
7175      * <li>stopEvent {Boolean} True to stop the event. That is stop propagation, and prevent the default action.</li>
7176      * <li>preventDefault {Boolean} True to prevent the default action</li>
7177      * <li>stopPropagation {Boolean} True to prevent event propagation</li>
7178      * <li>normalized {Boolean} False to pass a browser event to the handler function instead of an Roo.EventObject</li>
7179      * <li>delay {Number} The number of milliseconds to delay the invocation of the handler after te event fires.</li>
7180      * <li>single {Boolean} True to add a handler to handle just the next firing of the event, and then remove itself.</li>
7181      * <li>buffer {Number} Causes the handler to be scheduled to run in an {@link Roo.util.DelayedTask} delayed
7182      * by the specified number of milliseconds. If the event fires again within that time, the original
7183      * handler is <em>not</em> invoked, but the new handler is scheduled in its place.</li>
7184      * </ul><br>
7185      * <p>
7186      * <b>Combining Options</b><br>
7187      * Using the options argument, it is possible to combine different types of listeners:<br>
7188      * <br>
7189      * A normalized, delayed, one-time listener that auto stops the event and passes a custom argument (forumId)<div style="margin: 5px 20px 20px;">
7190      * Code:<pre><code>
7191 el.on('click', this.onClick, this, {
7192     single: true,
7193     delay: 100,
7194     stopEvent : true,
7195     forumId: 4
7196 });</code></pre>
7197      * <p>
7198      * <b>Attaching multiple handlers in 1 call</b><br>
7199       * The method also allows for a single argument to be passed which is a config object containing properties
7200      * which specify multiple handlers.
7201      * <p>
7202      * Code:<pre><code>
7203 el.on({
7204     'click' : {
7205         fn: this.onClick
7206         scope: this,
7207         delay: 100
7208     },
7209     'mouseover' : {
7210         fn: this.onMouseOver
7211         scope: this
7212     },
7213     'mouseout' : {
7214         fn: this.onMouseOut
7215         scope: this
7216     }
7217 });</code></pre>
7218      * <p>
7219      * Or a shorthand syntax:<br>
7220      * Code:<pre><code>
7221 el.on({
7222     'click' : this.onClick,
7223     'mouseover' : this.onMouseOver,
7224     'mouseout' : this.onMouseOut
7225     scope: this
7226 });</code></pre>
7227      */
7228         addListener : function(element, eventName, fn, scope, options){
7229             if(typeof eventName == "object"){
7230                 var o = eventName;
7231                 for(var e in o){
7232                     if(propRe.test(e)){
7233                         continue;
7234                     }
7235                     if(typeof o[e] == "function"){
7236                         // shared options
7237                         listen(element, e, o, o[e], o.scope);
7238                     }else{
7239                         // individual options
7240                         listen(element, e, o[e]);
7241                     }
7242                 }
7243                 return;
7244             }
7245             return listen(element, eventName, options, fn, scope);
7246         },
7247         
7248         /**
7249          * Removes an event handler
7250          *
7251          * @param {String/HTMLElement}   element        The id or html element to remove the 
7252          *                             event from
7253          * @param {String}   eventName     The type of event
7254          * @param {Function} fn
7255          * @return {Boolean} True if a listener was actually removed
7256          */
7257         removeListener : function(element, eventName, fn){
7258             return stopListening(element, eventName, fn);
7259         },
7260         
7261         /**
7262          * Fires when the document is ready (before onload and before images are loaded). Can be 
7263          * accessed shorthanded Roo.onReady().
7264          * @param {Function} fn        The method the event invokes
7265          * @param {Object}   scope    An  object that becomes the scope of the handler
7266          * @param {boolean}  options
7267          */
7268         onDocumentReady : function(fn, scope, options){
7269             if(docReadyState){ // if it already fired
7270                 docReadyEvent.addListener(fn, scope, options);
7271                 docReadyEvent.fire();
7272                 docReadyEvent.clearListeners();
7273                 return;
7274             }
7275             if(!docReadyEvent){
7276                 initDocReady();
7277             }
7278             docReadyEvent.addListener(fn, scope, options);
7279         },
7280         
7281         /**
7282          * Fires when the window is resized and provides resize event buffering (50 milliseconds), passes new viewport width and height to handlers.
7283          * @param {Function} fn        The method the event invokes
7284          * @param {Object}   scope    An object that becomes the scope of the handler
7285          * @param {boolean}  options
7286          */
7287         onWindowResize : function(fn, scope, options)
7288         {
7289             if(!resizeEvent){
7290                 resizeEvent = new Roo.util.Event();
7291                 resizeTask = new Roo.util.DelayedTask(function(){
7292                     resizeEvent.fire(D.getViewWidth(), D.getViewHeight());
7293                 });
7294                 E.on(window, "resize", function()
7295                 {
7296                     if (Roo.isIE) {
7297                         resizeTask.delay(50);
7298                     } else {
7299                         resizeEvent.fire(D.getViewWidth(), D.getViewHeight());
7300                     }
7301                 });
7302             }
7303             resizeEvent.addListener(fn, scope, options);
7304         },
7305
7306         /**
7307          * Fires when the user changes the active text size. Handler gets called with 2 params, the old size and the new size.
7308          * @param {Function} fn        The method the event invokes
7309          * @param {Object}   scope    An object that becomes the scope of the handler
7310          * @param {boolean}  options
7311          */
7312         onTextResize : function(fn, scope, options){
7313             if(!textEvent){
7314                 textEvent = new Roo.util.Event();
7315                 var textEl = new Roo.Element(document.createElement('div'));
7316                 textEl.dom.className = 'x-text-resize';
7317                 textEl.dom.innerHTML = 'X';
7318                 textEl.appendTo(document.body);
7319                 textSize = textEl.dom.offsetHeight;
7320                 setInterval(function(){
7321                     if(textEl.dom.offsetHeight != textSize){
7322                         textEvent.fire(textSize, textSize = textEl.dom.offsetHeight);
7323                     }
7324                 }, this.textResizeInterval);
7325             }
7326             textEvent.addListener(fn, scope, options);
7327         },
7328
7329         /**
7330          * Removes the passed window resize listener.
7331          * @param {Function} fn        The method the event invokes
7332          * @param {Object}   scope    The scope of handler
7333          */
7334         removeResizeListener : function(fn, scope){
7335             if(resizeEvent){
7336                 resizeEvent.removeListener(fn, scope);
7337             }
7338         },
7339
7340         // private
7341         fireResize : function(){
7342             if(resizeEvent){
7343                 resizeEvent.fire(D.getViewWidth(), D.getViewHeight());
7344             }   
7345         },
7346         /**
7347          * Url used for onDocumentReady with using SSL (defaults to Roo.SSL_SECURE_URL)
7348          */
7349         ieDeferSrc : false,
7350         /**
7351          * The frequency, in milliseconds, to check for text resize events (defaults to 50)
7352          */
7353         textResizeInterval : 50
7354     };
7355     
7356     /**
7357      * Fix for doc tools
7358      * @scopeAlias pub=Roo.EventManager
7359      */
7360     
7361      /**
7362      * Appends an event handler to an element (shorthand for addListener)
7363      * @param {String/HTMLElement}   element        The html element or id to assign the
7364      * @param {String}   eventName The type of event to listen for
7365      * @param {Function} handler The method the event invokes
7366      * @param {Object}   scope (optional) The scope in which to execute the handler
7367      * function. The handler function's "this" context.
7368      * @param {Object}   options (optional) An object containing handler configuration
7369      * properties. This may contain any of the following properties:<ul>
7370      * <li>scope {Object} The scope in which to execute the handler function. The handler function's "this" context.</li>
7371      * <li>delegate {String} A simple selector to filter the target or look for a descendant of the target</li>
7372      * <li>stopEvent {Boolean} True to stop the event. That is stop propagation, and prevent the default action.</li>
7373      * <li>preventDefault {Boolean} True to prevent the default action</li>
7374      * <li>stopPropagation {Boolean} True to prevent event propagation</li>
7375      * <li>normalized {Boolean} False to pass a browser event to the handler function instead of an Roo.EventObject</li>
7376      * <li>delay {Number} The number of milliseconds to delay the invocation of the handler after te event fires.</li>
7377      * <li>single {Boolean} True to add a handler to handle just the next firing of the event, and then remove itself.</li>
7378      * <li>buffer {Number} Causes the handler to be scheduled to run in an {@link Roo.util.DelayedTask} delayed
7379      * by the specified number of milliseconds. If the event fires again within that time, the original
7380      * handler is <em>not</em> invoked, but the new handler is scheduled in its place.</li>
7381      * </ul><br>
7382      * <p>
7383      * <b>Combining Options</b><br>
7384      * Using the options argument, it is possible to combine different types of listeners:<br>
7385      * <br>
7386      * A normalized, delayed, one-time listener that auto stops the event and passes a custom argument (forumId)<div style="margin: 5px 20px 20px;">
7387      * Code:<pre><code>
7388 el.on('click', this.onClick, this, {
7389     single: true,
7390     delay: 100,
7391     stopEvent : true,
7392     forumId: 4
7393 });</code></pre>
7394      * <p>
7395      * <b>Attaching multiple handlers in 1 call</b><br>
7396       * The method also allows for a single argument to be passed which is a config object containing properties
7397      * which specify multiple handlers.
7398      * <p>
7399      * Code:<pre><code>
7400 el.on({
7401     'click' : {
7402         fn: this.onClick
7403         scope: this,
7404         delay: 100
7405     },
7406     'mouseover' : {
7407         fn: this.onMouseOver
7408         scope: this
7409     },
7410     'mouseout' : {
7411         fn: this.onMouseOut
7412         scope: this
7413     }
7414 });</code></pre>
7415      * <p>
7416      * Or a shorthand syntax:<br>
7417      * Code:<pre><code>
7418 el.on({
7419     'click' : this.onClick,
7420     'mouseover' : this.onMouseOver,
7421     'mouseout' : this.onMouseOut
7422     scope: this
7423 });</code></pre>
7424      */
7425     pub.on = pub.addListener;
7426     pub.un = pub.removeListener;
7427
7428     pub.stoppedMouseDownEvent = new Roo.util.Event();
7429     return pub;
7430 }();
7431 /**
7432   * Fires when the document is ready (before onload and before images are loaded).  Shorthand of {@link Roo.EventManager#onDocumentReady}.
7433   * @param {Function} fn        The method the event invokes
7434   * @param {Object}   scope    An  object that becomes the scope of the handler
7435   * @param {boolean}  override If true, the obj passed in becomes
7436   *                             the execution scope of the listener
7437   * @member Roo
7438   * @method onReady
7439  */
7440 Roo.onReady = Roo.EventManager.onDocumentReady;
7441
7442 Roo.onReady(function(){
7443     var bd = Roo.get(document.body);
7444     if(!bd){ return; }
7445
7446     var cls = [
7447             Roo.isIE ? "roo-ie"
7448             : Roo.isIE11 ? "roo-ie11"
7449             : Roo.isEdge ? "roo-edge"
7450             : Roo.isGecko ? "roo-gecko"
7451             : Roo.isOpera ? "roo-opera"
7452             : Roo.isSafari ? "roo-safari" : ""];
7453
7454     if(Roo.isMac){
7455         cls.push("roo-mac");
7456     }
7457     if(Roo.isLinux){
7458         cls.push("roo-linux");
7459     }
7460     if(Roo.isIOS){
7461         cls.push("roo-ios");
7462     }
7463     if(Roo.isTouch){
7464         cls.push("roo-touch");
7465     }
7466     if(Roo.isBorderBox){
7467         cls.push('roo-border-box');
7468     }
7469     if(Roo.isStrict){ // add to the parent to allow for selectors like ".ext-strict .ext-ie"
7470         var p = bd.dom.parentNode;
7471         if(p){
7472             p.className += ' roo-strict';
7473         }
7474     }
7475     bd.addClass(cls.join(' '));
7476 });
7477
7478 /**
7479  * @class Roo.EventObject
7480  * EventObject exposes the Yahoo! UI Event functionality directly on the object
7481  * passed to your event handler. It exists mostly for convenience. It also fixes the annoying null checks automatically to cleanup your code 
7482  * Example:
7483  * <pre><code>
7484  function handleClick(e){ // e is not a standard event object, it is a Roo.EventObject
7485     e.preventDefault();
7486     var target = e.getTarget();
7487     ...
7488  }
7489  var myDiv = Roo.get("myDiv");
7490  myDiv.on("click", handleClick);
7491  //or
7492  Roo.EventManager.on("myDiv", 'click', handleClick);
7493  Roo.EventManager.addListener("myDiv", 'click', handleClick);
7494  </code></pre>
7495  * @static
7496  */
7497 Roo.EventObject = function(){
7498     
7499     var E = Roo.lib.Event;
7500     
7501     // safari keypress events for special keys return bad keycodes
7502     var safariKeys = {
7503         63234 : 37, // left
7504         63235 : 39, // right
7505         63232 : 38, // up
7506         63233 : 40, // down
7507         63276 : 33, // page up
7508         63277 : 34, // page down
7509         63272 : 46, // delete
7510         63273 : 36, // home
7511         63275 : 35  // end
7512     };
7513
7514     // normalize button clicks
7515     var btnMap = Roo.isIE ? {1:0,4:1,2:2} :
7516                 (Roo.isSafari ? {1:0,2:1,3:2} : {0:0,1:1,2:2});
7517
7518     Roo.EventObjectImpl = function(e){
7519         if(e){
7520             this.setEvent(e.browserEvent || e);
7521         }
7522     };
7523     Roo.EventObjectImpl.prototype = {
7524         /**
7525          * Used to fix doc tools.
7526          * @scope Roo.EventObject.prototype
7527          */
7528             
7529
7530         
7531         
7532         /** The normal browser event */
7533         browserEvent : null,
7534         /** The button pressed in a mouse event */
7535         button : -1,
7536         /** True if the shift key was down during the event */
7537         shiftKey : false,
7538         /** True if the control key was down during the event */
7539         ctrlKey : false,
7540         /** True if the alt key was down during the event */
7541         altKey : false,
7542
7543         /** Key constant 
7544         * @type Number */
7545         BACKSPACE : 8,
7546         /** Key constant 
7547         * @type Number */
7548         TAB : 9,
7549         /** Key constant 
7550         * @type Number */
7551         RETURN : 13,
7552         /** Key constant 
7553         * @type Number */
7554         ENTER : 13,
7555         /** Key constant 
7556         * @type Number */
7557         SHIFT : 16,
7558         /** Key constant 
7559         * @type Number */
7560         CONTROL : 17,
7561         /** Key constant 
7562         * @type Number */
7563         ESC : 27,
7564         /** Key constant 
7565         * @type Number */
7566         SPACE : 32,
7567         /** Key constant 
7568         * @type Number */
7569         PAGEUP : 33,
7570         /** Key constant 
7571         * @type Number */
7572         PAGEDOWN : 34,
7573         /** Key constant 
7574         * @type Number */
7575         END : 35,
7576         /** Key constant 
7577         * @type Number */
7578         HOME : 36,
7579         /** Key constant 
7580         * @type Number */
7581         LEFT : 37,
7582         /** Key constant 
7583         * @type Number */
7584         UP : 38,
7585         /** Key constant 
7586         * @type Number */
7587         RIGHT : 39,
7588         /** Key constant 
7589         * @type Number */
7590         DOWN : 40,
7591         /** Key constant 
7592         * @type Number */
7593         DELETE : 46,
7594         /** Key constant 
7595         * @type Number */
7596         F5 : 116,
7597
7598            /** @private */
7599         setEvent : function(e){
7600             if(e == this || (e && e.browserEvent)){ // already wrapped
7601                 return e;
7602             }
7603             this.browserEvent = e;
7604             if(e){
7605                 // normalize buttons
7606                 this.button = e.button ? btnMap[e.button] : (e.which ? e.which-1 : -1);
7607                 if(e.type == 'click' && this.button == -1){
7608                     this.button = 0;
7609                 }
7610                 this.type = e.type;
7611                 this.shiftKey = e.shiftKey;
7612                 // mac metaKey behaves like ctrlKey
7613                 this.ctrlKey = e.ctrlKey || e.metaKey;
7614                 this.altKey = e.altKey;
7615                 // in getKey these will be normalized for the mac
7616                 this.keyCode = e.keyCode;
7617                 // keyup warnings on firefox.
7618                 this.charCode = (e.type == 'keyup' || e.type == 'keydown') ? 0 : e.charCode;
7619                 // cache the target for the delayed and or buffered events
7620                 this.target = E.getTarget(e);
7621                 // same for XY
7622                 this.xy = E.getXY(e);
7623             }else{
7624                 this.button = -1;
7625                 this.shiftKey = false;
7626                 this.ctrlKey = false;
7627                 this.altKey = false;
7628                 this.keyCode = 0;
7629                 this.charCode =0;
7630                 this.target = null;
7631                 this.xy = [0, 0];
7632             }
7633             return this;
7634         },
7635
7636         /**
7637          * Stop the event (preventDefault and stopPropagation)
7638          */
7639         stopEvent : function(){
7640             if(this.browserEvent){
7641                 if(this.browserEvent.type == 'mousedown'){
7642                     Roo.EventManager.stoppedMouseDownEvent.fire(this);
7643                 }
7644                 E.stopEvent(this.browserEvent);
7645             }
7646         },
7647
7648         /**
7649          * Prevents the browsers default handling of the event.
7650          */
7651         preventDefault : function(){
7652             if(this.browserEvent){
7653                 E.preventDefault(this.browserEvent);
7654             }
7655         },
7656
7657         /** @private */
7658         isNavKeyPress : function(){
7659             var k = this.keyCode;
7660             k = Roo.isSafari ? (safariKeys[k] || k) : k;
7661             return (k >= 33 && k <= 40) || k == this.RETURN || k == this.TAB || k == this.ESC;
7662         },
7663
7664         isSpecialKey : function(){
7665             var k = this.keyCode;
7666             return (this.type == 'keypress' && this.ctrlKey) || k == 9 || k == 13  || k == 40 || k == 27 ||
7667             (k == 16) || (k == 17) ||
7668             (k >= 18 && k <= 20) ||
7669             (k >= 33 && k <= 35) ||
7670             (k >= 36 && k <= 39) ||
7671             (k >= 44 && k <= 45);
7672         },
7673         /**
7674          * Cancels bubbling of the event.
7675          */
7676         stopPropagation : function(){
7677             if(this.browserEvent){
7678                 if(this.type == 'mousedown'){
7679                     Roo.EventManager.stoppedMouseDownEvent.fire(this);
7680                 }
7681                 E.stopPropagation(this.browserEvent);
7682             }
7683         },
7684
7685         /**
7686          * Gets the key code for the event.
7687          * @return {Number}
7688          */
7689         getCharCode : function(){
7690             return this.charCode || this.keyCode;
7691         },
7692
7693         /**
7694          * Returns a normalized keyCode for the event.
7695          * @return {Number} The key code
7696          */
7697         getKey : function(){
7698             var k = this.keyCode || this.charCode;
7699             return Roo.isSafari ? (safariKeys[k] || k) : k;
7700         },
7701
7702         /**
7703          * Gets the x coordinate of the event.
7704          * @return {Number}
7705          */
7706         getPageX : function(){
7707             return this.xy[0];
7708         },
7709
7710         /**
7711          * Gets the y coordinate of the event.
7712          * @return {Number}
7713          */
7714         getPageY : function(){
7715             return this.xy[1];
7716         },
7717
7718         /**
7719          * Gets the time of the event.
7720          * @return {Number}
7721          */
7722         getTime : function(){
7723             if(this.browserEvent){
7724                 return E.getTime(this.browserEvent);
7725             }
7726             return null;
7727         },
7728
7729         /**
7730          * Gets the page coordinates of the event.
7731          * @return {Array} The xy values like [x, y]
7732          */
7733         getXY : function(){
7734             return this.xy;
7735         },
7736
7737         /**
7738          * Gets the target for the event.
7739          * @param {String} selector (optional) A simple selector to filter the target or look for an ancestor of the target
7740          * @param {Number/String/HTMLElement/Element} maxDepth (optional) The max depth to
7741                 search as a number or element (defaults to 10 || document.body)
7742          * @param {Boolean} returnEl (optional) True to return a Roo.Element object instead of DOM node
7743          * @return {HTMLelement}
7744          */
7745         getTarget : function(selector, maxDepth, returnEl){
7746             return selector ? Roo.fly(this.target).findParent(selector, maxDepth, returnEl) : this.target;
7747         },
7748         /**
7749          * Gets the related target.
7750          * @return {HTMLElement}
7751          */
7752         getRelatedTarget : function(){
7753             if(this.browserEvent){
7754                 return E.getRelatedTarget(this.browserEvent);
7755             }
7756             return null;
7757         },
7758
7759         /**
7760          * Normalizes mouse wheel delta across browsers
7761          * @return {Number} The delta
7762          */
7763         getWheelDelta : function(){
7764             var e = this.browserEvent;
7765             var delta = 0;
7766             if(e.wheelDelta){ /* IE/Opera. */
7767                 delta = e.wheelDelta/120;
7768             }else if(e.detail){ /* Mozilla case. */
7769                 delta = -e.detail/3;
7770             }
7771             return delta;
7772         },
7773
7774         /**
7775          * Returns true if the control, meta, shift or alt key was pressed during this event.
7776          * @return {Boolean}
7777          */
7778         hasModifier : function(){
7779             return !!((this.ctrlKey || this.altKey) || this.shiftKey);
7780         },
7781
7782         /**
7783          * Returns true if the target of this event equals el or is a child of el
7784          * @param {String/HTMLElement/Element} el
7785          * @param {Boolean} related (optional) true to test if the related target is within el instead of the target
7786          * @return {Boolean}
7787          */
7788         within : function(el, related){
7789             var t = this[related ? "getRelatedTarget" : "getTarget"]();
7790             return t && Roo.fly(el).contains(t);
7791         },
7792
7793         getPoint : function(){
7794             return new Roo.lib.Point(this.xy[0], this.xy[1]);
7795         }
7796     };
7797
7798     return new Roo.EventObjectImpl();
7799 }();
7800             
7801     /*
7802  * Based on:
7803  * Ext JS Library 1.1.1
7804  * Copyright(c) 2006-2007, Ext JS, LLC.
7805  *
7806  * Originally Released Under LGPL - original licence link has changed is not relivant.
7807  *
7808  * Fork - LGPL
7809  * <script type="text/javascript">
7810  */
7811
7812  
7813 // was in Composite Element!??!?!
7814  
7815 (function(){
7816     var D = Roo.lib.Dom;
7817     var E = Roo.lib.Event;
7818     var A = Roo.lib.Anim;
7819
7820     // local style camelizing for speed
7821     var propCache = {};
7822     var camelRe = /(-[a-z])/gi;
7823     var camelFn = function(m, a){ return a.charAt(1).toUpperCase(); };
7824     var view = document.defaultView;
7825
7826 /**
7827  * @class Roo.Element
7828  * Represents an Element in the DOM.<br><br>
7829  * Usage:<br>
7830 <pre><code>
7831 var el = Roo.get("my-div");
7832
7833 // or with getEl
7834 var el = getEl("my-div");
7835
7836 // or with a DOM element
7837 var el = Roo.get(myDivElement);
7838 </code></pre>
7839  * Using Roo.get() or getEl() instead of calling the constructor directly ensures you get the same object
7840  * each call instead of constructing a new one.<br><br>
7841  * <b>Animations</b><br />
7842  * Many of the functions for manipulating an element have an optional "animate" parameter. The animate parameter
7843  * should either be a boolean (true) or an object literal with animation options. The animation options are:
7844 <pre>
7845 Option    Default   Description
7846 --------- --------  ---------------------------------------------
7847 duration  .35       The duration of the animation in seconds
7848 easing    easeOut   The YUI easing method
7849 callback  none      A function to execute when the anim completes
7850 scope     this      The scope (this) of the callback function
7851 </pre>
7852 * Also, the Anim object being used for the animation will be set on your options object as "anim", which allows you to stop or
7853 * manipulate the animation. Here's an example:
7854 <pre><code>
7855 var el = Roo.get("my-div");
7856
7857 // no animation
7858 el.setWidth(100);
7859
7860 // default animation
7861 el.setWidth(100, true);
7862
7863 // animation with some options set
7864 el.setWidth(100, {
7865     duration: 1,
7866     callback: this.foo,
7867     scope: this
7868 });
7869
7870 // using the "anim" property to get the Anim object
7871 var opt = {
7872     duration: 1,
7873     callback: this.foo,
7874     scope: this
7875 };
7876 el.setWidth(100, opt);
7877 ...
7878 if(opt.anim.isAnimated()){
7879     opt.anim.stop();
7880 }
7881 </code></pre>
7882 * <b> Composite (Collections of) Elements</b><br />
7883  * For working with collections of Elements, see <a href="Roo.CompositeElement.html">Roo.CompositeElement</a>
7884  * @constructor Create a new Element directly.
7885  * @param {String/HTMLElement} element
7886  * @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).
7887  */
7888     Roo.Element = function(element, forceNew)
7889     {
7890         var dom = typeof element == "string" ?
7891                 document.getElementById(element) : element;
7892         
7893         this.listeners = {};
7894         
7895         if(!dom){ // invalid id/element
7896             return null;
7897         }
7898         var id = dom.id;
7899         if(forceNew !== true && id && Roo.Element.cache[id]){ // element object already exists
7900             return Roo.Element.cache[id];
7901         }
7902
7903         /**
7904          * The DOM element
7905          * @type HTMLElement
7906          */
7907         this.dom = dom;
7908
7909         /**
7910          * The DOM element ID
7911          * @type String
7912          */
7913         this.id = id || Roo.id(dom);
7914         
7915         return this; // assumed for cctor?
7916     };
7917
7918     var El = Roo.Element;
7919
7920     El.prototype = {
7921         /**
7922          * The element's default display mode  (defaults to "") 
7923          * @type String
7924          */
7925         originalDisplay : "",
7926
7927         
7928         // note this is overridden in BS version..
7929         visibilityMode : 1, 
7930         /**
7931          * The default unit to append to CSS values where a unit isn't provided (defaults to px).
7932          * @type String
7933          */
7934         defaultUnit : "px",
7935         
7936         /**
7937          * Sets the element's visibility mode. When setVisible() is called it
7938          * will use this to determine whether to set the visibility or the display property.
7939          * @param visMode Element.VISIBILITY or Element.DISPLAY
7940          * @return {Roo.Element} this
7941          */
7942         setVisibilityMode : function(visMode){
7943             this.visibilityMode = visMode;
7944             return this;
7945         },
7946         /**
7947          * Convenience method for setVisibilityMode(Element.DISPLAY)
7948          * @param {String} display (optional) What to set display to when visible
7949          * @return {Roo.Element} this
7950          */
7951         enableDisplayMode : function(display){
7952             this.setVisibilityMode(El.DISPLAY);
7953             if(typeof display != "undefined") { this.originalDisplay = display; }
7954             return this;
7955         },
7956
7957         /**
7958          * 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)
7959          * @param {String} selector The simple selector to test
7960          * @param {Number/String/HTMLElement/Element} maxDepth (optional) The max depth to
7961                 search as a number or element (defaults to 10 || document.body)
7962          * @param {Boolean} returnEl (optional) True to return a Roo.Element object instead of DOM node
7963          * @return {HTMLElement} The matching DOM node (or null if no match was found)
7964          */
7965         findParent : function(simpleSelector, maxDepth, returnEl){
7966             var p = this.dom, b = document.body, depth = 0, dq = Roo.DomQuery, stopEl;
7967             maxDepth = maxDepth || 50;
7968             if(typeof maxDepth != "number"){
7969                 stopEl = Roo.getDom(maxDepth);
7970                 maxDepth = 10;
7971             }
7972             while(p && p.nodeType == 1 && depth < maxDepth && p != b && p != stopEl){
7973                 if(dq.is(p, simpleSelector)){
7974                     return returnEl ? Roo.get(p) : p;
7975                 }
7976                 depth++;
7977                 p = p.parentNode;
7978             }
7979             return null;
7980         },
7981
7982
7983         /**
7984          * Looks at parent nodes for a match of the passed simple selector (e.g. div.some-class or span:first-child)
7985          * @param {String} selector The simple selector to test
7986          * @param {Number/String/HTMLElement/Element} maxDepth (optional) The max depth to
7987                 search as a number or element (defaults to 10 || document.body)
7988          * @param {Boolean} returnEl (optional) True to return a Roo.Element object instead of DOM node
7989          * @return {HTMLElement} The matching DOM node (or null if no match was found)
7990          */
7991         findParentNode : function(simpleSelector, maxDepth, returnEl){
7992             var p = Roo.fly(this.dom.parentNode, '_internal');
7993             return p ? p.findParent(simpleSelector, maxDepth, returnEl) : null;
7994         },
7995         
7996         /**
7997          * Looks at  the scrollable parent element
7998          */
7999         findScrollableParent : function()
8000         {
8001             var overflowRegex = /(auto|scroll)/;
8002             
8003             if(this.getStyle('position') === 'fixed'){
8004                 return Roo.isAndroid ? Roo.get(document.documentElement) : Roo.get(document.body);
8005             }
8006             
8007             var excludeStaticParent = this.getStyle('position') === "absolute";
8008             
8009             for (var parent = this; (parent = Roo.get(parent.dom.parentNode));){
8010                 
8011                 if (excludeStaticParent && parent.getStyle('position') === "static") {
8012                     continue;
8013                 }
8014                 
8015                 if (overflowRegex.test(parent.getStyle('overflow') + parent.getStyle('overflow-x') + parent.getStyle('overflow-y'))){
8016                     return parent;
8017                 }
8018                 
8019                 if(parent.dom.nodeName.toLowerCase() == 'body'){
8020                     return Roo.isAndroid ? Roo.get(document.documentElement) : Roo.get(document.body);
8021                 }
8022             }
8023             
8024             return Roo.isAndroid ? Roo.get(document.documentElement) : Roo.get(document.body);
8025         },
8026
8027         /**
8028          * Walks up the dom looking for a parent node that matches the passed simple selector (e.g. div.some-class or span:first-child).
8029          * This is a shortcut for findParentNode() that always returns an Roo.Element.
8030          * @param {String} selector The simple selector to test
8031          * @param {Number/String/HTMLElement/Element} maxDepth (optional) The max depth to
8032                 search as a number or element (defaults to 10 || document.body)
8033          * @return {Roo.Element} The matching DOM node (or null if no match was found)
8034          */
8035         up : function(simpleSelector, maxDepth){
8036             return this.findParentNode(simpleSelector, maxDepth, true);
8037         },
8038
8039
8040
8041         /**
8042          * Returns true if this element matches the passed simple selector (e.g. div.some-class or span:first-child)
8043          * @param {String} selector The simple selector to test
8044          * @return {Boolean} True if this element matches the selector, else false
8045          */
8046         is : function(simpleSelector){
8047             return Roo.DomQuery.is(this.dom, simpleSelector);
8048         },
8049
8050         /**
8051          * Perform animation on this element.
8052          * @param {Object} args The YUI animation control args
8053          * @param {Float} duration (optional) How long the animation lasts in seconds (defaults to .35)
8054          * @param {Function} onComplete (optional) Function to call when animation completes
8055          * @param {String} easing (optional) Easing method to use (defaults to 'easeOut')
8056          * @param {String} animType (optional) 'run' is the default. Can also be 'color', 'motion', or 'scroll'
8057          * @return {Roo.Element} this
8058          */
8059         animate : function(args, duration, onComplete, easing, animType){
8060             this.anim(args, {duration: duration, callback: onComplete, easing: easing}, animType);
8061             return this;
8062         },
8063
8064         /*
8065          * @private Internal animation call
8066          */
8067         anim : function(args, opt, animType, defaultDur, defaultEase, cb){
8068             animType = animType || 'run';
8069             opt = opt || {};
8070             var anim = Roo.lib.Anim[animType](
8071                 this.dom, args,
8072                 (opt.duration || defaultDur) || .35,
8073                 (opt.easing || defaultEase) || 'easeOut',
8074                 function(){
8075                     Roo.callback(cb, this);
8076                     Roo.callback(opt.callback, opt.scope || this, [this, opt]);
8077                 },
8078                 this
8079             );
8080             opt.anim = anim;
8081             return anim;
8082         },
8083
8084         // private legacy anim prep
8085         preanim : function(a, i){
8086             return !a[i] ? false : (typeof a[i] == "object" ? a[i]: {duration: a[i+1], callback: a[i+2], easing: a[i+3]});
8087         },
8088
8089         /**
8090          * Removes worthless text nodes
8091          * @param {Boolean} forceReclean (optional) By default the element
8092          * keeps track if it has been cleaned already so
8093          * you can call this over and over. However, if you update the element and
8094          * need to force a reclean, you can pass true.
8095          */
8096         clean : function(forceReclean){
8097             if(this.isCleaned && forceReclean !== true){
8098                 return this;
8099             }
8100             var ns = /\S/;
8101             var d = this.dom, n = d.firstChild, ni = -1;
8102             while(n){
8103                 var nx = n.nextSibling;
8104                 if(n.nodeType == 3 && !ns.test(n.nodeValue)){
8105                     d.removeChild(n);
8106                 }else{
8107                     n.nodeIndex = ++ni;
8108                 }
8109                 n = nx;
8110             }
8111             this.isCleaned = true;
8112             return this;
8113         },
8114
8115         // private
8116         calcOffsetsTo : function(el){
8117             el = Roo.get(el);
8118             var d = el.dom;
8119             var restorePos = false;
8120             if(el.getStyle('position') == 'static'){
8121                 el.position('relative');
8122                 restorePos = true;
8123             }
8124             var x = 0, y =0;
8125             var op = this.dom;
8126             while(op && op != d && op.tagName != 'HTML'){
8127                 x+= op.offsetLeft;
8128                 y+= op.offsetTop;
8129                 op = op.offsetParent;
8130             }
8131             if(restorePos){
8132                 el.position('static');
8133             }
8134             return [x, y];
8135         },
8136
8137         /**
8138          * Scrolls this element into view within the passed container.
8139          * @param {String/HTMLElement/Element} container (optional) The container element to scroll (defaults to document.body)
8140          * @param {Boolean} hscroll (optional) False to disable horizontal scroll (defaults to true)
8141          * @return {Roo.Element} this
8142          */
8143         scrollIntoView : function(container, hscroll){
8144             var c = Roo.getDom(container) || document.body;
8145             var el = this.dom;
8146
8147             var o = this.calcOffsetsTo(c),
8148                 l = o[0],
8149                 t = o[1],
8150                 b = t+el.offsetHeight,
8151                 r = l+el.offsetWidth;
8152
8153             var ch = c.clientHeight;
8154             var ct = parseInt(c.scrollTop, 10);
8155             var cl = parseInt(c.scrollLeft, 10);
8156             var cb = ct + ch;
8157             var cr = cl + c.clientWidth;
8158
8159             if(t < ct){
8160                 c.scrollTop = t;
8161             }else if(b > cb){
8162                 c.scrollTop = b-ch;
8163             }
8164
8165             if(hscroll !== false){
8166                 if(l < cl){
8167                     c.scrollLeft = l;
8168                 }else if(r > cr){
8169                     c.scrollLeft = r-c.clientWidth;
8170                 }
8171             }
8172             return this;
8173         },
8174
8175         // private
8176         scrollChildIntoView : function(child, hscroll){
8177             Roo.fly(child, '_scrollChildIntoView').scrollIntoView(this, hscroll);
8178         },
8179
8180         /**
8181          * Measures the element's content height and updates height to match. Note: this function uses setTimeout so
8182          * the new height may not be available immediately.
8183          * @param {Boolean} animate (optional) Animate the transition (defaults to false)
8184          * @param {Float} duration (optional) Length of the animation in seconds (defaults to .35)
8185          * @param {Function} onComplete (optional) Function to call when animation completes
8186          * @param {String} easing (optional) Easing method to use (defaults to easeOut)
8187          * @return {Roo.Element} this
8188          */
8189         autoHeight : function(animate, duration, onComplete, easing){
8190             var oldHeight = this.getHeight();
8191             this.clip();
8192             this.setHeight(1); // force clipping
8193             setTimeout(function(){
8194                 var height = parseInt(this.dom.scrollHeight, 10); // parseInt for Safari
8195                 if(!animate){
8196                     this.setHeight(height);
8197                     this.unclip();
8198                     if(typeof onComplete == "function"){
8199                         onComplete();
8200                     }
8201                 }else{
8202                     this.setHeight(oldHeight); // restore original height
8203                     this.setHeight(height, animate, duration, function(){
8204                         this.unclip();
8205                         if(typeof onComplete == "function") { onComplete(); }
8206                     }.createDelegate(this), easing);
8207                 }
8208             }.createDelegate(this), 0);
8209             return this;
8210         },
8211
8212         /**
8213          * Returns true if this element is an ancestor of the passed element
8214          * @param {HTMLElement/String} el The element to check
8215          * @return {Boolean} True if this element is an ancestor of el, else false
8216          */
8217         contains : function(el){
8218             if(!el){return false;}
8219             return D.isAncestor(this.dom, el.dom ? el.dom : el);
8220         },
8221
8222         /**
8223          * Checks whether the element is currently visible using both visibility and display properties.
8224          * @param {Boolean} deep (optional) True to walk the dom and see if parent elements are hidden (defaults to false)
8225          * @return {Boolean} True if the element is currently visible, else false
8226          */
8227         isVisible : function(deep) {
8228             var vis = !(this.getStyle("visibility") == "hidden" || this.getStyle("display") == "none");
8229             if(deep !== true || !vis){
8230                 return vis;
8231             }
8232             var p = this.dom.parentNode;
8233             while(p && p.tagName.toLowerCase() != "body"){
8234                 if(!Roo.fly(p, '_isVisible').isVisible()){
8235                     return false;
8236                 }
8237                 p = p.parentNode;
8238             }
8239             return true;
8240         },
8241
8242         /**
8243          * Creates a {@link Roo.CompositeElement} for child nodes based on the passed CSS selector (the selector should not contain an id).
8244          * @param {String} selector The CSS selector
8245          * @param {Boolean} unique (optional) True to create a unique Roo.Element for each child (defaults to false, which creates a single shared flyweight object)
8246          * @return {CompositeElement/CompositeElementLite} The composite element
8247          */
8248         select : function(selector, unique){
8249             return El.select(selector, unique, this.dom);
8250         },
8251
8252         /**
8253          * Selects child nodes based on the passed CSS selector (the selector should not contain an id).
8254          * @param {String} selector The CSS selector
8255          * @return {Array} An array of the matched nodes
8256          */
8257         query : function(selector, unique){
8258             return Roo.DomQuery.select(selector, this.dom);
8259         },
8260
8261         /**
8262          * Selects a single child at any depth below this element based on the passed CSS selector (the selector should not contain an id).
8263          * @param {String} selector The CSS selector
8264          * @param {Boolean} returnDom (optional) True to return the DOM node instead of Roo.Element (defaults to false)
8265          * @return {HTMLElement/Roo.Element} The child Roo.Element (or DOM node if returnDom = true)
8266          */
8267         child : function(selector, returnDom){
8268             var n = Roo.DomQuery.selectNode(selector, this.dom);
8269             return returnDom ? n : Roo.get(n);
8270         },
8271
8272         /**
8273          * Selects a single *direct* child based on the passed CSS selector (the selector should not contain an id).
8274          * @param {String} selector The CSS selector
8275          * @param {Boolean} returnDom (optional) True to return the DOM node instead of Roo.Element (defaults to false)
8276          * @return {HTMLElement/Roo.Element} The child Roo.Element (or DOM node if returnDom = true)
8277          */
8278         down : function(selector, returnDom){
8279             var n = Roo.DomQuery.selectNode(" > " + selector, this.dom);
8280             return returnDom ? n : Roo.get(n);
8281         },
8282
8283         /**
8284          * Initializes a {@link Roo.dd.DD} drag drop object for this element.
8285          * @param {String} group The group the DD object is member of
8286          * @param {Object} config The DD config object
8287          * @param {Object} overrides An object containing methods to override/implement on the DD object
8288          * @return {Roo.dd.DD} The DD object
8289          */
8290         initDD : function(group, config, overrides){
8291             var dd = new Roo.dd.DD(Roo.id(this.dom), group, config);
8292             return Roo.apply(dd, overrides);
8293         },
8294
8295         /**
8296          * Initializes a {@link Roo.dd.DDProxy} object for this element.
8297          * @param {String} group The group the DDProxy object is member of
8298          * @param {Object} config The DDProxy config object
8299          * @param {Object} overrides An object containing methods to override/implement on the DDProxy object
8300          * @return {Roo.dd.DDProxy} The DDProxy object
8301          */
8302         initDDProxy : function(group, config, overrides){
8303             var dd = new Roo.dd.DDProxy(Roo.id(this.dom), group, config);
8304             return Roo.apply(dd, overrides);
8305         },
8306
8307         /**
8308          * Initializes a {@link Roo.dd.DDTarget} object for this element.
8309          * @param {String} group The group the DDTarget object is member of
8310          * @param {Object} config The DDTarget config object
8311          * @param {Object} overrides An object containing methods to override/implement on the DDTarget object
8312          * @return {Roo.dd.DDTarget} The DDTarget object
8313          */
8314         initDDTarget : function(group, config, overrides){
8315             var dd = new Roo.dd.DDTarget(Roo.id(this.dom), group, config);
8316             return Roo.apply(dd, overrides);
8317         },
8318
8319         /**
8320          * Sets the visibility of the element (see details). If the visibilityMode is set to Element.DISPLAY, it will use
8321          * the display property to hide the element, otherwise it uses visibility. The default is to hide and show using the visibility property.
8322          * @param {Boolean} visible Whether the element is visible
8323          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
8324          * @return {Roo.Element} this
8325          */
8326          setVisible : function(visible, animate){
8327             if(!animate || !A){
8328                 if(this.visibilityMode == El.DISPLAY){
8329                     this.setDisplayed(visible);
8330                 }else{
8331                     this.fixDisplay();
8332                     this.dom.style.visibility = visible ? "visible" : "hidden";
8333                 }
8334             }else{
8335                 // closure for composites
8336                 var dom = this.dom;
8337                 var visMode = this.visibilityMode;
8338                 if(visible){
8339                     this.setOpacity(.01);
8340                     this.setVisible(true);
8341                 }
8342                 this.anim({opacity: { to: (visible?1:0) }},
8343                       this.preanim(arguments, 1),
8344                       null, .35, 'easeIn', function(){
8345                          if(!visible){
8346                              if(visMode == El.DISPLAY){
8347                                  dom.style.display = "none";
8348                              }else{
8349                                  dom.style.visibility = "hidden";
8350                              }
8351                              Roo.get(dom).setOpacity(1);
8352                          }
8353                      });
8354             }
8355             return this;
8356         },
8357
8358         /**
8359          * Returns true if display is not "none"
8360          * @return {Boolean}
8361          */
8362         isDisplayed : function() {
8363             return this.getStyle("display") != "none";
8364         },
8365
8366         /**
8367          * Toggles the element's visibility or display, depending on visibility mode.
8368          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
8369          * @return {Roo.Element} this
8370          */
8371         toggle : function(animate){
8372             this.setVisible(!this.isVisible(), this.preanim(arguments, 0));
8373             return this;
8374         },
8375
8376         /**
8377          * Sets the CSS display property. Uses originalDisplay if the specified value is a boolean true.
8378          * @param {Boolean} value Boolean value to display the element using its default display, or a string to set the display directly
8379          * @return {Roo.Element} this
8380          */
8381         setDisplayed : function(value) {
8382             if(typeof value == "boolean"){
8383                value = value ? this.originalDisplay : "none";
8384             }
8385             this.setStyle("display", value);
8386             return this;
8387         },
8388
8389         /**
8390          * Tries to focus the element. Any exceptions are caught and ignored.
8391          * @return {Roo.Element} this
8392          */
8393         focus : function() {
8394             try{
8395                 this.dom.focus();
8396             }catch(e){}
8397             return this;
8398         },
8399
8400         /**
8401          * Tries to blur the element. Any exceptions are caught and ignored.
8402          * @return {Roo.Element} this
8403          */
8404         blur : function() {
8405             try{
8406                 this.dom.blur();
8407             }catch(e){}
8408             return this;
8409         },
8410
8411         /**
8412          * Adds one or more CSS classes to the element. Duplicate classes are automatically filtered out.
8413          * @param {String/Array} className The CSS class to add, or an array of classes
8414          * @return {Roo.Element} this
8415          */
8416         addClass : function(className){
8417             if(className instanceof Array){
8418                 for(var i = 0, len = className.length; i < len; i++) {
8419                     this.addClass(className[i]);
8420                 }
8421             }else{
8422                 if(className && !this.hasClass(className)){
8423                     if (this.dom instanceof SVGElement) {
8424                         this.dom.className.baseVal =this.dom.className.baseVal  + " " + className;
8425                     } else {
8426                         this.dom.className = this.dom.className + " " + className;
8427                     }
8428                 }
8429             }
8430             return this;
8431         },
8432
8433         /**
8434          * Adds one or more CSS classes to this element and removes the same class(es) from all siblings.
8435          * @param {String/Array} className The CSS class to add, or an array of classes
8436          * @return {Roo.Element} this
8437          */
8438         radioClass : function(className){
8439             var siblings = this.dom.parentNode.childNodes;
8440             for(var i = 0; i < siblings.length; i++) {
8441                 var s = siblings[i];
8442                 if(s.nodeType == 1){
8443                     Roo.get(s).removeClass(className);
8444                 }
8445             }
8446             this.addClass(className);
8447             return this;
8448         },
8449
8450         /**
8451          * Removes one or more CSS classes from the element.
8452          * @param {String/Array} className The CSS class to remove, or an array of classes
8453          * @return {Roo.Element} this
8454          */
8455         removeClass : function(className){
8456             
8457             var cn = this.dom instanceof SVGElement ? this.dom.className.baseVal : this.dom.className;
8458             if(!className || !cn){
8459                 return this;
8460             }
8461             if(className instanceof Array){
8462                 for(var i = 0, len = className.length; i < len; i++) {
8463                     this.removeClass(className[i]);
8464                 }
8465             }else{
8466                 if(this.hasClass(className)){
8467                     var re = this.classReCache[className];
8468                     if (!re) {
8469                        re = new RegExp('(?:^|\\s+)' + className + '(?:\\s+|$)', "g");
8470                        this.classReCache[className] = re;
8471                     }
8472                     if (this.dom instanceof SVGElement) {
8473                         this.dom.className.baseVal = cn.replace(re, " ");
8474                     } else {
8475                         this.dom.className = cn.replace(re, " ");
8476                     }
8477                 }
8478             }
8479             return this;
8480         },
8481
8482         // private
8483         classReCache: {},
8484
8485         /**
8486          * Toggles the specified CSS class on this element (removes it if it already exists, otherwise adds it).
8487          * @param {String} className The CSS class to toggle
8488          * @return {Roo.Element} this
8489          */
8490         toggleClass : function(className){
8491             if(this.hasClass(className)){
8492                 this.removeClass(className);
8493             }else{
8494                 this.addClass(className);
8495             }
8496             return this;
8497         },
8498
8499         /**
8500          * Checks if the specified CSS class exists on this element's DOM node.
8501          * @param {String} className The CSS class to check for
8502          * @return {Boolean} True if the class exists, else false
8503          */
8504         hasClass : function(className){
8505             if (this.dom instanceof SVGElement) {
8506                 return className && (' '+this.dom.className.baseVal +' ').indexOf(' '+className+' ') != -1; 
8507             } 
8508             return className && (' '+this.dom.className+' ').indexOf(' '+className+' ') != -1;
8509         },
8510
8511         /**
8512          * Replaces a CSS class on the element with another.  If the old name does not exist, the new name will simply be added.
8513          * @param {String} oldClassName The CSS class to replace
8514          * @param {String} newClassName The replacement CSS class
8515          * @return {Roo.Element} this
8516          */
8517         replaceClass : function(oldClassName, newClassName){
8518             this.removeClass(oldClassName);
8519             this.addClass(newClassName);
8520             return this;
8521         },
8522
8523         /**
8524          * Returns an object with properties matching the styles requested.
8525          * For example, el.getStyles('color', 'font-size', 'width') might return
8526          * {'color': '#FFFFFF', 'font-size': '13px', 'width': '100px'}.
8527          * @param {String} style1 A style name
8528          * @param {String} style2 A style name
8529          * @param {String} etc.
8530          * @return {Object} The style object
8531          */
8532         getStyles : function(){
8533             var a = arguments, len = a.length, r = {};
8534             for(var i = 0; i < len; i++){
8535                 r[a[i]] = this.getStyle(a[i]);
8536             }
8537             return r;
8538         },
8539
8540         /**
8541          * Normalizes currentStyle and computedStyle. This is not YUI getStyle, it is an optimised version.
8542          * @param {String} property The style property whose value is returned.
8543          * @return {String} The current value of the style property for this element.
8544          */
8545         getStyle : function(){
8546             return view && view.getComputedStyle ?
8547                 function(prop){
8548                     var el = this.dom, v, cs, camel;
8549                     if(prop == 'float'){
8550                         prop = "cssFloat";
8551                     }
8552                     if(el.style && (v = el.style[prop])){
8553                         return v;
8554                     }
8555                     if(cs = view.getComputedStyle(el, "")){
8556                         if(!(camel = propCache[prop])){
8557                             camel = propCache[prop] = prop.replace(camelRe, camelFn);
8558                         }
8559                         return cs[camel];
8560                     }
8561                     return null;
8562                 } :
8563                 function(prop){
8564                     var el = this.dom, v, cs, camel;
8565                     if(prop == 'opacity'){
8566                         if(typeof el.style.filter == 'string'){
8567                             var m = el.style.filter.match(/alpha\(opacity=(.*)\)/i);
8568                             if(m){
8569                                 var fv = parseFloat(m[1]);
8570                                 if(!isNaN(fv)){
8571                                     return fv ? fv / 100 : 0;
8572                                 }
8573                             }
8574                         }
8575                         return 1;
8576                     }else if(prop == 'float'){
8577                         prop = "styleFloat";
8578                     }
8579                     if(!(camel = propCache[prop])){
8580                         camel = propCache[prop] = prop.replace(camelRe, camelFn);
8581                     }
8582                     if(v = el.style[camel]){
8583                         return v;
8584                     }
8585                     if(cs = el.currentStyle){
8586                         return cs[camel];
8587                     }
8588                     return null;
8589                 };
8590         }(),
8591
8592         /**
8593          * Wrapper for setting style properties, also takes single object parameter of multiple styles.
8594          * @param {String/Object} property The style property to be set, or an object of multiple styles.
8595          * @param {String} value (optional) The value to apply to the given property, or null if an object was passed.
8596          * @return {Roo.Element} this
8597          */
8598         setStyle : function(prop, value){
8599             if(typeof prop == "string"){
8600                 
8601                 if (prop == 'float') {
8602                     this.setStyle(Roo.isIE ? 'styleFloat'  : 'cssFloat', value);
8603                     return this;
8604                 }
8605                 
8606                 var camel;
8607                 if(!(camel = propCache[prop])){
8608                     camel = propCache[prop] = prop.replace(camelRe, camelFn);
8609                 }
8610                 
8611                 if(camel == 'opacity') {
8612                     this.setOpacity(value);
8613                 }else{
8614                     this.dom.style[camel] = value;
8615                 }
8616             }else{
8617                 for(var style in prop){
8618                     if(typeof prop[style] != "function"){
8619                        this.setStyle(style, prop[style]);
8620                     }
8621                 }
8622             }
8623             return this;
8624         },
8625
8626         /**
8627          * More flexible version of {@link #setStyle} for setting style properties.
8628          * @param {String/Object/Function} styles A style specification string, e.g. "width:100px", or object in the form {width:"100px"}, or
8629          * a function which returns such a specification.
8630          * @return {Roo.Element} this
8631          */
8632         applyStyles : function(style){
8633             Roo.DomHelper.applyStyles(this.dom, style);
8634             return this;
8635         },
8636
8637         /**
8638           * 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).
8639           * @return {Number} The X position of the element
8640           */
8641         getX : function(){
8642             return D.getX(this.dom);
8643         },
8644
8645         /**
8646           * 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).
8647           * @return {Number} The Y position of the element
8648           */
8649         getY : function(){
8650             return D.getY(this.dom);
8651         },
8652
8653         /**
8654           * 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).
8655           * @return {Array} The XY position of the element
8656           */
8657         getXY : function(){
8658             return D.getXY(this.dom);
8659         },
8660
8661         /**
8662          * 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).
8663          * @param {Number} The X position of the element
8664          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
8665          * @return {Roo.Element} this
8666          */
8667         setX : function(x, animate){
8668             if(!animate || !A){
8669                 D.setX(this.dom, x);
8670             }else{
8671                 this.setXY([x, this.getY()], this.preanim(arguments, 1));
8672             }
8673             return this;
8674         },
8675
8676         /**
8677          * 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).
8678          * @param {Number} The Y position of the element
8679          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
8680          * @return {Roo.Element} this
8681          */
8682         setY : function(y, animate){
8683             if(!animate || !A){
8684                 D.setY(this.dom, y);
8685             }else{
8686                 this.setXY([this.getX(), y], this.preanim(arguments, 1));
8687             }
8688             return this;
8689         },
8690
8691         /**
8692          * Sets the element's left position directly using CSS style (instead of {@link #setX}).
8693          * @param {String} left The left CSS property value
8694          * @return {Roo.Element} this
8695          */
8696         setLeft : function(left){
8697             this.setStyle("left", this.addUnits(left));
8698             return this;
8699         },
8700
8701         /**
8702          * Sets the element's top position directly using CSS style (instead of {@link #setY}).
8703          * @param {String} top The top CSS property value
8704          * @return {Roo.Element} this
8705          */
8706         setTop : function(top){
8707             this.setStyle("top", this.addUnits(top));
8708             return this;
8709         },
8710
8711         /**
8712          * Sets the element's CSS right style.
8713          * @param {String} right The right CSS property value
8714          * @return {Roo.Element} this
8715          */
8716         setRight : function(right){
8717             this.setStyle("right", this.addUnits(right));
8718             return this;
8719         },
8720
8721         /**
8722          * Sets the element's CSS bottom style.
8723          * @param {String} bottom The bottom CSS property value
8724          * @return {Roo.Element} this
8725          */
8726         setBottom : function(bottom){
8727             this.setStyle("bottom", this.addUnits(bottom));
8728             return this;
8729         },
8730
8731         /**
8732          * Sets the position of the element in page coordinates, regardless of how the element is positioned.
8733          * The element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
8734          * @param {Array} pos Contains X & Y [x, y] values for new position (coordinates are page-based)
8735          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
8736          * @return {Roo.Element} this
8737          */
8738         setXY : function(pos, animate){
8739             if(!animate || !A){
8740                 D.setXY(this.dom, pos);
8741             }else{
8742                 this.anim({points: {to: pos}}, this.preanim(arguments, 1), 'motion');
8743             }
8744             return this;
8745         },
8746
8747         /**
8748          * Sets the position of the element in page coordinates, regardless of how the element is positioned.
8749          * The element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
8750          * @param {Number} x X value for new position (coordinates are page-based)
8751          * @param {Number} y Y value for new position (coordinates are page-based)
8752          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
8753          * @return {Roo.Element} this
8754          */
8755         setLocation : function(x, y, animate){
8756             this.setXY([x, y], this.preanim(arguments, 2));
8757             return this;
8758         },
8759
8760         /**
8761          * Sets the position of the element in page coordinates, regardless of how the element is positioned.
8762          * The element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
8763          * @param {Number} x X value for new position (coordinates are page-based)
8764          * @param {Number} y Y value for new position (coordinates are page-based)
8765          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
8766          * @return {Roo.Element} this
8767          */
8768         moveTo : function(x, y, animate){
8769             this.setXY([x, y], this.preanim(arguments, 2));
8770             return this;
8771         },
8772
8773         /**
8774          * Returns the region of the given element.
8775          * The element must be part of the DOM tree to have a region (display:none or elements not appended return false).
8776          * @return {Region} A Roo.lib.Region containing "top, left, bottom, right" member data.
8777          */
8778         getRegion : function(){
8779             return D.getRegion(this.dom);
8780         },
8781
8782         /**
8783          * Returns the offset height of the element
8784          * @param {Boolean} contentHeight (optional) true to get the height minus borders and padding
8785          * @return {Number} The element's height
8786          */
8787         getHeight : function(contentHeight){
8788             var h = this.dom.offsetHeight || 0;
8789             return contentHeight !== true ? h : h-this.getBorderWidth("tb")-this.getPadding("tb");
8790         },
8791
8792         /**
8793          * Returns the offset width of the element
8794          * @param {Boolean} contentWidth (optional) true to get the width minus borders and padding
8795          * @return {Number} The element's width
8796          */
8797         getWidth : function(contentWidth){
8798             var w = this.dom.offsetWidth || 0;
8799             return contentWidth !== true ? w : w-this.getBorderWidth("lr")-this.getPadding("lr");
8800         },
8801
8802         /**
8803          * Returns either the offsetHeight or the height of this element based on CSS height adjusted by padding or borders
8804          * when needed to simulate offsetHeight when offsets aren't available. This may not work on display:none elements
8805          * if a height has not been set using CSS.
8806          * @return {Number}
8807          */
8808         getComputedHeight : function(){
8809             var h = Math.max(this.dom.offsetHeight, this.dom.clientHeight);
8810             if(!h){
8811                 h = parseInt(this.getStyle('height'), 10) || 0;
8812                 if(!this.isBorderBox()){
8813                     h += this.getFrameWidth('tb');
8814                 }
8815             }
8816             return h;
8817         },
8818
8819         /**
8820          * Returns either the offsetWidth or the width of this element based on CSS width adjusted by padding or borders
8821          * when needed to simulate offsetWidth when offsets aren't available. This may not work on display:none elements
8822          * if a width has not been set using CSS.
8823          * @return {Number}
8824          */
8825         getComputedWidth : function(){
8826             var w = Math.max(this.dom.offsetWidth, this.dom.clientWidth);
8827             if(!w){
8828                 w = parseInt(this.getStyle('width'), 10) || 0;
8829                 if(!this.isBorderBox()){
8830                     w += this.getFrameWidth('lr');
8831                 }
8832             }
8833             return w;
8834         },
8835
8836         /**
8837          * Returns the size of the element.
8838          * @param {Boolean} contentSize (optional) true to get the width/size minus borders and padding
8839          * @return {Object} An object containing the element's size {width: (element width), height: (element height)}
8840          */
8841         getSize : function(contentSize){
8842             return {width: this.getWidth(contentSize), height: this.getHeight(contentSize)};
8843         },
8844
8845         /**
8846          * Returns the width and height of the viewport.
8847          * @return {Object} An object containing the viewport's size {width: (viewport width), height: (viewport height)}
8848          */
8849         getViewSize : function(){
8850             var d = this.dom, doc = document, aw = 0, ah = 0;
8851             if(d == doc || d == doc.body){
8852                 return {width : D.getViewWidth(), height: D.getViewHeight()};
8853             }else{
8854                 return {
8855                     width : d.clientWidth,
8856                     height: d.clientHeight
8857                 };
8858             }
8859         },
8860
8861         /**
8862          * Returns the value of the "value" attribute
8863          * @param {Boolean} asNumber true to parse the value as a number
8864          * @return {String/Number}
8865          */
8866         getValue : function(asNumber){
8867             return asNumber ? parseInt(this.dom.value, 10) : this.dom.value;
8868         },
8869
8870         // private
8871         adjustWidth : function(width){
8872             if(typeof width == "number"){
8873                 if(this.autoBoxAdjust && !this.isBorderBox()){
8874                    width -= (this.getBorderWidth("lr") + this.getPadding("lr"));
8875                 }
8876                 if(width < 0){
8877                     width = 0;
8878                 }
8879             }
8880             return width;
8881         },
8882
8883         // private
8884         adjustHeight : function(height){
8885             if(typeof height == "number"){
8886                if(this.autoBoxAdjust && !this.isBorderBox()){
8887                    height -= (this.getBorderWidth("tb") + this.getPadding("tb"));
8888                }
8889                if(height < 0){
8890                    height = 0;
8891                }
8892             }
8893             return height;
8894         },
8895
8896         /**
8897          * Set the width of the element
8898          * @param {Number} width The new width
8899          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8900          * @return {Roo.Element} this
8901          */
8902         setWidth : function(width, animate){
8903             width = this.adjustWidth(width);
8904             if(!animate || !A){
8905                 this.dom.style.width = this.addUnits(width);
8906             }else{
8907                 this.anim({width: {to: width}}, this.preanim(arguments, 1));
8908             }
8909             return this;
8910         },
8911
8912         /**
8913          * Set the height of the element
8914          * @param {Number} height The new height
8915          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8916          * @return {Roo.Element} this
8917          */
8918          setHeight : function(height, animate){
8919             height = this.adjustHeight(height);
8920             if(!animate || !A){
8921                 this.dom.style.height = this.addUnits(height);
8922             }else{
8923                 this.anim({height: {to: height}}, this.preanim(arguments, 1));
8924             }
8925             return this;
8926         },
8927
8928         /**
8929          * Set the size of the element. If animation is true, both width an height will be animated concurrently.
8930          * @param {Number} width The new width
8931          * @param {Number} height The new height
8932          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8933          * @return {Roo.Element} this
8934          */
8935          setSize : function(width, height, animate){
8936             if(typeof width == "object"){ // in case of object from getSize()
8937                 height = width.height; width = width.width;
8938             }
8939             width = this.adjustWidth(width); height = this.adjustHeight(height);
8940             if(!animate || !A){
8941                 this.dom.style.width = this.addUnits(width);
8942                 this.dom.style.height = this.addUnits(height);
8943             }else{
8944                 this.anim({width: {to: width}, height: {to: height}}, this.preanim(arguments, 2));
8945             }
8946             return this;
8947         },
8948
8949         /**
8950          * Sets the element's position and size in one shot. If animation is true then width, height, x and y will be animated concurrently.
8951          * @param {Number} x X value for new position (coordinates are page-based)
8952          * @param {Number} y Y value for new position (coordinates are page-based)
8953          * @param {Number} width The new width
8954          * @param {Number} height The new height
8955          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8956          * @return {Roo.Element} this
8957          */
8958         setBounds : function(x, y, width, height, animate){
8959             if(!animate || !A){
8960                 this.setSize(width, height);
8961                 this.setLocation(x, y);
8962             }else{
8963                 width = this.adjustWidth(width); height = this.adjustHeight(height);
8964                 this.anim({points: {to: [x, y]}, width: {to: width}, height: {to: height}},
8965                               this.preanim(arguments, 4), 'motion');
8966             }
8967             return this;
8968         },
8969
8970         /**
8971          * 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.
8972          * @param {Roo.lib.Region} region The region to fill
8973          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8974          * @return {Roo.Element} this
8975          */
8976         setRegion : function(region, animate){
8977             this.setBounds(region.left, region.top, region.right-region.left, region.bottom-region.top, this.preanim(arguments, 1));
8978             return this;
8979         },
8980
8981         /**
8982          * Appends an event handler
8983          *
8984          * @param {String}   eventName     The type of event to append
8985          * @param {Function} fn        The method the event invokes
8986          * @param {Object} scope       (optional) The scope (this object) of the fn
8987          * @param {Object}   options   (optional)An object with standard {@link Roo.EventManager#addListener} options
8988          */
8989         addListener : function(eventName, fn, scope, options)
8990         {
8991             if (eventName == 'dblclick') { // doublclick (touchstart) - faked on touch.
8992                 this.addListener('touchstart', this.onTapHandler, this);
8993             }
8994             
8995             // we need to handle a special case where dom element is a svg element.
8996             // in this case we do not actua
8997             if (!this.dom) {
8998                 return;
8999             }
9000             
9001             if (this.dom instanceof SVGElement && !(this.dom instanceof SVGSVGElement)) {
9002                 if (typeof(this.listeners[eventName]) == 'undefined') {
9003                     this.listeners[eventName] =  new Roo.util.Event(this, eventName);
9004                 }
9005                 this.listeners[eventName].addListener(fn, scope, options);
9006                 return;
9007             }
9008             
9009                 
9010             Roo.EventManager.on(this.dom,  eventName, fn, scope || this, options);
9011             
9012             
9013         },
9014         tapedTwice : false,
9015         onTapHandler : function(event)
9016         {
9017             if(!this.tapedTwice) {
9018                 this.tapedTwice = true;
9019                 var s = this;
9020                 setTimeout( function() {
9021                     s.tapedTwice = false;
9022                 }, 300 );
9023                 return;
9024             }
9025             event.preventDefault();
9026             var revent = new MouseEvent('dblclick',  {
9027                 view: window,
9028                 bubbles: true,
9029                 cancelable: true
9030             });
9031              
9032             this.dom.dispatchEvent(revent);
9033             //action on double tap goes below
9034              
9035         }, 
9036  
9037         /**
9038          * Removes an event handler from this element
9039          * @param {String} eventName the type of event to remove
9040          * @param {Function} fn the method the event invokes
9041          * @param {Function} scope (needed for svg fake listeners)
9042          * @return {Roo.Element} this
9043          */
9044         removeListener : function(eventName, fn, scope){
9045             Roo.EventManager.removeListener(this.dom,  eventName, fn);
9046             if (typeof(this.listeners) == 'undefined'  || typeof(this.listeners[eventName]) == 'undefined') {
9047                 return this;
9048             }
9049             this.listeners[eventName].removeListener(fn, scope);
9050             return this;
9051         },
9052
9053         /**
9054          * Removes all previous added listeners from this element
9055          * @return {Roo.Element} this
9056          */
9057         removeAllListeners : function(){
9058             E.purgeElement(this.dom);
9059             this.listeners = {};
9060             return this;
9061         },
9062
9063         relayEvent : function(eventName, observable){
9064             this.on(eventName, function(e){
9065                 observable.fireEvent(eventName, e);
9066             });
9067         },
9068
9069         
9070         /**
9071          * Set the opacity of the element
9072          * @param {Float} opacity The new opacity. 0 = transparent, .5 = 50% visibile, 1 = fully visible, etc
9073          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
9074          * @return {Roo.Element} this
9075          */
9076          setOpacity : function(opacity, animate){
9077             if(!animate || !A){
9078                 var s = this.dom.style;
9079                 if(Roo.isIE){
9080                     s.zoom = 1;
9081                     s.filter = (s.filter || '').replace(/alpha\([^\)]*\)/gi,"") +
9082                                (opacity == 1 ? "" : "alpha(opacity=" + opacity * 100 + ")");
9083                 }else{
9084                     s.opacity = opacity;
9085                 }
9086             }else{
9087                 this.anim({opacity: {to: opacity}}, this.preanim(arguments, 1), null, .35, 'easeIn');
9088             }
9089             return this;
9090         },
9091
9092         /**
9093          * Gets the left X coordinate
9094          * @param {Boolean} local True to get the local css position instead of page coordinate
9095          * @return {Number}
9096          */
9097         getLeft : function(local){
9098             if(!local){
9099                 return this.getX();
9100             }else{
9101                 return parseInt(this.getStyle("left"), 10) || 0;
9102             }
9103         },
9104
9105         /**
9106          * Gets the right X coordinate of the element (element X position + element width)
9107          * @param {Boolean} local True to get the local css position instead of page coordinate
9108          * @return {Number}
9109          */
9110         getRight : function(local){
9111             if(!local){
9112                 return this.getX() + this.getWidth();
9113             }else{
9114                 return (this.getLeft(true) + this.getWidth()) || 0;
9115             }
9116         },
9117
9118         /**
9119          * Gets the top Y coordinate
9120          * @param {Boolean} local True to get the local css position instead of page coordinate
9121          * @return {Number}
9122          */
9123         getTop : function(local) {
9124             if(!local){
9125                 return this.getY();
9126             }else{
9127                 return parseInt(this.getStyle("top"), 10) || 0;
9128             }
9129         },
9130
9131         /**
9132          * Gets the bottom Y coordinate of the element (element Y position + element height)
9133          * @param {Boolean} local True to get the local css position instead of page coordinate
9134          * @return {Number}
9135          */
9136         getBottom : function(local){
9137             if(!local){
9138                 return this.getY() + this.getHeight();
9139             }else{
9140                 return (this.getTop(true) + this.getHeight()) || 0;
9141             }
9142         },
9143
9144         /**
9145         * Initializes positioning on this element. If a desired position is not passed, it will make the
9146         * the element positioned relative IF it is not already positioned.
9147         * @param {String} pos (optional) Positioning to use "relative", "absolute" or "fixed"
9148         * @param {Number} zIndex (optional) The zIndex to apply
9149         * @param {Number} x (optional) Set the page X position
9150         * @param {Number} y (optional) Set the page Y position
9151         */
9152         position : function(pos, zIndex, x, y){
9153             if(!pos){
9154                if(this.getStyle('position') == 'static'){
9155                    this.setStyle('position', 'relative');
9156                }
9157             }else{
9158                 this.setStyle("position", pos);
9159             }
9160             if(zIndex){
9161                 this.setStyle("z-index", zIndex);
9162             }
9163             if(x !== undefined && y !== undefined){
9164                 this.setXY([x, y]);
9165             }else if(x !== undefined){
9166                 this.setX(x);
9167             }else if(y !== undefined){
9168                 this.setY(y);
9169             }
9170         },
9171
9172         /**
9173         * Clear positioning back to the default when the document was loaded
9174         * @param {String} value (optional) The value to use for the left,right,top,bottom, defaults to '' (empty string). You could use 'auto'.
9175         * @return {Roo.Element} this
9176          */
9177         clearPositioning : function(value){
9178             value = value ||'';
9179             this.setStyle({
9180                 "left": value,
9181                 "right": value,
9182                 "top": value,
9183                 "bottom": value,
9184                 "z-index": "",
9185                 "position" : "static"
9186             });
9187             return this;
9188         },
9189
9190         /**
9191         * Gets an object with all CSS positioning properties. Useful along with setPostioning to get
9192         * snapshot before performing an update and then restoring the element.
9193         * @return {Object}
9194         */
9195         getPositioning : function(){
9196             var l = this.getStyle("left");
9197             var t = this.getStyle("top");
9198             return {
9199                 "position" : this.getStyle("position"),
9200                 "left" : l,
9201                 "right" : l ? "" : this.getStyle("right"),
9202                 "top" : t,
9203                 "bottom" : t ? "" : this.getStyle("bottom"),
9204                 "z-index" : this.getStyle("z-index")
9205             };
9206         },
9207
9208         /**
9209          * Gets the width of the border(s) for the specified side(s)
9210          * @param {String} side Can be t, l, r, b or any combination of those to add multiple values. For example,
9211          * passing lr would get the border (l)eft width + the border (r)ight width.
9212          * @return {Number} The width of the sides passed added together
9213          */
9214         getBorderWidth : function(side){
9215             return this.addStyles(side, El.borders);
9216         },
9217
9218         /**
9219          * Gets the width of the padding(s) for the specified side(s)
9220          * @param {String} side Can be t, l, r, b or any combination of those to add multiple values. For example,
9221          * passing lr would get the padding (l)eft + the padding (r)ight.
9222          * @return {Number} The padding of the sides passed added together
9223          */
9224         getPadding : function(side){
9225             return this.addStyles(side, El.paddings);
9226         },
9227
9228         /**
9229         * Set positioning with an object returned by getPositioning().
9230         * @param {Object} posCfg
9231         * @return {Roo.Element} this
9232          */
9233         setPositioning : function(pc){
9234             this.applyStyles(pc);
9235             if(pc.right == "auto"){
9236                 this.dom.style.right = "";
9237             }
9238             if(pc.bottom == "auto"){
9239                 this.dom.style.bottom = "";
9240             }
9241             return this;
9242         },
9243
9244         // private
9245         fixDisplay : function(){
9246             if(this.getStyle("display") == "none"){
9247                 this.setStyle("visibility", "hidden");
9248                 this.setStyle("display", this.originalDisplay); // first try reverting to default
9249                 if(this.getStyle("display") == "none"){ // if that fails, default to block
9250                     this.setStyle("display", "block");
9251                 }
9252             }
9253         },
9254
9255         /**
9256          * Quick set left and top adding default units
9257          * @param {String} left The left CSS property value
9258          * @param {String} top The top CSS property value
9259          * @return {Roo.Element} this
9260          */
9261          setLeftTop : function(left, top){
9262             this.dom.style.left = this.addUnits(left);
9263             this.dom.style.top = this.addUnits(top);
9264             return this;
9265         },
9266
9267         /**
9268          * Move this element relative to its current position.
9269          * @param {String} direction Possible values are: "l","left" - "r","right" - "t","top","up" - "b","bottom","down".
9270          * @param {Number} distance How far to move the element in pixels
9271          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
9272          * @return {Roo.Element} this
9273          */
9274          move : function(direction, distance, animate){
9275             var xy = this.getXY();
9276             direction = direction.toLowerCase();
9277             switch(direction){
9278                 case "l":
9279                 case "left":
9280                     this.moveTo(xy[0]-distance, xy[1], this.preanim(arguments, 2));
9281                     break;
9282                case "r":
9283                case "right":
9284                     this.moveTo(xy[0]+distance, xy[1], this.preanim(arguments, 2));
9285                     break;
9286                case "t":
9287                case "top":
9288                case "up":
9289                     this.moveTo(xy[0], xy[1]-distance, this.preanim(arguments, 2));
9290                     break;
9291                case "b":
9292                case "bottom":
9293                case "down":
9294                     this.moveTo(xy[0], xy[1]+distance, this.preanim(arguments, 2));
9295                     break;
9296             }
9297             return this;
9298         },
9299
9300         /**
9301          *  Store the current overflow setting and clip overflow on the element - use {@link #unclip} to remove
9302          * @return {Roo.Element} this
9303          */
9304         clip : function(){
9305             if(!this.isClipped){
9306                this.isClipped = true;
9307                this.originalClip = {
9308                    "o": this.getStyle("overflow"),
9309                    "x": this.getStyle("overflow-x"),
9310                    "y": this.getStyle("overflow-y")
9311                };
9312                this.setStyle("overflow", "hidden");
9313                this.setStyle("overflow-x", "hidden");
9314                this.setStyle("overflow-y", "hidden");
9315             }
9316             return this;
9317         },
9318
9319         /**
9320          *  Return clipping (overflow) to original clipping before clip() was called
9321          * @return {Roo.Element} this
9322          */
9323         unclip : function(){
9324             if(this.isClipped){
9325                 this.isClipped = false;
9326                 var o = this.originalClip;
9327                 if(o.o){this.setStyle("overflow", o.o);}
9328                 if(o.x){this.setStyle("overflow-x", o.x);}
9329                 if(o.y){this.setStyle("overflow-y", o.y);}
9330             }
9331             return this;
9332         },
9333
9334
9335         /**
9336          * Gets the x,y coordinates specified by the anchor position on the element.
9337          * @param {String} anchor (optional) The specified anchor position (defaults to "c").  See {@link #alignTo} for details on supported anchor positions.
9338          * @param {Object} size (optional) An object containing the size to use for calculating anchor position
9339          *                       {width: (target width), height: (target height)} (defaults to the element's current size)
9340          * @param {Boolean} local (optional) True to get the local (element top/left-relative) anchor position instead of page coordinates
9341          * @return {Array} [x, y] An array containing the element's x and y coordinates
9342          */
9343         getAnchorXY : function(anchor, local, s){
9344             //Passing a different size is useful for pre-calculating anchors,
9345             //especially for anchored animations that change the el size.
9346
9347             var w, h, vp = false;
9348             if(!s){
9349                 var d = this.dom;
9350                 if(d == document.body || d == document){
9351                     vp = true;
9352                     w = D.getViewWidth(); h = D.getViewHeight();
9353                 }else{
9354                     w = this.getWidth(); h = this.getHeight();
9355                 }
9356             }else{
9357                 w = s.width;  h = s.height;
9358             }
9359             var x = 0, y = 0, r = Math.round;
9360             switch((anchor || "tl").toLowerCase()){
9361                 case "c":
9362                     x = r(w*.5);
9363                     y = r(h*.5);
9364                 break;
9365                 case "t":
9366                     x = r(w*.5);
9367                     y = 0;
9368                 break;
9369                 case "l":
9370                     x = 0;
9371                     y = r(h*.5);
9372                 break;
9373                 case "r":
9374                     x = w;
9375                     y = r(h*.5);
9376                 break;
9377                 case "b":
9378                     x = r(w*.5);
9379                     y = h;
9380                 break;
9381                 case "tl":
9382                     x = 0;
9383                     y = 0;
9384                 break;
9385                 case "bl":
9386                     x = 0;
9387                     y = h;
9388                 break;
9389                 case "br":
9390                     x = w;
9391                     y = h;
9392                 break;
9393                 case "tr":
9394                     x = w;
9395                     y = 0;
9396                 break;
9397             }
9398             if(local === true){
9399                 return [x, y];
9400             }
9401             if(vp){
9402                 var sc = this.getScroll();
9403                 return [x + sc.left, y + sc.top];
9404             }
9405             //Add the element's offset xy
9406             var o = this.getXY();
9407             return [x+o[0], y+o[1]];
9408         },
9409
9410         /**
9411          * Gets the x,y coordinates to align this element with another element. See {@link #alignTo} for more info on the
9412          * supported position values.
9413          * @param {String/HTMLElement/Roo.Element} element The element to align to.
9414          * @param {String} position The position to align to.
9415          * @param {Array} offsets (optional) Offset the positioning by [x, y]
9416          * @return {Array} [x, y]
9417          */
9418         getAlignToXY : function(el, p, o)
9419         {
9420             el = Roo.get(el);
9421             var d = this.dom;
9422             if(!el.dom){
9423                 throw "Element.alignTo with an element that doesn't exist";
9424             }
9425             var c = false; //constrain to viewport
9426             var p1 = "", p2 = "";
9427             o = o || [0,0];
9428
9429             if(!p){
9430                 p = "tl-bl";
9431             }else if(p == "?"){
9432                 p = "tl-bl?";
9433             }else if(p.indexOf("-") == -1){
9434                 p = "tl-" + p;
9435             }
9436             p = p.toLowerCase();
9437             var m = p.match(/^([a-z]+)-([a-z]+)(\?)?$/);
9438             if(!m){
9439                throw "Element.alignTo with an invalid alignment " + p;
9440             }
9441             p1 = m[1]; p2 = m[2]; c = !!m[3];
9442
9443             //Subtract the aligned el's internal xy from the target's offset xy
9444             //plus custom offset to get the aligned el's new offset xy
9445             var a1 = this.getAnchorXY(p1, true);
9446             var a2 = el.getAnchorXY(p2, false);
9447             var x = a2[0] - a1[0] + o[0];
9448             var y = a2[1] - a1[1] + o[1];
9449             if(c){
9450                 //constrain the aligned el to viewport if necessary
9451                 var w = this.getWidth(), h = this.getHeight(), r = el.getRegion();
9452                 // 5px of margin for ie
9453                 var dw = D.getViewWidth()-5, dh = D.getViewHeight()-5;
9454
9455                 //If we are at a viewport boundary and the aligned el is anchored on a target border that is
9456                 //perpendicular to the vp border, allow the aligned el to slide on that border,
9457                 //otherwise swap the aligned el to the opposite border of the target.
9458                 var p1y = p1.charAt(0), p1x = p1.charAt(p1.length-1);
9459                var p2y = p2.charAt(0), p2x = p2.charAt(p2.length-1);
9460                var swapY = ((p1y=="t" && p2y=="b") || (p1y=="b" && p2y=="t")  );
9461                var swapX = ((p1x=="r" && p2x=="l") || (p1x=="l" && p2x=="r"));
9462
9463                var doc = document;
9464                var scrollX = (doc.documentElement.scrollLeft || doc.body.scrollLeft || 0)+5;
9465                var scrollY = (doc.documentElement.scrollTop || doc.body.scrollTop || 0)+5;
9466
9467                if((x+w) > dw + scrollX){
9468                     x = swapX ? r.left-w : dw+scrollX-w;
9469                 }
9470                if(x < scrollX){
9471                    x = swapX ? r.right : scrollX;
9472                }
9473                if((y+h) > dh + scrollY){
9474                     y = swapY ? r.top-h : dh+scrollY-h;
9475                 }
9476                if (y < scrollY){
9477                    y = swapY ? r.bottom : scrollY;
9478                }
9479             }
9480             return [x,y];
9481         },
9482
9483         // private
9484         getConstrainToXY : function(){
9485             var os = {top:0, left:0, bottom:0, right: 0};
9486
9487             return function(el, local, offsets, proposedXY){
9488                 el = Roo.get(el);
9489                 offsets = offsets ? Roo.applyIf(offsets, os) : os;
9490
9491                 var vw, vh, vx = 0, vy = 0;
9492                 if(el.dom == document.body || el.dom == document){
9493                     vw = Roo.lib.Dom.getViewWidth();
9494                     vh = Roo.lib.Dom.getViewHeight();
9495                 }else{
9496                     vw = el.dom.clientWidth;
9497                     vh = el.dom.clientHeight;
9498                     if(!local){
9499                         var vxy = el.getXY();
9500                         vx = vxy[0];
9501                         vy = vxy[1];
9502                     }
9503                 }
9504
9505                 var s = el.getScroll();
9506
9507                 vx += offsets.left + s.left;
9508                 vy += offsets.top + s.top;
9509
9510                 vw -= offsets.right;
9511                 vh -= offsets.bottom;
9512
9513                 var vr = vx+vw;
9514                 var vb = vy+vh;
9515
9516                 var xy = proposedXY || (!local ? this.getXY() : [this.getLeft(true), this.getTop(true)]);
9517                 var x = xy[0], y = xy[1];
9518                 var w = this.dom.offsetWidth, h = this.dom.offsetHeight;
9519
9520                 // only move it if it needs it
9521                 var moved = false;
9522
9523                 // first validate right/bottom
9524                 if((x + w) > vr){
9525                     x = vr - w;
9526                     moved = true;
9527                 }
9528                 if((y + h) > vb){
9529                     y = vb - h;
9530                     moved = true;
9531                 }
9532                 // then make sure top/left isn't negative
9533                 if(x < vx){
9534                     x = vx;
9535                     moved = true;
9536                 }
9537                 if(y < vy){
9538                     y = vy;
9539                     moved = true;
9540                 }
9541                 return moved ? [x, y] : false;
9542             };
9543         }(),
9544
9545         // private
9546         adjustForConstraints : function(xy, parent, offsets){
9547             return this.getConstrainToXY(parent || document, false, offsets, xy) ||  xy;
9548         },
9549
9550         /**
9551          * Aligns this element with another element relative to the specified anchor points. If the other element is the
9552          * document it aligns it to the viewport.
9553          * The position parameter is optional, and can be specified in any one of the following formats:
9554          * <ul>
9555          *   <li><b>Blank</b>: Defaults to aligning the element's top-left corner to the target's bottom-left corner ("tl-bl").</li>
9556          *   <li><b>One anchor (deprecated)</b>: The passed anchor position is used as the target element's anchor point.
9557          *       The element being aligned will position its top-left corner (tl) to that point.  <i>This method has been
9558          *       deprecated in favor of the newer two anchor syntax below</i>.</li>
9559          *   <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
9560          *       element's anchor point, and the second value is used as the target's anchor point.</li>
9561          * </ul>
9562          * In addition to the anchor points, the position parameter also supports the "?" character.  If "?" is passed at the end of
9563          * the position string, the element will attempt to align as specified, but the position will be adjusted to constrain to
9564          * the viewport if necessary.  Note that the element being aligned might be swapped to align to a different position than
9565          * that specified in order to enforce the viewport constraints.
9566          * Following are all of the supported anchor positions:
9567     <pre>
9568     Value  Description
9569     -----  -----------------------------
9570     tl     The top left corner (default)
9571     t      The center of the top edge
9572     tr     The top right corner
9573     l      The center of the left edge
9574     c      In the center of the element
9575     r      The center of the right edge
9576     bl     The bottom left corner
9577     b      The center of the bottom edge
9578     br     The bottom right corner
9579     </pre>
9580     Example Usage:
9581     <pre><code>
9582     // align el to other-el using the default positioning ("tl-bl", non-constrained)
9583     el.alignTo("other-el");
9584
9585     // align the top left corner of el with the top right corner of other-el (constrained to viewport)
9586     el.alignTo("other-el", "tr?");
9587
9588     // align the bottom right corner of el with the center left edge of other-el
9589     el.alignTo("other-el", "br-l?");
9590
9591     // align the center of el with the bottom left corner of other-el and
9592     // adjust the x position by -6 pixels (and the y position by 0)
9593     el.alignTo("other-el", "c-bl", [-6, 0]);
9594     </code></pre>
9595          * @param {String/HTMLElement/Roo.Element} element The element to align to.
9596          * @param {String} position The position to align to.
9597          * @param {Array} offsets (optional) Offset the positioning by [x, y]
9598          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
9599          * @return {Roo.Element} this
9600          */
9601         alignTo : function(element, position, offsets, animate){
9602             var xy = this.getAlignToXY(element, position, offsets);
9603             this.setXY(xy, this.preanim(arguments, 3));
9604             return this;
9605         },
9606
9607         /**
9608          * Anchors an element to another element and realigns it when the window is resized.
9609          * @param {String/HTMLElement/Roo.Element} element The element to align to.
9610          * @param {String} position The position to align to.
9611          * @param {Array} offsets (optional) Offset the positioning by [x, y]
9612          * @param {Boolean/Object} animate (optional) True for the default animation or a standard Element animation config object
9613          * @param {Boolean/Number} monitorScroll (optional) True to monitor body scroll and reposition. If this parameter
9614          * is a number, it is used as the buffer delay (defaults to 50ms).
9615          * @param {Function} callback The function to call after the animation finishes
9616          * @return {Roo.Element} this
9617          */
9618         anchorTo : function(el, alignment, offsets, animate, monitorScroll, callback){
9619             var action = function(){
9620                 this.alignTo(el, alignment, offsets, animate);
9621                 Roo.callback(callback, this);
9622             };
9623             Roo.EventManager.onWindowResize(action, this);
9624             var tm = typeof monitorScroll;
9625             if(tm != 'undefined'){
9626                 Roo.EventManager.on(window, 'scroll', action, this,
9627                     {buffer: tm == 'number' ? monitorScroll : 50});
9628             }
9629             action.call(this); // align immediately
9630             return this;
9631         },
9632         /**
9633          * Clears any opacity settings from this element. Required in some cases for IE.
9634          * @return {Roo.Element} this
9635          */
9636         clearOpacity : function(){
9637             if (window.ActiveXObject) {
9638                 if(typeof this.dom.style.filter == 'string' && (/alpha/i).test(this.dom.style.filter)){
9639                     this.dom.style.filter = "";
9640                 }
9641             } else {
9642                 this.dom.style.opacity = "";
9643                 this.dom.style["-moz-opacity"] = "";
9644                 this.dom.style["-khtml-opacity"] = "";
9645             }
9646             return this;
9647         },
9648
9649         /**
9650          * Hide this element - Uses display mode to determine whether to use "display" or "visibility". See {@link #setVisible}.
9651          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
9652          * @return {Roo.Element} this
9653          */
9654         hide : function(animate){
9655             this.setVisible(false, this.preanim(arguments, 0));
9656             return this;
9657         },
9658
9659         /**
9660         * Show this element - Uses display mode to determine whether to use "display" or "visibility". See {@link #setVisible}.
9661         * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
9662          * @return {Roo.Element} this
9663          */
9664         show : function(animate){
9665             this.setVisible(true, this.preanim(arguments, 0));
9666             return this;
9667         },
9668
9669         /**
9670          * @private Test if size has a unit, otherwise appends the default
9671          */
9672         addUnits : function(size){
9673             return Roo.Element.addUnits(size, this.defaultUnit);
9674         },
9675
9676         /**
9677          * Temporarily enables offsets (width,height,x,y) for an element with display:none, use endMeasure() when done.
9678          * @return {Roo.Element} this
9679          */
9680         beginMeasure : function(){
9681             var el = this.dom;
9682             if(el.offsetWidth || el.offsetHeight){
9683                 return this; // offsets work already
9684             }
9685             var changed = [];
9686             var p = this.dom, b = document.body; // start with this element
9687             while((!el.offsetWidth && !el.offsetHeight) && p && p.tagName && p != b){
9688                 var pe = Roo.get(p);
9689                 if(pe.getStyle('display') == 'none'){
9690                     changed.push({el: p, visibility: pe.getStyle("visibility")});
9691                     p.style.visibility = "hidden";
9692                     p.style.display = "block";
9693                 }
9694                 p = p.parentNode;
9695             }
9696             this._measureChanged = changed;
9697             return this;
9698
9699         },
9700
9701         /**
9702          * Restores displays to before beginMeasure was called
9703          * @return {Roo.Element} this
9704          */
9705         endMeasure : function(){
9706             var changed = this._measureChanged;
9707             if(changed){
9708                 for(var i = 0, len = changed.length; i < len; i++) {
9709                     var r = changed[i];
9710                     r.el.style.visibility = r.visibility;
9711                     r.el.style.display = "none";
9712                 }
9713                 this._measureChanged = null;
9714             }
9715             return this;
9716         },
9717
9718         /**
9719         * Update the innerHTML of this element, optionally searching for and processing scripts
9720         * @param {String} html The new HTML
9721         * @param {Boolean} loadScripts (optional) true to look for and process scripts
9722         * @param {Function} callback For async script loading you can be noticed when the update completes
9723         * @return {Roo.Element} this
9724          */
9725         update : function(html, loadScripts, callback){
9726             if(typeof html == "undefined"){
9727                 html = "";
9728             }
9729             if(loadScripts !== true){
9730                 this.dom.innerHTML = html;
9731                 if(typeof callback == "function"){
9732                     callback();
9733                 }
9734                 return this;
9735             }
9736             var id = Roo.id();
9737             var dom = this.dom;
9738
9739             html += '<span id="' + id + '"></span>';
9740
9741             E.onAvailable(id, function(){
9742                 var hd = document.getElementsByTagName("head")[0];
9743                 var re = /(?:<script([^>]*)?>)((\n|\r|.)*?)(?:<\/script>)/ig;
9744                 var srcRe = /\ssrc=([\'\"])(.*?)\1/i;
9745                 var typeRe = /\stype=([\'\"])(.*?)\1/i;
9746
9747                 var match;
9748                 while(match = re.exec(html)){
9749                     var attrs = match[1];
9750                     var srcMatch = attrs ? attrs.match(srcRe) : false;
9751                     if(srcMatch && srcMatch[2]){
9752                        var s = document.createElement("script");
9753                        s.src = srcMatch[2];
9754                        var typeMatch = attrs.match(typeRe);
9755                        if(typeMatch && typeMatch[2]){
9756                            s.type = typeMatch[2];
9757                        }
9758                        hd.appendChild(s);
9759                     }else if(match[2] && match[2].length > 0){
9760                         if(window.execScript) {
9761                            window.execScript(match[2]);
9762                         } else {
9763                             /**
9764                              * eval:var:id
9765                              * eval:var:dom
9766                              * eval:var:html
9767                              * 
9768                              */
9769                            window.eval(match[2]);
9770                         }
9771                     }
9772                 }
9773                 var el = document.getElementById(id);
9774                 if(el){el.parentNode.removeChild(el);}
9775                 if(typeof callback == "function"){
9776                     callback();
9777                 }
9778             });
9779             dom.innerHTML = html.replace(/(?:<script.*?>)((\n|\r|.)*?)(?:<\/script>)/ig, "");
9780             return this;
9781         },
9782
9783         /**
9784          * Direct access to the UpdateManager update() method (takes the same parameters).
9785          * @param {String/Function} url The url for this request or a function to call to get the url
9786          * @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}
9787          * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess)
9788          * @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.
9789          * @return {Roo.Element} this
9790          */
9791         load : function(){
9792             var um = this.getUpdateManager();
9793             um.update.apply(um, arguments);
9794             return this;
9795         },
9796
9797         /**
9798         * Gets this element's UpdateManager
9799         * @return {Roo.UpdateManager} The UpdateManager
9800         */
9801         getUpdateManager : function(){
9802             if(!this.updateManager){
9803                 this.updateManager = new Roo.UpdateManager(this);
9804             }
9805             return this.updateManager;
9806         },
9807
9808         /**
9809          * Disables text selection for this element (normalized across browsers)
9810          * @return {Roo.Element} this
9811          */
9812         unselectable : function(){
9813             this.dom.unselectable = "on";
9814             this.swallowEvent("selectstart", true);
9815             this.applyStyles("-moz-user-select:none;-khtml-user-select:none;");
9816             this.addClass("x-unselectable");
9817             return this;
9818         },
9819
9820         /**
9821         * Calculates the x, y to center this element on the screen
9822         * @return {Array} The x, y values [x, y]
9823         */
9824         getCenterXY : function(){
9825             return this.getAlignToXY(document, 'c-c');
9826         },
9827
9828         /**
9829         * Centers the Element in either the viewport, or another Element.
9830         * @param {String/HTMLElement/Roo.Element} centerIn (optional) The element in which to center the element.
9831         */
9832         center : function(centerIn){
9833             this.alignTo(centerIn || document, 'c-c');
9834             return this;
9835         },
9836
9837         /**
9838          * Tests various css rules/browsers to determine if this element uses a border box
9839          * @return {Boolean}
9840          */
9841         isBorderBox : function(){
9842             return noBoxAdjust[this.dom.tagName.toLowerCase()] || Roo.isBorderBox;
9843         },
9844
9845         /**
9846          * Return a box {x, y, width, height} that can be used to set another elements
9847          * size/location to match this element.
9848          * @param {Boolean} contentBox (optional) If true a box for the content of the element is returned.
9849          * @param {Boolean} local (optional) If true the element's left and top are returned instead of page x/y.
9850          * @return {Object} box An object in the format {x, y, width, height}
9851          */
9852         getBox : function(contentBox, local){
9853             var xy;
9854             if(!local){
9855                 xy = this.getXY();
9856             }else{
9857                 var left = parseInt(this.getStyle("left"), 10) || 0;
9858                 var top = parseInt(this.getStyle("top"), 10) || 0;
9859                 xy = [left, top];
9860             }
9861             var el = this.dom, w = el.offsetWidth, h = el.offsetHeight, bx;
9862             if(!contentBox){
9863                 bx = {x: xy[0], y: xy[1], 0: xy[0], 1: xy[1], width: w, height: h};
9864             }else{
9865                 var l = this.getBorderWidth("l")+this.getPadding("l");
9866                 var r = this.getBorderWidth("r")+this.getPadding("r");
9867                 var t = this.getBorderWidth("t")+this.getPadding("t");
9868                 var b = this.getBorderWidth("b")+this.getPadding("b");
9869                 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)};
9870             }
9871             bx.right = bx.x + bx.width;
9872             bx.bottom = bx.y + bx.height;
9873             return bx;
9874         },
9875
9876         /**
9877          * Returns the sum width of the padding and borders for the passed "sides". See getBorderWidth()
9878          for more information about the sides.
9879          * @param {String} sides
9880          * @return {Number}
9881          */
9882         getFrameWidth : function(sides, onlyContentBox){
9883             return onlyContentBox && Roo.isBorderBox ? 0 : (this.getPadding(sides) + this.getBorderWidth(sides));
9884         },
9885
9886         /**
9887          * 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.
9888          * @param {Object} box The box to fill {x, y, width, height}
9889          * @param {Boolean} adjust (optional) Whether to adjust for box-model issues automatically
9890          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
9891          * @return {Roo.Element} this
9892          */
9893         setBox : function(box, adjust, animate){
9894             var w = box.width, h = box.height;
9895             if((adjust && !this.autoBoxAdjust) && !this.isBorderBox()){
9896                w -= (this.getBorderWidth("lr") + this.getPadding("lr"));
9897                h -= (this.getBorderWidth("tb") + this.getPadding("tb"));
9898             }
9899             this.setBounds(box.x, box.y, w, h, this.preanim(arguments, 2));
9900             return this;
9901         },
9902
9903         /**
9904          * Forces the browser to repaint this element
9905          * @return {Roo.Element} this
9906          */
9907          repaint : function(){
9908             var dom = this.dom;
9909             this.addClass("x-repaint");
9910             setTimeout(function(){
9911                 Roo.get(dom).removeClass("x-repaint");
9912             }, 1);
9913             return this;
9914         },
9915
9916         /**
9917          * Returns an object with properties top, left, right and bottom representing the margins of this element unless sides is passed,
9918          * then it returns the calculated width of the sides (see getPadding)
9919          * @param {String} sides (optional) Any combination of l, r, t, b to get the sum of those sides
9920          * @return {Object/Number}
9921          */
9922         getMargins : function(side){
9923             if(!side){
9924                 return {
9925                     top: parseInt(this.getStyle("margin-top"), 10) || 0,
9926                     left: parseInt(this.getStyle("margin-left"), 10) || 0,
9927                     bottom: parseInt(this.getStyle("margin-bottom"), 10) || 0,
9928                     right: parseInt(this.getStyle("margin-right"), 10) || 0
9929                 };
9930             }else{
9931                 return this.addStyles(side, El.margins);
9932              }
9933         },
9934
9935         // private
9936         addStyles : function(sides, styles){
9937             var val = 0, v, w;
9938             for(var i = 0, len = sides.length; i < len; i++){
9939                 v = this.getStyle(styles[sides.charAt(i)]);
9940                 if(v){
9941                      w = parseInt(v, 10);
9942                      if(w){ val += w; }
9943                 }
9944             }
9945             return val;
9946         },
9947
9948         /**
9949          * Creates a proxy element of this element
9950          * @param {String/Object} config The class name of the proxy element or a DomHelper config object
9951          * @param {String/HTMLElement} renderTo (optional) The element or element id to render the proxy to (defaults to document.body)
9952          * @param {Boolean} matchBox (optional) True to align and size the proxy to this element now (defaults to false)
9953          * @return {Roo.Element} The new proxy element
9954          */
9955         createProxy : function(config, renderTo, matchBox){
9956             if(renderTo){
9957                 renderTo = Roo.getDom(renderTo);
9958             }else{
9959                 renderTo = document.body;
9960             }
9961             config = typeof config == "object" ?
9962                 config : {tag : "div", cls: config};
9963             var proxy = Roo.DomHelper.append(renderTo, config, true);
9964             if(matchBox){
9965                proxy.setBox(this.getBox());
9966             }
9967             return proxy;
9968         },
9969
9970         /**
9971          * Puts a mask over this element to disable user interaction. Requires core.css.
9972          * This method can only be applied to elements which accept child nodes.
9973          * @param {String} msg (optional) A message to display in the mask
9974          * @param {String} msgCls (optional) A css class to apply to the msg element - use no-spinner to hide the spinner on bootstrap
9975          * @return {Element} The mask  element
9976          */
9977         mask : function(msg, msgCls)
9978         {
9979             if(this.getStyle("position") == "static" && this.dom.tagName !== 'BODY'){
9980                 this.setStyle("position", "relative");
9981             }
9982             if(!this._mask){
9983                 this._mask = Roo.DomHelper.append(this.dom, {cls:"roo-el-mask"}, true);
9984             }
9985             
9986             this.addClass("x-masked");
9987             this._mask.setDisplayed(true);
9988             
9989             // we wander
9990             var z = 0;
9991             var dom = this.dom;
9992             while (dom && dom.style) {
9993                 if (!isNaN(parseInt(dom.style.zIndex))) {
9994                     z = Math.max(z, parseInt(dom.style.zIndex));
9995                 }
9996                 dom = dom.parentNode;
9997             }
9998             // if we are masking the body - then it hides everything..
9999             if (this.dom == document.body) {
10000                 z = 1000000;
10001                 this._mask.setWidth(Roo.lib.Dom.getDocumentWidth());
10002                 this._mask.setHeight(Roo.lib.Dom.getDocumentHeight());
10003             }
10004            
10005             if(typeof msg == 'string'){
10006                 if(!this._maskMsg){
10007                     this._maskMsg = Roo.DomHelper.append(this.dom, {
10008                         cls: "roo-el-mask-msg", 
10009                         cn: [
10010                             {
10011                                 tag: 'i',
10012                                 cls: 'fa fa-spinner fa-spin'
10013                             },
10014                             {
10015                                 tag: 'div'
10016                             }   
10017                         ]
10018                     }, true);
10019                 }
10020                 var mm = this._maskMsg;
10021                 mm.dom.className = msgCls ? "roo-el-mask-msg " + msgCls : "roo-el-mask-msg";
10022                 if (mm.dom.lastChild) { // weird IE issue?
10023                     mm.dom.lastChild.innerHTML = msg;
10024                 }
10025                 mm.setDisplayed(true);
10026                 mm.center(this);
10027                 mm.setStyle('z-index', z + 102);
10028             }
10029             if(Roo.isIE && !(Roo.isIE7 && Roo.isStrict) && this.getStyle('height') == 'auto'){ // ie will not expand full height automatically
10030                 this._mask.setHeight(this.getHeight());
10031             }
10032             this._mask.setStyle('z-index', z + 100);
10033             
10034             return this._mask;
10035         },
10036
10037         /**
10038          * Removes a previously applied mask. If removeEl is true the mask overlay is destroyed, otherwise
10039          * it is cached for reuse.
10040          */
10041         unmask : function(removeEl){
10042             if(this._mask){
10043                 if(removeEl === true){
10044                     this._mask.remove();
10045                     delete this._mask;
10046                     if(this._maskMsg){
10047                         this._maskMsg.remove();
10048                         delete this._maskMsg;
10049                     }
10050                 }else{
10051                     this._mask.setDisplayed(false);
10052                     if(this._maskMsg){
10053                         this._maskMsg.setDisplayed(false);
10054                     }
10055                 }
10056             }
10057             this.removeClass("x-masked");
10058         },
10059
10060         /**
10061          * Returns true if this element is masked
10062          * @return {Boolean}
10063          */
10064         isMasked : function(){
10065             return this._mask && this._mask.isVisible();
10066         },
10067
10068         /**
10069          * Creates an iframe shim for this element to keep selects and other windowed objects from
10070          * showing through.
10071          * @return {Roo.Element} The new shim element
10072          */
10073         createShim : function(){
10074             var el = document.createElement('iframe');
10075             el.frameBorder = 'no';
10076             el.className = 'roo-shim';
10077             if(Roo.isIE && Roo.isSecure){
10078                 el.src = Roo.SSL_SECURE_URL;
10079             }
10080             var shim = Roo.get(this.dom.parentNode.insertBefore(el, this.dom));
10081             shim.autoBoxAdjust = false;
10082             return shim;
10083         },
10084
10085         /**
10086          * Removes this element from the DOM and deletes it from the cache
10087          */
10088         remove : function(){
10089             if(this.dom.parentNode){
10090                 this.dom.parentNode.removeChild(this.dom);
10091             }
10092             delete El.cache[this.dom.id];
10093         },
10094
10095         /**
10096          * Sets up event handlers to add and remove a css class when the mouse is over this element
10097          * @param {String} className
10098          * @param {Boolean} preventFlicker (optional) If set to true, it prevents flickering by filtering
10099          * mouseout events for children elements
10100          * @return {Roo.Element} this
10101          */
10102         addClassOnOver : function(className, preventFlicker){
10103             this.on("mouseover", function(){
10104                 Roo.fly(this, '_internal').addClass(className);
10105             }, this.dom);
10106             var removeFn = function(e){
10107                 if(preventFlicker !== true || !e.within(this, true)){
10108                     Roo.fly(this, '_internal').removeClass(className);
10109                 }
10110             };
10111             this.on("mouseout", removeFn, this.dom);
10112             return this;
10113         },
10114
10115         /**
10116          * Sets up event handlers to add and remove a css class when this element has the focus
10117          * @param {String} className
10118          * @return {Roo.Element} this
10119          */
10120         addClassOnFocus : function(className){
10121             this.on("focus", function(){
10122                 Roo.fly(this, '_internal').addClass(className);
10123             }, this.dom);
10124             this.on("blur", function(){
10125                 Roo.fly(this, '_internal').removeClass(className);
10126             }, this.dom);
10127             return this;
10128         },
10129         /**
10130          * 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)
10131          * @param {String} className
10132          * @return {Roo.Element} this
10133          */
10134         addClassOnClick : function(className){
10135             var dom = this.dom;
10136             this.on("mousedown", function(){
10137                 Roo.fly(dom, '_internal').addClass(className);
10138                 var d = Roo.get(document);
10139                 var fn = function(){
10140                     Roo.fly(dom, '_internal').removeClass(className);
10141                     d.removeListener("mouseup", fn);
10142                 };
10143                 d.on("mouseup", fn);
10144             });
10145             return this;
10146         },
10147
10148         /**
10149          * Stops the specified event from bubbling and optionally prevents the default action
10150          * @param {String} eventName
10151          * @param {Boolean} preventDefault (optional) true to prevent the default action too
10152          * @return {Roo.Element} this
10153          */
10154         swallowEvent : function(eventName, preventDefault){
10155             var fn = function(e){
10156                 e.stopPropagation();
10157                 if(preventDefault){
10158                     e.preventDefault();
10159                 }
10160             };
10161             if(eventName instanceof Array){
10162                 for(var i = 0, len = eventName.length; i < len; i++){
10163                      this.on(eventName[i], fn);
10164                 }
10165                 return this;
10166             }
10167             this.on(eventName, fn);
10168             return this;
10169         },
10170
10171         /**
10172          * @private
10173          */
10174         fitToParentDelegate : Roo.emptyFn, // keep a reference to the fitToParent delegate
10175
10176         /**
10177          * Sizes this element to its parent element's dimensions performing
10178          * neccessary box adjustments.
10179          * @param {Boolean} monitorResize (optional) If true maintains the fit when the browser window is resized.
10180          * @param {String/HTMLElment/Element} targetParent (optional) The target parent, default to the parentNode.
10181          * @return {Roo.Element} this
10182          */
10183         fitToParent : function(monitorResize, targetParent) {
10184           Roo.EventManager.removeResizeListener(this.fitToParentDelegate); // always remove previous fitToParent delegate from onWindowResize
10185           this.fitToParentDelegate = Roo.emptyFn; // remove reference to previous delegate
10186           if (monitorResize === true && !this.dom.parentNode) { // check if this Element still exists
10187             return this;
10188           }
10189           var p = Roo.get(targetParent || this.dom.parentNode);
10190           this.setSize(p.getComputedWidth() - p.getFrameWidth('lr'), p.getComputedHeight() - p.getFrameWidth('tb'));
10191           if (monitorResize === true) {
10192             this.fitToParentDelegate = this.fitToParent.createDelegate(this, [true, targetParent]);
10193             Roo.EventManager.onWindowResize(this.fitToParentDelegate);
10194           }
10195           return this;
10196         },
10197
10198         /**
10199          * Gets the next sibling, skipping text nodes
10200          * @return {HTMLElement} The next sibling or null
10201          */
10202         getNextSibling : function(){
10203             var n = this.dom.nextSibling;
10204             while(n && n.nodeType != 1){
10205                 n = n.nextSibling;
10206             }
10207             return n;
10208         },
10209
10210         /**
10211          * Gets the previous sibling, skipping text nodes
10212          * @return {HTMLElement} The previous sibling or null
10213          */
10214         getPrevSibling : function(){
10215             var n = this.dom.previousSibling;
10216             while(n && n.nodeType != 1){
10217                 n = n.previousSibling;
10218             }
10219             return n;
10220         },
10221
10222
10223         /**
10224          * Appends the passed element(s) to this element
10225          * @param {String/HTMLElement/Array/Element/CompositeElement} el
10226          * @return {Roo.Element} this
10227          */
10228         appendChild: function(el){
10229             el = Roo.get(el);
10230             el.appendTo(this);
10231             return this;
10232         },
10233
10234         /**
10235          * Creates the passed DomHelper config and appends it to this element or optionally inserts it before the passed child element.
10236          * @param {Object} config DomHelper element config object.  If no tag is specified (e.g., {tag:'input'}) then a div will be
10237          * automatically generated with the specified attributes.
10238          * @param {HTMLElement} insertBefore (optional) a child element of this element
10239          * @param {Boolean} returnDom (optional) true to return the dom node instead of creating an Element
10240          * @return {Roo.Element} The new child element
10241          */
10242         createChild: function(config, insertBefore, returnDom){
10243             config = config || {tag:'div'};
10244             if(insertBefore){
10245                 return Roo.DomHelper.insertBefore(insertBefore, config, returnDom !== true);
10246             }
10247             return Roo.DomHelper[!this.dom.firstChild ? 'overwrite' : 'append'](this.dom, config,  returnDom !== true);
10248         },
10249
10250         /**
10251          * Appends this element to the passed element
10252          * @param {String/HTMLElement/Element} el The new parent element
10253          * @return {Roo.Element} this
10254          */
10255         appendTo: function(el){
10256             el = Roo.getDom(el);
10257             el.appendChild(this.dom);
10258             return this;
10259         },
10260
10261         /**
10262          * Inserts this element before the passed element in the DOM
10263          * @param {String/HTMLElement/Element} el The element to insert before
10264          * @return {Roo.Element} this
10265          */
10266         insertBefore: function(el){
10267             el = Roo.getDom(el);
10268             el.parentNode.insertBefore(this.dom, el);
10269             return this;
10270         },
10271
10272         /**
10273          * Inserts this element after the passed element in the DOM
10274          * @param {String/HTMLElement/Element} el The element to insert after
10275          * @return {Roo.Element} this
10276          */
10277         insertAfter: function(el){
10278             el = Roo.getDom(el);
10279             el.parentNode.insertBefore(this.dom, el.nextSibling);
10280             return this;
10281         },
10282
10283         /**
10284          * Inserts (or creates) an element (or DomHelper config) as the first child of the this element
10285          * @param {String/HTMLElement/Element/Object} el The id or element to insert or a DomHelper config to create and insert
10286          * @return {Roo.Element} The new child
10287          */
10288         insertFirst: function(el, returnDom){
10289             el = el || {};
10290             if(typeof el == 'object' && !el.nodeType){ // dh config
10291                 return this.createChild(el, this.dom.firstChild, returnDom);
10292             }else{
10293                 el = Roo.getDom(el);
10294                 this.dom.insertBefore(el, this.dom.firstChild);
10295                 return !returnDom ? Roo.get(el) : el;
10296             }
10297         },
10298
10299         /**
10300          * Inserts (or creates) the passed element (or DomHelper config) as a sibling of this element
10301          * @param {String/HTMLElement/Element/Object} el The id or element to insert or a DomHelper config to create and insert
10302          * @param {String} where (optional) 'before' or 'after' defaults to before
10303          * @param {Boolean} returnDom (optional) True to return the raw DOM element instead of Roo.Element
10304          * @return {Roo.Element} the inserted Element
10305          */
10306         insertSibling: function(el, where, returnDom){
10307             where = where ? where.toLowerCase() : 'before';
10308             el = el || {};
10309             var rt, refNode = where == 'before' ? this.dom : this.dom.nextSibling;
10310
10311             if(typeof el == 'object' && !el.nodeType){ // dh config
10312                 if(where == 'after' && !this.dom.nextSibling){
10313                     rt = Roo.DomHelper.append(this.dom.parentNode, el, !returnDom);
10314                 }else{
10315                     rt = Roo.DomHelper[where == 'after' ? 'insertAfter' : 'insertBefore'](this.dom, el, !returnDom);
10316                 }
10317
10318             }else{
10319                 rt = this.dom.parentNode.insertBefore(Roo.getDom(el),
10320                             where == 'before' ? this.dom : this.dom.nextSibling);
10321                 if(!returnDom){
10322                     rt = Roo.get(rt);
10323                 }
10324             }
10325             return rt;
10326         },
10327
10328         /**
10329          * Creates and wraps this element with another element
10330          * @param {Object} config (optional) DomHelper element config object for the wrapper element or null for an empty div
10331          * @param {Boolean} returnDom (optional) True to return the raw DOM element instead of Roo.Element
10332          * @return {HTMLElement/Element} The newly created wrapper element
10333          */
10334         wrap: function(config, returnDom){
10335             if(!config){
10336                 config = {tag: "div"};
10337             }
10338             var newEl = Roo.DomHelper.insertBefore(this.dom, config, !returnDom);
10339             newEl.dom ? newEl.dom.appendChild(this.dom) : newEl.appendChild(this.dom);
10340             return newEl;
10341         },
10342
10343         /**
10344          * Replaces the passed element with this element
10345          * @param {String/HTMLElement/Element} el The element to replace
10346          * @return {Roo.Element} this
10347          */
10348         replace: function(el){
10349             el = Roo.get(el);
10350             this.insertBefore(el);
10351             el.remove();
10352             return this;
10353         },
10354
10355         /**
10356          * Inserts an html fragment into this element
10357          * @param {String} where Where to insert the html in relation to the this element - beforeBegin, afterBegin, beforeEnd, afterEnd.
10358          * @param {String} html The HTML fragment
10359          * @param {Boolean} returnEl True to return an Roo.Element
10360          * @return {HTMLElement/Roo.Element} The inserted node (or nearest related if more than 1 inserted)
10361          */
10362         insertHtml : function(where, html, returnEl){
10363             var el = Roo.DomHelper.insertHtml(where, this.dom, html);
10364             return returnEl ? Roo.get(el) : el;
10365         },
10366
10367         /**
10368          * Sets the passed attributes as attributes of this element (a style attribute can be a string, object or function)
10369          * @param {Object} o The object with the attributes
10370          * @param {Boolean} useSet (optional) false to override the default setAttribute to use expandos.
10371          * @return {Roo.Element} this
10372          */
10373         set : function(o, useSet){
10374             var el = this.dom;
10375             useSet = typeof useSet == 'undefined' ? (el.setAttribute ? true : false) : useSet;
10376             for(var attr in o){
10377                 if(attr == "style" || typeof o[attr] == "function")  { continue; }
10378                 if(attr=="cls"){
10379                     el.className = o["cls"];
10380                 }else{
10381                     if(useSet) {
10382                         el.setAttribute(attr, o[attr]);
10383                     } else {
10384                         el[attr] = o[attr];
10385                     }
10386                 }
10387             }
10388             if(o.style){
10389                 Roo.DomHelper.applyStyles(el, o.style);
10390             }
10391             return this;
10392         },
10393
10394         /**
10395          * Convenience method for constructing a KeyMap
10396          * @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:
10397          *                                  {key: (number or array), shift: (true/false), ctrl: (true/false), alt: (true/false)}
10398          * @param {Function} fn The function to call
10399          * @param {Object} scope (optional) The scope of the function
10400          * @return {Roo.KeyMap} The KeyMap created
10401          */
10402         addKeyListener : function(key, fn, scope){
10403             var config;
10404             if(typeof key != "object" || key instanceof Array){
10405                 config = {
10406                     key: key,
10407                     fn: fn,
10408                     scope: scope
10409                 };
10410             }else{
10411                 config = {
10412                     key : key.key,
10413                     shift : key.shift,
10414                     ctrl : key.ctrl,
10415                     alt : key.alt,
10416                     fn: fn,
10417                     scope: scope
10418                 };
10419             }
10420             return new Roo.KeyMap(this, config);
10421         },
10422
10423         /**
10424          * Creates a KeyMap for this element
10425          * @param {Object} config The KeyMap config. See {@link Roo.KeyMap} for more details
10426          * @return {Roo.KeyMap} The KeyMap created
10427          */
10428         addKeyMap : function(config){
10429             return new Roo.KeyMap(this, config);
10430         },
10431
10432         /**
10433          * Returns true if this element is scrollable.
10434          * @return {Boolean}
10435          */
10436          isScrollable : function(){
10437             var dom = this.dom;
10438             return dom.scrollHeight > dom.clientHeight || dom.scrollWidth > dom.clientWidth;
10439         },
10440
10441         /**
10442          * 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().
10443          * @param {String} side Either "left" for scrollLeft values or "top" for scrollTop values.
10444          * @param {Number} value The new scroll value
10445          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
10446          * @return {Element} this
10447          */
10448
10449         scrollTo : function(side, value, animate){
10450             var prop = side.toLowerCase() == "left" ? "scrollLeft" : "scrollTop";
10451             if(!animate || !A){
10452                 this.dom[prop] = value;
10453             }else{
10454                 var to = prop == "scrollLeft" ? [value, this.dom.scrollTop] : [this.dom.scrollLeft, value];
10455                 this.anim({scroll: {"to": to}}, this.preanim(arguments, 2), 'scroll');
10456             }
10457             return this;
10458         },
10459
10460         /**
10461          * Scrolls this element the specified direction. Does bounds checking to make sure the scroll is
10462          * within this element's scrollable range.
10463          * @param {String} direction Possible values are: "l","left" - "r","right" - "t","top","up" - "b","bottom","down".
10464          * @param {Number} distance How far to scroll the element in pixels
10465          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
10466          * @return {Boolean} Returns true if a scroll was triggered or false if the element
10467          * was scrolled as far as it could go.
10468          */
10469          scroll : function(direction, distance, animate){
10470              if(!this.isScrollable()){
10471                  return;
10472              }
10473              var el = this.dom;
10474              var l = el.scrollLeft, t = el.scrollTop;
10475              var w = el.scrollWidth, h = el.scrollHeight;
10476              var cw = el.clientWidth, ch = el.clientHeight;
10477              direction = direction.toLowerCase();
10478              var scrolled = false;
10479              var a = this.preanim(arguments, 2);
10480              switch(direction){
10481                  case "l":
10482                  case "left":
10483                      if(w - l > cw){
10484                          var v = Math.min(l + distance, w-cw);
10485                          this.scrollTo("left", v, a);
10486                          scrolled = true;
10487                      }
10488                      break;
10489                 case "r":
10490                 case "right":
10491                      if(l > 0){
10492                          var v = Math.max(l - distance, 0);
10493                          this.scrollTo("left", v, a);
10494                          scrolled = true;
10495                      }
10496                      break;
10497                 case "t":
10498                 case "top":
10499                 case "up":
10500                      if(t > 0){
10501                          var v = Math.max(t - distance, 0);
10502                          this.scrollTo("top", v, a);
10503                          scrolled = true;
10504                      }
10505                      break;
10506                 case "b":
10507                 case "bottom":
10508                 case "down":
10509                      if(h - t > ch){
10510                          var v = Math.min(t + distance, h-ch);
10511                          this.scrollTo("top", v, a);
10512                          scrolled = true;
10513                      }
10514                      break;
10515              }
10516              return scrolled;
10517         },
10518
10519         /**
10520          * Translates the passed page coordinates into left/top css values for this element
10521          * @param {Number/Array} x The page x or an array containing [x, y]
10522          * @param {Number} y The page y
10523          * @return {Object} An object with left and top properties. e.g. {left: (value), top: (value)}
10524          */
10525         translatePoints : function(x, y){
10526             if(typeof x == 'object' || x instanceof Array){
10527                 y = x[1]; x = x[0];
10528             }
10529             var p = this.getStyle('position');
10530             var o = this.getXY();
10531
10532             var l = parseInt(this.getStyle('left'), 10);
10533             var t = parseInt(this.getStyle('top'), 10);
10534
10535             if(isNaN(l)){
10536                 l = (p == "relative") ? 0 : this.dom.offsetLeft;
10537             }
10538             if(isNaN(t)){
10539                 t = (p == "relative") ? 0 : this.dom.offsetTop;
10540             }
10541
10542             return {left: (x - o[0] + l), top: (y - o[1] + t)};
10543         },
10544
10545         /**
10546          * Returns the current scroll position of the element.
10547          * @return {Object} An object containing the scroll position in the format {left: (scrollLeft), top: (scrollTop)}
10548          */
10549         getScroll : function(){
10550             var d = this.dom, doc = document;
10551             if(d == doc || d == doc.body){
10552                 var l = window.pageXOffset || doc.documentElement.scrollLeft || doc.body.scrollLeft || 0;
10553                 var t = window.pageYOffset || doc.documentElement.scrollTop || doc.body.scrollTop || 0;
10554                 return {left: l, top: t};
10555             }else{
10556                 return {left: d.scrollLeft, top: d.scrollTop};
10557             }
10558         },
10559
10560         /**
10561          * Return the CSS color for the specified CSS attribute. rgb, 3 digit (like #fff) and valid values
10562          * are convert to standard 6 digit hex color.
10563          * @param {String} attr The css attribute
10564          * @param {String} defaultValue The default value to use when a valid color isn't found
10565          * @param {String} prefix (optional) defaults to #. Use an empty string when working with
10566          * YUI color anims.
10567          */
10568         getColor : function(attr, defaultValue, prefix){
10569             var v = this.getStyle(attr);
10570             if(!v || v == "transparent" || v == "inherit") {
10571                 return defaultValue;
10572             }
10573             var color = typeof prefix == "undefined" ? "#" : prefix;
10574             if(v.substr(0, 4) == "rgb("){
10575                 var rvs = v.slice(4, v.length -1).split(",");
10576                 for(var i = 0; i < 3; i++){
10577                     var h = parseInt(rvs[i]).toString(16);
10578                     if(h < 16){
10579                         h = "0" + h;
10580                     }
10581                     color += h;
10582                 }
10583             } else {
10584                 if(v.substr(0, 1) == "#"){
10585                     if(v.length == 4) {
10586                         for(var i = 1; i < 4; i++){
10587                             var c = v.charAt(i);
10588                             color +=  c + c;
10589                         }
10590                     }else if(v.length == 7){
10591                         color += v.substr(1);
10592                     }
10593                 }
10594             }
10595             return(color.length > 5 ? color.toLowerCase() : defaultValue);
10596         },
10597
10598         /**
10599          * Wraps the specified element with a special markup/CSS block that renders by default as a gray container with a
10600          * gradient background, rounded corners and a 4-way shadow.
10601          * @param {String} class (optional) A base CSS class to apply to the containing wrapper element (defaults to 'x-box').
10602          * Note that there are a number of CSS rules that are dependent on this name to make the overall effect work,
10603          * so if you supply an alternate base class, make sure you also supply all of the necessary rules.
10604          * @return {Roo.Element} this
10605          */
10606         boxWrap : function(cls){
10607             cls = cls || 'x-box';
10608             var el = Roo.get(this.insertHtml('beforeBegin', String.format('<div class="{0}">'+El.boxMarkup+'</div>', cls)));
10609             el.child('.'+cls+'-mc').dom.appendChild(this.dom);
10610             return el;
10611         },
10612
10613         /**
10614          * Returns the value of a namespaced attribute from the element's underlying DOM node.
10615          * @param {String} namespace The namespace in which to look for the attribute
10616          * @param {String} name The attribute name
10617          * @return {String} The attribute value
10618          */
10619         getAttributeNS : Roo.isIE ? function(ns, name){
10620             var d = this.dom;
10621             var type = typeof d[ns+":"+name];
10622             if(type != 'undefined' && type != 'unknown'){
10623                 return d[ns+":"+name];
10624             }
10625             return d[name];
10626         } : function(ns, name){
10627             var d = this.dom;
10628             return d.getAttributeNS(ns, name) || d.getAttribute(ns+":"+name) || d.getAttribute(name) || d[name];
10629         },
10630         
10631         
10632         /**
10633          * Sets or Returns the value the dom attribute value
10634          * @param {String|Object} name The attribute name (or object to set multiple attributes)
10635          * @param {String} value (optional) The value to set the attribute to
10636          * @return {String} The attribute value
10637          */
10638         attr : function(name){
10639             if (arguments.length > 1) {
10640                 this.dom.setAttribute(name, arguments[1]);
10641                 return arguments[1];
10642             }
10643             if (typeof(name) == 'object') {
10644                 for(var i in name) {
10645                     this.attr(i, name[i]);
10646                 }
10647                 return name;
10648             }
10649             
10650             
10651             if (!this.dom.hasAttribute(name)) {
10652                 return undefined;
10653             }
10654             return this.dom.getAttribute(name);
10655         }
10656         
10657         
10658         
10659     };
10660
10661     var ep = El.prototype;
10662
10663     /**
10664      * Appends an event handler (Shorthand for addListener)
10665      * @param {String}   eventName     The type of event to append
10666      * @param {Function} fn        The method the event invokes
10667      * @param {Object} scope       (optional) The scope (this object) of the fn
10668      * @param {Object}   options   (optional)An object with standard {@link Roo.EventManager#addListener} options
10669      * @method
10670      */
10671     ep.on = ep.addListener;
10672         // backwards compat
10673     ep.mon = ep.addListener;
10674
10675     /**
10676      * Removes an event handler from this element (shorthand for removeListener)
10677      * @param {String} eventName the type of event to remove
10678      * @param {Function} fn the method the event invokes
10679      * @return {Roo.Element} this
10680      * @method
10681      */
10682     ep.un = ep.removeListener;
10683
10684     /**
10685      * true to automatically adjust width and height settings for box-model issues (default to true)
10686      */
10687     ep.autoBoxAdjust = true;
10688
10689     // private
10690     El.unitPattern = /\d+(px|em|%|en|ex|pt|in|cm|mm|pc)$/i;
10691
10692     // private
10693     El.addUnits = function(v, defaultUnit){
10694         if(v === "" || v == "auto"){
10695             return v;
10696         }
10697         if(v === undefined){
10698             return '';
10699         }
10700         if(typeof v == "number" || !El.unitPattern.test(v)){
10701             return v + (defaultUnit || 'px');
10702         }
10703         return v;
10704     };
10705
10706     // special markup used throughout Roo when box wrapping elements
10707     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>';
10708     /**
10709      * Visibility mode constant - Use visibility to hide element
10710      * @static
10711      * @type Number
10712      */
10713     El.VISIBILITY = 1;
10714     /**
10715      * Visibility mode constant - Use display to hide element
10716      * @static
10717      * @type Number
10718      */
10719     El.DISPLAY = 2;
10720
10721     El.borders = {l: "border-left-width", r: "border-right-width", t: "border-top-width", b: "border-bottom-width"};
10722     El.paddings = {l: "padding-left", r: "padding-right", t: "padding-top", b: "padding-bottom"};
10723     El.margins = {l: "margin-left", r: "margin-right", t: "margin-top", b: "margin-bottom"};
10724
10725
10726
10727     /**
10728      * @private
10729      */
10730     El.cache = {};
10731
10732     var docEl;
10733
10734     /**
10735      * Static method to retrieve Element objects. Uses simple caching to consistently return the same object.
10736      * Automatically fixes if an object was recreated with the same id via AJAX or DOM.
10737      * @param {String/HTMLElement/Element} el The id of the node, a DOM Node or an existing Element.
10738      * @return {Element} The Element object
10739      * @static
10740      */
10741     El.get = function(el){
10742         var ex, elm, id;
10743         if(!el){ return null; }
10744         if(typeof el == "string"){ // element id
10745             if(!(elm = document.getElementById(el))){
10746                 return null;
10747             }
10748             if(ex = El.cache[el]){
10749                 ex.dom = elm;
10750             }else{
10751                 ex = El.cache[el] = new El(elm);
10752             }
10753             return ex;
10754         }else if(el.tagName){ // dom element
10755             if(!(id = el.id)){
10756                 id = Roo.id(el);
10757             }
10758             if(ex = El.cache[id]){
10759                 ex.dom = el;
10760             }else{
10761                 ex = El.cache[id] = new El(el);
10762             }
10763             return ex;
10764         }else if(el instanceof El){
10765             if(el != docEl){
10766                 el.dom = document.getElementById(el.id) || el.dom; // refresh dom element in case no longer valid,
10767                                                               // catch case where it hasn't been appended
10768                 El.cache[el.id] = el; // in case it was created directly with Element(), let's cache it
10769             }
10770             return el;
10771         }else if(el.isComposite){
10772             return el;
10773         }else if(el instanceof Array){
10774             return El.select(el);
10775         }else if(el == document){
10776             // create a bogus element object representing the document object
10777             if(!docEl){
10778                 var f = function(){};
10779                 f.prototype = El.prototype;
10780                 docEl = new f();
10781                 docEl.dom = document;
10782             }
10783             return docEl;
10784         }
10785         return null;
10786     };
10787
10788     // private
10789     El.uncache = function(el){
10790         for(var i = 0, a = arguments, len = a.length; i < len; i++) {
10791             if(a[i]){
10792                 delete El.cache[a[i].id || a[i]];
10793             }
10794         }
10795     };
10796
10797     // private
10798     // Garbage collection - uncache elements/purge listeners on orphaned elements
10799     // so we don't hold a reference and cause the browser to retain them
10800     El.garbageCollect = function(){
10801         if(!Roo.enableGarbageCollector){
10802             clearInterval(El.collectorThread);
10803             return;
10804         }
10805         for(var eid in El.cache){
10806             var el = El.cache[eid], d = el.dom;
10807             // -------------------------------------------------------
10808             // Determining what is garbage:
10809             // -------------------------------------------------------
10810             // !d
10811             // dom node is null, definitely garbage
10812             // -------------------------------------------------------
10813             // !d.parentNode
10814             // no parentNode == direct orphan, definitely garbage
10815             // -------------------------------------------------------
10816             // !d.offsetParent && !document.getElementById(eid)
10817             // display none elements have no offsetParent so we will
10818             // also try to look it up by it's id. However, check
10819             // offsetParent first so we don't do unneeded lookups.
10820             // This enables collection of elements that are not orphans
10821             // directly, but somewhere up the line they have an orphan
10822             // parent.
10823             // -------------------------------------------------------
10824             if(!d || !d.parentNode || (!d.offsetParent && !document.getElementById(eid))){
10825                 delete El.cache[eid];
10826                 if(d && Roo.enableListenerCollection){
10827                     E.purgeElement(d);
10828                 }
10829             }
10830         }
10831     }
10832     El.collectorThreadId = setInterval(El.garbageCollect, 30000);
10833
10834
10835     // dom is optional
10836     El.Flyweight = function(dom){
10837         this.dom = dom;
10838     };
10839     El.Flyweight.prototype = El.prototype;
10840
10841     El._flyweights = {};
10842     /**
10843      * Gets the globally shared flyweight Element, with the passed node as the active element. Do not store a reference to this element -
10844      * the dom node can be overwritten by other code.
10845      * @param {String/HTMLElement} el The dom node or id
10846      * @param {String} named (optional) Allows for creation of named reusable flyweights to
10847      *                                  prevent conflicts (e.g. internally Roo uses "_internal")
10848      * @static
10849      * @return {Element} The shared Element object
10850      */
10851     El.fly = function(el, named){
10852         named = named || '_global';
10853         el = Roo.getDom(el);
10854         if(!el){
10855             return null;
10856         }
10857         if(!El._flyweights[named]){
10858             El._flyweights[named] = new El.Flyweight();
10859         }
10860         El._flyweights[named].dom = el;
10861         return El._flyweights[named];
10862     };
10863
10864     /**
10865      * Static method to retrieve Element objects. Uses simple caching to consistently return the same object.
10866      * Automatically fixes if an object was recreated with the same id via AJAX or DOM.
10867      * Shorthand of {@link Roo.Element#get}
10868      * @param {String/HTMLElement/Element} el The id of the node, a DOM Node or an existing Element.
10869      * @return {Element} The Element object
10870      * @member Roo
10871      * @method get
10872      */
10873     Roo.get = El.get;
10874     /**
10875      * Gets the globally shared flyweight Element, with the passed node as the active element. Do not store a reference to this element -
10876      * the dom node can be overwritten by other code.
10877      * Shorthand of {@link Roo.Element#fly}
10878      * @param {String/HTMLElement} el The dom node or id
10879      * @param {String} named (optional) Allows for creation of named reusable flyweights to
10880      *                                  prevent conflicts (e.g. internally Roo uses "_internal")
10881      * @static
10882      * @return {Element} The shared Element object
10883      * @member Roo
10884      * @method fly
10885      */
10886     Roo.fly = El.fly;
10887
10888     // speedy lookup for elements never to box adjust
10889     var noBoxAdjust = Roo.isStrict ? {
10890         select:1
10891     } : {
10892         input:1, select:1, textarea:1
10893     };
10894     if(Roo.isIE || Roo.isGecko){
10895         noBoxAdjust['button'] = 1;
10896     }
10897
10898
10899     Roo.EventManager.on(window, 'unload', function(){
10900         delete El.cache;
10901         delete El._flyweights;
10902     });
10903 })();
10904
10905
10906
10907
10908 if(Roo.DomQuery){
10909     Roo.Element.selectorFunction = Roo.DomQuery.select;
10910 }
10911
10912 Roo.Element.select = function(selector, unique, root){
10913     var els;
10914     if(typeof selector == "string"){
10915         els = Roo.Element.selectorFunction(selector, root);
10916     }else if(selector.length !== undefined){
10917         els = selector;
10918     }else{
10919         throw "Invalid selector";
10920     }
10921     if(unique === true){
10922         return new Roo.CompositeElement(els);
10923     }else{
10924         return new Roo.CompositeElementLite(els);
10925     }
10926 };
10927 /**
10928  * Selects elements based on the passed CSS selector to enable working on them as 1.
10929  * @param {String/Array} selector The CSS selector or an array of elements
10930  * @param {Boolean} unique (optional) true to create a unique Roo.Element for each element (defaults to a shared flyweight object)
10931  * @param {HTMLElement/String} root (optional) The root element of the query or id of the root
10932  * @return {CompositeElementLite/CompositeElement}
10933  * @member Roo
10934  * @method select
10935  */
10936 Roo.select = Roo.Element.select;
10937
10938
10939
10940
10941
10942
10943
10944
10945
10946
10947
10948
10949
10950
10951 /*
10952  * Based on:
10953  * Ext JS Library 1.1.1
10954  * Copyright(c) 2006-2007, Ext JS, LLC.
10955  *
10956  * Originally Released Under LGPL - original licence link has changed is not relivant.
10957  *
10958  * Fork - LGPL
10959  * <script type="text/javascript">
10960  */
10961
10962
10963
10964 //Notifies Element that fx methods are available
10965 Roo.enableFx = true;
10966
10967 /**
10968  * @class Roo.Fx
10969  * <p>A class to provide basic animation and visual effects support.  <b>Note:</b> This class is automatically applied
10970  * to the {@link Roo.Element} interface when included, so all effects calls should be performed via Element.
10971  * Conversely, since the effects are not actually defined in Element, Roo.Fx <b>must</b> be included in order for the 
10972  * Element effects to work.</p><br/>
10973  *
10974  * <p>It is important to note that although the Fx methods and many non-Fx Element methods support "method chaining" in that
10975  * they return the Element object itself as the method return value, it is not always possible to mix the two in a single
10976  * method chain.  The Fx methods use an internal effects queue so that each effect can be properly timed and sequenced.
10977  * Non-Fx methods, on the other hand, have no such internal queueing and will always execute immediately.  For this reason,
10978  * while it may be possible to mix certain Fx and non-Fx method calls in a single chain, it may not always provide the
10979  * expected results and should be done with care.</p><br/>
10980  *
10981  * <p>Motion effects support 8-way anchoring, meaning that you can choose one of 8 different anchor points on the Element
10982  * that will serve as either the start or end point of the animation.  Following are all of the supported anchor positions:</p>
10983 <pre>
10984 Value  Description
10985 -----  -----------------------------
10986 tl     The top left corner
10987 t      The center of the top edge
10988 tr     The top right corner
10989 l      The center of the left edge
10990 r      The center of the right edge
10991 bl     The bottom left corner
10992 b      The center of the bottom edge
10993 br     The bottom right corner
10994 </pre>
10995  * <b>Although some Fx methods accept specific custom config parameters, the ones shown in the Config Options section
10996  * below are common options that can be passed to any Fx method.</b>
10997  * @cfg {Function} callback A function called when the effect is finished
10998  * @cfg {Object} scope The scope of the effect function
10999  * @cfg {String} easing A valid Easing value for the effect
11000  * @cfg {String} afterCls A css class to apply after the effect
11001  * @cfg {Number} duration The length of time (in seconds) that the effect should last
11002  * @cfg {Boolean} remove Whether the Element should be removed from the DOM and destroyed after the effect finishes
11003  * @cfg {Boolean} useDisplay Whether to use the <i>display</i> CSS property instead of <i>visibility</i> when hiding Elements (only applies to 
11004  * effects that end with the element being visually hidden, ignored otherwise)
11005  * @cfg {String/Object/Function} afterStyle A style specification string, e.g. "width:100px", or an object in the form {width:"100px"}, or
11006  * a function which returns such a specification that will be applied to the Element after the effect finishes
11007  * @cfg {Boolean} block Whether the effect should block other effects from queueing while it runs
11008  * @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
11009  * @cfg {Boolean} stopFx Whether subsequent effects should be stopped and removed after the current effect finishes
11010  */
11011 Roo.Fx = {
11012         /**
11013          * Slides the element into view.  An anchor point can be optionally passed to set the point of
11014          * origin for the slide effect.  This function automatically handles wrapping the element with
11015          * a fixed-size container if needed.  See the Fx class overview for valid anchor point options.
11016          * Usage:
11017          *<pre><code>
11018 // default: slide the element in from the top
11019 el.slideIn();
11020
11021 // custom: slide the element in from the right with a 2-second duration
11022 el.slideIn('r', { duration: 2 });
11023
11024 // common config options shown with default values
11025 el.slideIn('t', {
11026     easing: 'easeOut',
11027     duration: .5
11028 });
11029 </code></pre>
11030          * @param {String} anchor (optional) One of the valid Fx anchor positions (defaults to top: 't')
11031          * @param {Object} options (optional) Object literal with any of the Fx config options
11032          * @return {Roo.Element} The Element
11033          */
11034     slideIn : function(anchor, o){
11035         var el = this.getFxEl();
11036         o = o || {};
11037
11038         el.queueFx(o, function(){
11039
11040             anchor = anchor || "t";
11041
11042             // fix display to visibility
11043             this.fixDisplay();
11044
11045             // restore values after effect
11046             var r = this.getFxRestore();
11047             var b = this.getBox();
11048             // fixed size for slide
11049             this.setSize(b);
11050
11051             // wrap if needed
11052             var wrap = this.fxWrap(r.pos, o, "hidden");
11053
11054             var st = this.dom.style;
11055             st.visibility = "visible";
11056             st.position = "absolute";
11057
11058             // clear out temp styles after slide and unwrap
11059             var after = function(){
11060                 el.fxUnwrap(wrap, r.pos, o);
11061                 st.width = r.width;
11062                 st.height = r.height;
11063                 el.afterFx(o);
11064             };
11065             // time to calc the positions
11066             var a, pt = {to: [b.x, b.y]}, bw = {to: b.width}, bh = {to: b.height};
11067
11068             switch(anchor.toLowerCase()){
11069                 case "t":
11070                     wrap.setSize(b.width, 0);
11071                     st.left = st.bottom = "0";
11072                     a = {height: bh};
11073                 break;
11074                 case "l":
11075                     wrap.setSize(0, b.height);
11076                     st.right = st.top = "0";
11077                     a = {width: bw};
11078                 break;
11079                 case "r":
11080                     wrap.setSize(0, b.height);
11081                     wrap.setX(b.right);
11082                     st.left = st.top = "0";
11083                     a = {width: bw, points: pt};
11084                 break;
11085                 case "b":
11086                     wrap.setSize(b.width, 0);
11087                     wrap.setY(b.bottom);
11088                     st.left = st.top = "0";
11089                     a = {height: bh, points: pt};
11090                 break;
11091                 case "tl":
11092                     wrap.setSize(0, 0);
11093                     st.right = st.bottom = "0";
11094                     a = {width: bw, height: bh};
11095                 break;
11096                 case "bl":
11097                     wrap.setSize(0, 0);
11098                     wrap.setY(b.y+b.height);
11099                     st.right = st.top = "0";
11100                     a = {width: bw, height: bh, points: pt};
11101                 break;
11102                 case "br":
11103                     wrap.setSize(0, 0);
11104                     wrap.setXY([b.right, b.bottom]);
11105                     st.left = st.top = "0";
11106                     a = {width: bw, height: bh, points: pt};
11107                 break;
11108                 case "tr":
11109                     wrap.setSize(0, 0);
11110                     wrap.setX(b.x+b.width);
11111                     st.left = st.bottom = "0";
11112                     a = {width: bw, height: bh, points: pt};
11113                 break;
11114             }
11115             this.dom.style.visibility = "visible";
11116             wrap.show();
11117
11118             arguments.callee.anim = wrap.fxanim(a,
11119                 o,
11120                 'motion',
11121                 .5,
11122                 'easeOut', after);
11123         });
11124         return this;
11125     },
11126     
11127         /**
11128          * Slides the element out of view.  An anchor point can be optionally passed to set the end point
11129          * for the slide effect.  When the effect is completed, the element will be hidden (visibility = 
11130          * 'hidden') but block elements will still take up space in the document.  The element must be removed
11131          * from the DOM using the 'remove' config option if desired.  This function automatically handles 
11132          * wrapping the element with a fixed-size container if needed.  See the Fx class overview for valid anchor point options.
11133          * Usage:
11134          *<pre><code>
11135 // default: slide the element out to the top
11136 el.slideOut();
11137
11138 // custom: slide the element out to the right with a 2-second duration
11139 el.slideOut('r', { duration: 2 });
11140
11141 // common config options shown with default values
11142 el.slideOut('t', {
11143     easing: 'easeOut',
11144     duration: .5,
11145     remove: false,
11146     useDisplay: false
11147 });
11148 </code></pre>
11149          * @param {String} anchor (optional) One of the valid Fx anchor positions (defaults to top: 't')
11150          * @param {Object} options (optional) Object literal with any of the Fx config options
11151          * @return {Roo.Element} The Element
11152          */
11153     slideOut : function(anchor, o){
11154         var el = this.getFxEl();
11155         o = o || {};
11156
11157         el.queueFx(o, function(){
11158
11159             anchor = anchor || "t";
11160
11161             // restore values after effect
11162             var r = this.getFxRestore();
11163             
11164             var b = this.getBox();
11165             // fixed size for slide
11166             this.setSize(b);
11167
11168             // wrap if needed
11169             var wrap = this.fxWrap(r.pos, o, "visible");
11170
11171             var st = this.dom.style;
11172             st.visibility = "visible";
11173             st.position = "absolute";
11174
11175             wrap.setSize(b);
11176
11177             var after = function(){
11178                 if(o.useDisplay){
11179                     el.setDisplayed(false);
11180                 }else{
11181                     el.hide();
11182                 }
11183
11184                 el.fxUnwrap(wrap, r.pos, o);
11185
11186                 st.width = r.width;
11187                 st.height = r.height;
11188
11189                 el.afterFx(o);
11190             };
11191
11192             var a, zero = {to: 0};
11193             switch(anchor.toLowerCase()){
11194                 case "t":
11195                     st.left = st.bottom = "0";
11196                     a = {height: zero};
11197                 break;
11198                 case "l":
11199                     st.right = st.top = "0";
11200                     a = {width: zero};
11201                 break;
11202                 case "r":
11203                     st.left = st.top = "0";
11204                     a = {width: zero, points: {to:[b.right, b.y]}};
11205                 break;
11206                 case "b":
11207                     st.left = st.top = "0";
11208                     a = {height: zero, points: {to:[b.x, b.bottom]}};
11209                 break;
11210                 case "tl":
11211                     st.right = st.bottom = "0";
11212                     a = {width: zero, height: zero};
11213                 break;
11214                 case "bl":
11215                     st.right = st.top = "0";
11216                     a = {width: zero, height: zero, points: {to:[b.x, b.bottom]}};
11217                 break;
11218                 case "br":
11219                     st.left = st.top = "0";
11220                     a = {width: zero, height: zero, points: {to:[b.x+b.width, b.bottom]}};
11221                 break;
11222                 case "tr":
11223                     st.left = st.bottom = "0";
11224                     a = {width: zero, height: zero, points: {to:[b.right, b.y]}};
11225                 break;
11226             }
11227
11228             arguments.callee.anim = wrap.fxanim(a,
11229                 o,
11230                 'motion',
11231                 .5,
11232                 "easeOut", after);
11233         });
11234         return this;
11235     },
11236
11237         /**
11238          * Fades the element out while slowly expanding it in all directions.  When the effect is completed, the 
11239          * element will be hidden (visibility = 'hidden') but block elements will still take up space in the document. 
11240          * The element must be removed from the DOM using the 'remove' config option if desired.
11241          * Usage:
11242          *<pre><code>
11243 // default
11244 el.puff();
11245
11246 // common config options shown with default values
11247 el.puff({
11248     easing: 'easeOut',
11249     duration: .5,
11250     remove: false,
11251     useDisplay: false
11252 });
11253 </code></pre>
11254          * @param {Object} options (optional) Object literal with any of the Fx config options
11255          * @return {Roo.Element} The Element
11256          */
11257     puff : function(o){
11258         var el = this.getFxEl();
11259         o = o || {};
11260
11261         el.queueFx(o, function(){
11262             this.clearOpacity();
11263             this.show();
11264
11265             // restore values after effect
11266             var r = this.getFxRestore();
11267             var st = this.dom.style;
11268
11269             var after = function(){
11270                 if(o.useDisplay){
11271                     el.setDisplayed(false);
11272                 }else{
11273                     el.hide();
11274                 }
11275
11276                 el.clearOpacity();
11277
11278                 el.setPositioning(r.pos);
11279                 st.width = r.width;
11280                 st.height = r.height;
11281                 st.fontSize = '';
11282                 el.afterFx(o);
11283             };
11284
11285             var width = this.getWidth();
11286             var height = this.getHeight();
11287
11288             arguments.callee.anim = this.fxanim({
11289                     width : {to: this.adjustWidth(width * 2)},
11290                     height : {to: this.adjustHeight(height * 2)},
11291                     points : {by: [-(width * .5), -(height * .5)]},
11292                     opacity : {to: 0},
11293                     fontSize: {to:200, unit: "%"}
11294                 },
11295                 o,
11296                 'motion',
11297                 .5,
11298                 "easeOut", after);
11299         });
11300         return this;
11301     },
11302
11303         /**
11304          * Blinks the element as if it was clicked and then collapses on its center (similar to switching off a television).
11305          * When the effect is completed, the element will be hidden (visibility = 'hidden') but block elements will still 
11306          * take up space in the document. The element must be removed from the DOM using the 'remove' config option if desired.
11307          * Usage:
11308          *<pre><code>
11309 // default
11310 el.switchOff();
11311
11312 // all config options shown with default values
11313 el.switchOff({
11314     easing: 'easeIn',
11315     duration: .3,
11316     remove: false,
11317     useDisplay: false
11318 });
11319 </code></pre>
11320          * @param {Object} options (optional) Object literal with any of the Fx config options
11321          * @return {Roo.Element} The Element
11322          */
11323     switchOff : function(o){
11324         var el = this.getFxEl();
11325         o = o || {};
11326
11327         el.queueFx(o, function(){
11328             this.clearOpacity();
11329             this.clip();
11330
11331             // restore values after effect
11332             var r = this.getFxRestore();
11333             var st = this.dom.style;
11334
11335             var after = function(){
11336                 if(o.useDisplay){
11337                     el.setDisplayed(false);
11338                 }else{
11339                     el.hide();
11340                 }
11341
11342                 el.clearOpacity();
11343                 el.setPositioning(r.pos);
11344                 st.width = r.width;
11345                 st.height = r.height;
11346
11347                 el.afterFx(o);
11348             };
11349
11350             this.fxanim({opacity:{to:0.3}}, null, null, .1, null, function(){
11351                 this.clearOpacity();
11352                 (function(){
11353                     this.fxanim({
11354                         height:{to:1},
11355                         points:{by:[0, this.getHeight() * .5]}
11356                     }, o, 'motion', 0.3, 'easeIn', after);
11357                 }).defer(100, this);
11358             });
11359         });
11360         return this;
11361     },
11362
11363     /**
11364      * Highlights the Element by setting a color (applies to the background-color by default, but can be
11365      * changed using the "attr" config option) and then fading back to the original color. If no original
11366      * color is available, you should provide the "endColor" config option which will be cleared after the animation.
11367      * Usage:
11368 <pre><code>
11369 // default: highlight background to yellow
11370 el.highlight();
11371
11372 // custom: highlight foreground text to blue for 2 seconds
11373 el.highlight("0000ff", { attr: 'color', duration: 2 });
11374
11375 // common config options shown with default values
11376 el.highlight("ffff9c", {
11377     attr: "background-color", //can be any valid CSS property (attribute) that supports a color value
11378     endColor: (current color) or "ffffff",
11379     easing: 'easeIn',
11380     duration: 1
11381 });
11382 </code></pre>
11383      * @param {String} color (optional) The highlight color. Should be a 6 char hex color without the leading # (defaults to yellow: 'ffff9c')
11384      * @param {Object} options (optional) Object literal with any of the Fx config options
11385      * @return {Roo.Element} The Element
11386      */ 
11387     highlight : function(color, o){
11388         var el = this.getFxEl();
11389         o = o || {};
11390
11391         el.queueFx(o, function(){
11392             color = color || "ffff9c";
11393             attr = o.attr || "backgroundColor";
11394
11395             this.clearOpacity();
11396             this.show();
11397
11398             var origColor = this.getColor(attr);
11399             var restoreColor = this.dom.style[attr];
11400             endColor = (o.endColor || origColor) || "ffffff";
11401
11402             var after = function(){
11403                 el.dom.style[attr] = restoreColor;
11404                 el.afterFx(o);
11405             };
11406
11407             var a = {};
11408             a[attr] = {from: color, to: endColor};
11409             arguments.callee.anim = this.fxanim(a,
11410                 o,
11411                 'color',
11412                 1,
11413                 'easeIn', after);
11414         });
11415         return this;
11416     },
11417
11418    /**
11419     * Shows a ripple of exploding, attenuating borders to draw attention to an Element.
11420     * Usage:
11421 <pre><code>
11422 // default: a single light blue ripple
11423 el.frame();
11424
11425 // custom: 3 red ripples lasting 3 seconds total
11426 el.frame("ff0000", 3, { duration: 3 });
11427
11428 // common config options shown with default values
11429 el.frame("C3DAF9", 1, {
11430     duration: 1 //duration of entire animation (not each individual ripple)
11431     // Note: Easing is not configurable and will be ignored if included
11432 });
11433 </code></pre>
11434     * @param {String} color (optional) The color of the border.  Should be a 6 char hex color without the leading # (defaults to light blue: 'C3DAF9').
11435     * @param {Number} count (optional) The number of ripples to display (defaults to 1)
11436     * @param {Object} options (optional) Object literal with any of the Fx config options
11437     * @return {Roo.Element} The Element
11438     */
11439     frame : function(color, count, o){
11440         var el = this.getFxEl();
11441         o = o || {};
11442
11443         el.queueFx(o, function(){
11444             color = color || "#C3DAF9";
11445             if(color.length == 6){
11446                 color = "#" + color;
11447             }
11448             count = count || 1;
11449             duration = o.duration || 1;
11450             this.show();
11451
11452             var b = this.getBox();
11453             var animFn = function(){
11454                 var proxy = this.createProxy({
11455
11456                      style:{
11457                         visbility:"hidden",
11458                         position:"absolute",
11459                         "z-index":"35000", // yee haw
11460                         border:"0px solid " + color
11461                      }
11462                   });
11463                 var scale = Roo.isBorderBox ? 2 : 1;
11464                 proxy.animate({
11465                     top:{from:b.y, to:b.y - 20},
11466                     left:{from:b.x, to:b.x - 20},
11467                     borderWidth:{from:0, to:10},
11468                     opacity:{from:1, to:0},
11469                     height:{from:b.height, to:(b.height + (20*scale))},
11470                     width:{from:b.width, to:(b.width + (20*scale))}
11471                 }, duration, function(){
11472                     proxy.remove();
11473                 });
11474                 if(--count > 0){
11475                      animFn.defer((duration/2)*1000, this);
11476                 }else{
11477                     el.afterFx(o);
11478                 }
11479             };
11480             animFn.call(this);
11481         });
11482         return this;
11483     },
11484
11485    /**
11486     * Creates a pause before any subsequent queued effects begin.  If there are
11487     * no effects queued after the pause it will have no effect.
11488     * Usage:
11489 <pre><code>
11490 el.pause(1);
11491 </code></pre>
11492     * @param {Number} seconds The length of time to pause (in seconds)
11493     * @return {Roo.Element} The Element
11494     */
11495     pause : function(seconds){
11496         var el = this.getFxEl();
11497         var o = {};
11498
11499         el.queueFx(o, function(){
11500             setTimeout(function(){
11501                 el.afterFx(o);
11502             }, seconds * 1000);
11503         });
11504         return this;
11505     },
11506
11507    /**
11508     * Fade an element in (from transparent to opaque).  The ending opacity can be specified
11509     * using the "endOpacity" config option.
11510     * Usage:
11511 <pre><code>
11512 // default: fade in from opacity 0 to 100%
11513 el.fadeIn();
11514
11515 // custom: fade in from opacity 0 to 75% over 2 seconds
11516 el.fadeIn({ endOpacity: .75, duration: 2});
11517
11518 // common config options shown with default values
11519 el.fadeIn({
11520     endOpacity: 1, //can be any value between 0 and 1 (e.g. .5)
11521     easing: 'easeOut',
11522     duration: .5
11523 });
11524 </code></pre>
11525     * @param {Object} options (optional) Object literal with any of the Fx config options
11526     * @return {Roo.Element} The Element
11527     */
11528     fadeIn : function(o){
11529         var el = this.getFxEl();
11530         o = o || {};
11531         el.queueFx(o, function(){
11532             this.setOpacity(0);
11533             this.fixDisplay();
11534             this.dom.style.visibility = 'visible';
11535             var to = o.endOpacity || 1;
11536             arguments.callee.anim = this.fxanim({opacity:{to:to}},
11537                 o, null, .5, "easeOut", function(){
11538                 if(to == 1){
11539                     this.clearOpacity();
11540                 }
11541                 el.afterFx(o);
11542             });
11543         });
11544         return this;
11545     },
11546
11547    /**
11548     * Fade an element out (from opaque to transparent).  The ending opacity can be specified
11549     * using the "endOpacity" config option.
11550     * Usage:
11551 <pre><code>
11552 // default: fade out from the element's current opacity to 0
11553 el.fadeOut();
11554
11555 // custom: fade out from the element's current opacity to 25% over 2 seconds
11556 el.fadeOut({ endOpacity: .25, duration: 2});
11557
11558 // common config options shown with default values
11559 el.fadeOut({
11560     endOpacity: 0, //can be any value between 0 and 1 (e.g. .5)
11561     easing: 'easeOut',
11562     duration: .5
11563     remove: false,
11564     useDisplay: false
11565 });
11566 </code></pre>
11567     * @param {Object} options (optional) Object literal with any of the Fx config options
11568     * @return {Roo.Element} The Element
11569     */
11570     fadeOut : function(o){
11571         var el = this.getFxEl();
11572         o = o || {};
11573         el.queueFx(o, function(){
11574             arguments.callee.anim = this.fxanim({opacity:{to:o.endOpacity || 0}},
11575                 o, null, .5, "easeOut", function(){
11576                 if(this.visibilityMode == Roo.Element.DISPLAY || o.useDisplay){
11577                      this.dom.style.display = "none";
11578                 }else{
11579                      this.dom.style.visibility = "hidden";
11580                 }
11581                 this.clearOpacity();
11582                 el.afterFx(o);
11583             });
11584         });
11585         return this;
11586     },
11587
11588    /**
11589     * Animates the transition of an element's dimensions from a starting height/width
11590     * to an ending height/width.
11591     * Usage:
11592 <pre><code>
11593 // change height and width to 100x100 pixels
11594 el.scale(100, 100);
11595
11596 // common config options shown with default values.  The height and width will default to
11597 // the element's existing values if passed as null.
11598 el.scale(
11599     [element's width],
11600     [element's height], {
11601     easing: 'easeOut',
11602     duration: .35
11603 });
11604 </code></pre>
11605     * @param {Number} width  The new width (pass undefined to keep the original width)
11606     * @param {Number} height  The new height (pass undefined to keep the original height)
11607     * @param {Object} options (optional) Object literal with any of the Fx config options
11608     * @return {Roo.Element} The Element
11609     */
11610     scale : function(w, h, o){
11611         this.shift(Roo.apply({}, o, {
11612             width: w,
11613             height: h
11614         }));
11615         return this;
11616     },
11617
11618    /**
11619     * Animates the transition of any combination of an element's dimensions, xy position and/or opacity.
11620     * Any of these properties not specified in the config object will not be changed.  This effect 
11621     * requires that at least one new dimension, position or opacity setting must be passed in on
11622     * the config object in order for the function to have any effect.
11623     * Usage:
11624 <pre><code>
11625 // slide the element horizontally to x position 200 while changing the height and opacity
11626 el.shift({ x: 200, height: 50, opacity: .8 });
11627
11628 // common config options shown with default values.
11629 el.shift({
11630     width: [element's width],
11631     height: [element's height],
11632     x: [element's x position],
11633     y: [element's y position],
11634     opacity: [element's opacity],
11635     easing: 'easeOut',
11636     duration: .35
11637 });
11638 </code></pre>
11639     * @param {Object} options  Object literal with any of the Fx config options
11640     * @return {Roo.Element} The Element
11641     */
11642     shift : function(o){
11643         var el = this.getFxEl();
11644         o = o || {};
11645         el.queueFx(o, function(){
11646             var a = {}, w = o.width, h = o.height, x = o.x, y = o.y,  op = o.opacity;
11647             if(w !== undefined){
11648                 a.width = {to: this.adjustWidth(w)};
11649             }
11650             if(h !== undefined){
11651                 a.height = {to: this.adjustHeight(h)};
11652             }
11653             if(x !== undefined || y !== undefined){
11654                 a.points = {to: [
11655                     x !== undefined ? x : this.getX(),
11656                     y !== undefined ? y : this.getY()
11657                 ]};
11658             }
11659             if(op !== undefined){
11660                 a.opacity = {to: op};
11661             }
11662             if(o.xy !== undefined){
11663                 a.points = {to: o.xy};
11664             }
11665             arguments.callee.anim = this.fxanim(a,
11666                 o, 'motion', .35, "easeOut", function(){
11667                 el.afterFx(o);
11668             });
11669         });
11670         return this;
11671     },
11672
11673         /**
11674          * Slides the element while fading it out of view.  An anchor point can be optionally passed to set the 
11675          * ending point of the effect.
11676          * Usage:
11677          *<pre><code>
11678 // default: slide the element downward while fading out
11679 el.ghost();
11680
11681 // custom: slide the element out to the right with a 2-second duration
11682 el.ghost('r', { duration: 2 });
11683
11684 // common config options shown with default values
11685 el.ghost('b', {
11686     easing: 'easeOut',
11687     duration: .5
11688     remove: false,
11689     useDisplay: false
11690 });
11691 </code></pre>
11692          * @param {String} anchor (optional) One of the valid Fx anchor positions (defaults to bottom: 'b')
11693          * @param {Object} options (optional) Object literal with any of the Fx config options
11694          * @return {Roo.Element} The Element
11695          */
11696     ghost : function(anchor, o){
11697         var el = this.getFxEl();
11698         o = o || {};
11699
11700         el.queueFx(o, function(){
11701             anchor = anchor || "b";
11702
11703             // restore values after effect
11704             var r = this.getFxRestore();
11705             var w = this.getWidth(),
11706                 h = this.getHeight();
11707
11708             var st = this.dom.style;
11709
11710             var after = function(){
11711                 if(o.useDisplay){
11712                     el.setDisplayed(false);
11713                 }else{
11714                     el.hide();
11715                 }
11716
11717                 el.clearOpacity();
11718                 el.setPositioning(r.pos);
11719                 st.width = r.width;
11720                 st.height = r.height;
11721
11722                 el.afterFx(o);
11723             };
11724
11725             var a = {opacity: {to: 0}, points: {}}, pt = a.points;
11726             switch(anchor.toLowerCase()){
11727                 case "t":
11728                     pt.by = [0, -h];
11729                 break;
11730                 case "l":
11731                     pt.by = [-w, 0];
11732                 break;
11733                 case "r":
11734                     pt.by = [w, 0];
11735                 break;
11736                 case "b":
11737                     pt.by = [0, h];
11738                 break;
11739                 case "tl":
11740                     pt.by = [-w, -h];
11741                 break;
11742                 case "bl":
11743                     pt.by = [-w, h];
11744                 break;
11745                 case "br":
11746                     pt.by = [w, h];
11747                 break;
11748                 case "tr":
11749                     pt.by = [w, -h];
11750                 break;
11751             }
11752
11753             arguments.callee.anim = this.fxanim(a,
11754                 o,
11755                 'motion',
11756                 .5,
11757                 "easeOut", after);
11758         });
11759         return this;
11760     },
11761
11762         /**
11763          * Ensures that all effects queued after syncFx is called on the element are
11764          * run concurrently.  This is the opposite of {@link #sequenceFx}.
11765          * @return {Roo.Element} The Element
11766          */
11767     syncFx : function(){
11768         this.fxDefaults = Roo.apply(this.fxDefaults || {}, {
11769             block : false,
11770             concurrent : true,
11771             stopFx : false
11772         });
11773         return this;
11774     },
11775
11776         /**
11777          * Ensures that all effects queued after sequenceFx is called on the element are
11778          * run in sequence.  This is the opposite of {@link #syncFx}.
11779          * @return {Roo.Element} The Element
11780          */
11781     sequenceFx : function(){
11782         this.fxDefaults = Roo.apply(this.fxDefaults || {}, {
11783             block : false,
11784             concurrent : false,
11785             stopFx : false
11786         });
11787         return this;
11788     },
11789
11790         /* @private */
11791     nextFx : function(){
11792         var ef = this.fxQueue[0];
11793         if(ef){
11794             ef.call(this);
11795         }
11796     },
11797
11798         /**
11799          * Returns true if the element has any effects actively running or queued, else returns false.
11800          * @return {Boolean} True if element has active effects, else false
11801          */
11802     hasActiveFx : function(){
11803         return this.fxQueue && this.fxQueue[0];
11804     },
11805
11806         /**
11807          * Stops any running effects and clears the element's internal effects queue if it contains
11808          * any additional effects that haven't started yet.
11809          * @return {Roo.Element} The Element
11810          */
11811     stopFx : function(){
11812         if(this.hasActiveFx()){
11813             var cur = this.fxQueue[0];
11814             if(cur && cur.anim && cur.anim.isAnimated()){
11815                 this.fxQueue = [cur]; // clear out others
11816                 cur.anim.stop(true);
11817             }
11818         }
11819         return this;
11820     },
11821
11822         /* @private */
11823     beforeFx : function(o){
11824         if(this.hasActiveFx() && !o.concurrent){
11825            if(o.stopFx){
11826                this.stopFx();
11827                return true;
11828            }
11829            return false;
11830         }
11831         return true;
11832     },
11833
11834         /**
11835          * Returns true if the element is currently blocking so that no other effect can be queued
11836          * until this effect is finished, else returns false if blocking is not set.  This is commonly
11837          * used to ensure that an effect initiated by a user action runs to completion prior to the
11838          * same effect being restarted (e.g., firing only one effect even if the user clicks several times).
11839          * @return {Boolean} True if blocking, else false
11840          */
11841     hasFxBlock : function(){
11842         var q = this.fxQueue;
11843         return q && q[0] && q[0].block;
11844     },
11845
11846         /* @private */
11847     queueFx : function(o, fn){
11848         if(!this.fxQueue){
11849             this.fxQueue = [];
11850         }
11851         if(!this.hasFxBlock()){
11852             Roo.applyIf(o, this.fxDefaults);
11853             if(!o.concurrent){
11854                 var run = this.beforeFx(o);
11855                 fn.block = o.block;
11856                 this.fxQueue.push(fn);
11857                 if(run){
11858                     this.nextFx();
11859                 }
11860             }else{
11861                 fn.call(this);
11862             }
11863         }
11864         return this;
11865     },
11866
11867         /* @private */
11868     fxWrap : function(pos, o, vis){
11869         var wrap;
11870         if(!o.wrap || !(wrap = Roo.get(o.wrap))){
11871             var wrapXY;
11872             if(o.fixPosition){
11873                 wrapXY = this.getXY();
11874             }
11875             var div = document.createElement("div");
11876             div.style.visibility = vis;
11877             wrap = Roo.get(this.dom.parentNode.insertBefore(div, this.dom));
11878             wrap.setPositioning(pos);
11879             if(wrap.getStyle("position") == "static"){
11880                 wrap.position("relative");
11881             }
11882             this.clearPositioning('auto');
11883             wrap.clip();
11884             wrap.dom.appendChild(this.dom);
11885             if(wrapXY){
11886                 wrap.setXY(wrapXY);
11887             }
11888         }
11889         return wrap;
11890     },
11891
11892         /* @private */
11893     fxUnwrap : function(wrap, pos, o){
11894         this.clearPositioning();
11895         this.setPositioning(pos);
11896         if(!o.wrap){
11897             wrap.dom.parentNode.insertBefore(this.dom, wrap.dom);
11898             wrap.remove();
11899         }
11900     },
11901
11902         /* @private */
11903     getFxRestore : function(){
11904         var st = this.dom.style;
11905         return {pos: this.getPositioning(), width: st.width, height : st.height};
11906     },
11907
11908         /* @private */
11909     afterFx : function(o){
11910         if(o.afterStyle){
11911             this.applyStyles(o.afterStyle);
11912         }
11913         if(o.afterCls){
11914             this.addClass(o.afterCls);
11915         }
11916         if(o.remove === true){
11917             this.remove();
11918         }
11919         Roo.callback(o.callback, o.scope, [this]);
11920         if(!o.concurrent){
11921             this.fxQueue.shift();
11922             this.nextFx();
11923         }
11924     },
11925
11926         /* @private */
11927     getFxEl : function(){ // support for composite element fx
11928         return Roo.get(this.dom);
11929     },
11930
11931         /* @private */
11932     fxanim : function(args, opt, animType, defaultDur, defaultEase, cb){
11933         animType = animType || 'run';
11934         opt = opt || {};
11935         var anim = Roo.lib.Anim[animType](
11936             this.dom, args,
11937             (opt.duration || defaultDur) || .35,
11938             (opt.easing || defaultEase) || 'easeOut',
11939             function(){
11940                 Roo.callback(cb, this);
11941             },
11942             this
11943         );
11944         opt.anim = anim;
11945         return anim;
11946     }
11947 };
11948
11949 // backwords compat
11950 Roo.Fx.resize = Roo.Fx.scale;
11951
11952 //When included, Roo.Fx is automatically applied to Element so that all basic
11953 //effects are available directly via the Element API
11954 Roo.apply(Roo.Element.prototype, Roo.Fx);/*
11955  * Based on:
11956  * Ext JS Library 1.1.1
11957  * Copyright(c) 2006-2007, Ext JS, LLC.
11958  *
11959  * Originally Released Under LGPL - original licence link has changed is not relivant.
11960  *
11961  * Fork - LGPL
11962  * <script type="text/javascript">
11963  */
11964
11965
11966 /**
11967  * @class Roo.CompositeElement
11968  * Standard composite class. Creates a Roo.Element for every element in the collection.
11969  * <br><br>
11970  * <b>NOTE: Although they are not listed, this class supports all of the set/update methods of Roo.Element. All Roo.Element
11971  * actions will be performed on all the elements in this collection.</b>
11972  * <br><br>
11973  * All methods return <i>this</i> and can be chained.
11974  <pre><code>
11975  var els = Roo.select("#some-el div.some-class", true);
11976  // or select directly from an existing element
11977  var el = Roo.get('some-el');
11978  el.select('div.some-class', true);
11979
11980  els.setWidth(100); // all elements become 100 width
11981  els.hide(true); // all elements fade out and hide
11982  // or
11983  els.setWidth(100).hide(true);
11984  </code></pre>
11985  */
11986 Roo.CompositeElement = function(els){
11987     this.elements = [];
11988     this.addElements(els);
11989 };
11990 Roo.CompositeElement.prototype = {
11991     isComposite: true,
11992     addElements : function(els){
11993         if(!els) {
11994             return this;
11995         }
11996         if(typeof els == "string"){
11997             els = Roo.Element.selectorFunction(els);
11998         }
11999         var yels = this.elements;
12000         var index = yels.length-1;
12001         for(var i = 0, len = els.length; i < len; i++) {
12002                 yels[++index] = Roo.get(els[i]);
12003         }
12004         return this;
12005     },
12006
12007     /**
12008     * Clears this composite and adds the elements returned by the passed selector.
12009     * @param {String/Array} els A string CSS selector, an array of elements or an element
12010     * @return {CompositeElement} this
12011     */
12012     fill : function(els){
12013         this.elements = [];
12014         this.add(els);
12015         return this;
12016     },
12017
12018     /**
12019     * Filters this composite to only elements that match the passed selector.
12020     * @param {String} selector A string CSS selector
12021     * @param {Boolean} inverse return inverse filter (not matches)
12022     * @return {CompositeElement} this
12023     */
12024     filter : function(selector, inverse){
12025         var els = [];
12026         inverse = inverse || false;
12027         this.each(function(el){
12028             var match = inverse ? !el.is(selector) : el.is(selector);
12029             if(match){
12030                 els[els.length] = el.dom;
12031             }
12032         });
12033         this.fill(els);
12034         return this;
12035     },
12036
12037     invoke : function(fn, args){
12038         var els = this.elements;
12039         for(var i = 0, len = els.length; i < len; i++) {
12040                 Roo.Element.prototype[fn].apply(els[i], args);
12041         }
12042         return this;
12043     },
12044     /**
12045     * Adds elements to this composite.
12046     * @param {String/Array} els A string CSS selector, an array of elements or an element
12047     * @return {CompositeElement} this
12048     */
12049     add : function(els){
12050         if(typeof els == "string"){
12051             this.addElements(Roo.Element.selectorFunction(els));
12052         }else if(els.length !== undefined){
12053             this.addElements(els);
12054         }else{
12055             this.addElements([els]);
12056         }
12057         return this;
12058     },
12059     /**
12060     * Calls the passed function passing (el, this, index) for each element in this composite.
12061     * @param {Function} fn The function to call
12062     * @param {Object} scope (optional) The <i>this</i> object (defaults to the element)
12063     * @return {CompositeElement} this
12064     */
12065     each : function(fn, scope){
12066         var els = this.elements;
12067         for(var i = 0, len = els.length; i < len; i++){
12068             if(fn.call(scope || els[i], els[i], this, i) === false) {
12069                 break;
12070             }
12071         }
12072         return this;
12073     },
12074
12075     /**
12076      * Returns the Element object at the specified index
12077      * @param {Number} index
12078      * @return {Roo.Element}
12079      */
12080     item : function(index){
12081         return this.elements[index] || null;
12082     },
12083
12084     /**
12085      * Returns the first Element
12086      * @return {Roo.Element}
12087      */
12088     first : function(){
12089         return this.item(0);
12090     },
12091
12092     /**
12093      * Returns the last Element
12094      * @return {Roo.Element}
12095      */
12096     last : function(){
12097         return this.item(this.elements.length-1);
12098     },
12099
12100     /**
12101      * Returns the number of elements in this composite
12102      * @return Number
12103      */
12104     getCount : function(){
12105         return this.elements.length;
12106     },
12107
12108     /**
12109      * Returns true if this composite contains the passed element
12110      * @return Boolean
12111      */
12112     contains : function(el){
12113         return this.indexOf(el) !== -1;
12114     },
12115
12116     /**
12117      * Returns true if this composite contains the passed element
12118      * @return Boolean
12119      */
12120     indexOf : function(el){
12121         return this.elements.indexOf(Roo.get(el));
12122     },
12123
12124
12125     /**
12126     * Removes the specified element(s).
12127     * @param {Mixed} el The id of an element, the Element itself, the index of the element in this composite
12128     * or an array of any of those.
12129     * @param {Boolean} removeDom (optional) True to also remove the element from the document
12130     * @return {CompositeElement} this
12131     */
12132     removeElement : function(el, removeDom){
12133         if(el instanceof Array){
12134             for(var i = 0, len = el.length; i < len; i++){
12135                 this.removeElement(el[i]);
12136             }
12137             return this;
12138         }
12139         var index = typeof el == 'number' ? el : this.indexOf(el);
12140         if(index !== -1){
12141             if(removeDom){
12142                 var d = this.elements[index];
12143                 if(d.dom){
12144                     d.remove();
12145                 }else{
12146                     d.parentNode.removeChild(d);
12147                 }
12148             }
12149             this.elements.splice(index, 1);
12150         }
12151         return this;
12152     },
12153
12154     /**
12155     * Replaces the specified element with the passed element.
12156     * @param {String/HTMLElement/Element/Number} el The id of an element, the Element itself, the index of the element in this composite
12157     * to replace.
12158     * @param {String/HTMLElement/Element} replacement The id of an element or the Element itself.
12159     * @param {Boolean} domReplace (Optional) True to remove and replace the element in the document too.
12160     * @return {CompositeElement} this
12161     */
12162     replaceElement : function(el, replacement, domReplace){
12163         var index = typeof el == 'number' ? el : this.indexOf(el);
12164         if(index !== -1){
12165             if(domReplace){
12166                 this.elements[index].replaceWith(replacement);
12167             }else{
12168                 this.elements.splice(index, 1, Roo.get(replacement))
12169             }
12170         }
12171         return this;
12172     },
12173
12174     /**
12175      * Removes all elements.
12176      */
12177     clear : function(){
12178         this.elements = [];
12179     }
12180 };
12181 (function(){
12182     Roo.CompositeElement.createCall = function(proto, fnName){
12183         if(!proto[fnName]){
12184             proto[fnName] = function(){
12185                 return this.invoke(fnName, arguments);
12186             };
12187         }
12188     };
12189     for(var fnName in Roo.Element.prototype){
12190         if(typeof Roo.Element.prototype[fnName] == "function"){
12191             Roo.CompositeElement.createCall(Roo.CompositeElement.prototype, fnName);
12192         }
12193     };
12194 })();
12195 /*
12196  * Based on:
12197  * Ext JS Library 1.1.1
12198  * Copyright(c) 2006-2007, Ext JS, LLC.
12199  *
12200  * Originally Released Under LGPL - original licence link has changed is not relivant.
12201  *
12202  * Fork - LGPL
12203  * <script type="text/javascript">
12204  */
12205
12206 /**
12207  * @class Roo.CompositeElementLite
12208  * @extends Roo.CompositeElement
12209  * Flyweight composite class. Reuses the same Roo.Element for element operations.
12210  <pre><code>
12211  var els = Roo.select("#some-el div.some-class");
12212  // or select directly from an existing element
12213  var el = Roo.get('some-el');
12214  el.select('div.some-class');
12215
12216  els.setWidth(100); // all elements become 100 width
12217  els.hide(true); // all elements fade out and hide
12218  // or
12219  els.setWidth(100).hide(true);
12220  </code></pre><br><br>
12221  * <b>NOTE: Although they are not listed, this class supports all of the set/update methods of Roo.Element. All Roo.Element
12222  * actions will be performed on all the elements in this collection.</b>
12223  */
12224 Roo.CompositeElementLite = function(els){
12225     Roo.CompositeElementLite.superclass.constructor.call(this, els);
12226     this.el = new Roo.Element.Flyweight();
12227 };
12228 Roo.extend(Roo.CompositeElementLite, Roo.CompositeElement, {
12229     addElements : function(els){
12230         if(els){
12231             if(els instanceof Array){
12232                 this.elements = this.elements.concat(els);
12233             }else{
12234                 var yels = this.elements;
12235                 var index = yels.length-1;
12236                 for(var i = 0, len = els.length; i < len; i++) {
12237                     yels[++index] = els[i];
12238                 }
12239             }
12240         }
12241         return this;
12242     },
12243     invoke : function(fn, args){
12244         var els = this.elements;
12245         var el = this.el;
12246         for(var i = 0, len = els.length; i < len; i++) {
12247             el.dom = els[i];
12248                 Roo.Element.prototype[fn].apply(el, args);
12249         }
12250         return this;
12251     },
12252     /**
12253      * Returns a flyweight Element of the dom element object at the specified index
12254      * @param {Number} index
12255      * @return {Roo.Element}
12256      */
12257     item : function(index){
12258         if(!this.elements[index]){
12259             return null;
12260         }
12261         this.el.dom = this.elements[index];
12262         return this.el;
12263     },
12264
12265     // fixes scope with flyweight
12266     addListener : function(eventName, handler, scope, opt){
12267         var els = this.elements;
12268         for(var i = 0, len = els.length; i < len; i++) {
12269             Roo.EventManager.on(els[i], eventName, handler, scope || els[i], opt);
12270         }
12271         return this;
12272     },
12273
12274     /**
12275     * Calls the passed function passing (el, this, index) for each element in this composite. <b>The element
12276     * passed is the flyweight (shared) Roo.Element instance, so if you require a
12277     * a reference to the dom node, use el.dom.</b>
12278     * @param {Function} fn The function to call
12279     * @param {Object} scope (optional) The <i>this</i> object (defaults to the element)
12280     * @return {CompositeElement} this
12281     */
12282     each : function(fn, scope){
12283         var els = this.elements;
12284         var el = this.el;
12285         for(var i = 0, len = els.length; i < len; i++){
12286             el.dom = els[i];
12287                 if(fn.call(scope || el, el, this, i) === false){
12288                 break;
12289             }
12290         }
12291         return this;
12292     },
12293
12294     indexOf : function(el){
12295         return this.elements.indexOf(Roo.getDom(el));
12296     },
12297
12298     replaceElement : function(el, replacement, domReplace){
12299         var index = typeof el == 'number' ? el : this.indexOf(el);
12300         if(index !== -1){
12301             replacement = Roo.getDom(replacement);
12302             if(domReplace){
12303                 var d = this.elements[index];
12304                 d.parentNode.insertBefore(replacement, d);
12305                 d.parentNode.removeChild(d);
12306             }
12307             this.elements.splice(index, 1, replacement);
12308         }
12309         return this;
12310     }
12311 });
12312 Roo.CompositeElementLite.prototype.on = Roo.CompositeElementLite.prototype.addListener;
12313
12314 /*
12315  * Based on:
12316  * Ext JS Library 1.1.1
12317  * Copyright(c) 2006-2007, Ext JS, LLC.
12318  *
12319  * Originally Released Under LGPL - original licence link has changed is not relivant.
12320  *
12321  * Fork - LGPL
12322  * <script type="text/javascript">
12323  */
12324
12325  
12326
12327 /**
12328  * @class Roo.data.Connection
12329  * @extends Roo.util.Observable
12330  * The class encapsulates a connection to the page's originating domain, allowing requests to be made
12331  * either to a configured URL, or to a URL specified at request time. 
12332  * 
12333  * Requests made by this class are asynchronous, and will return immediately. No data from
12334  * the server will be available to the statement immediately following the {@link #request} call.
12335  * To process returned data, use a callback in the request options object, or an event listener.
12336  * 
12337  * Note: If you are doing a file upload, you will not get a normal response object sent back to
12338  * your callback or event handler.  Since the upload is handled via in IFRAME, there is no XMLHttpRequest.
12339  * The response object is created using the innerHTML of the IFRAME's document as the responseText
12340  * property and, if present, the IFRAME's XML document as the responseXML property.
12341  * 
12342  * This means that a valid XML or HTML document must be returned. If JSON data is required, it is suggested
12343  * that it be placed either inside a &lt;textarea> in an HTML document and retrieved from the responseText
12344  * using a regex, or inside a CDATA section in an XML document and retrieved from the responseXML using
12345  * standard DOM methods.
12346  * @constructor
12347  * @param {Object} config a configuration object.
12348  */
12349 Roo.data.Connection = function(config){
12350     Roo.apply(this, config);
12351     this.addEvents({
12352         /**
12353          * @event beforerequest
12354          * Fires before a network request is made to retrieve a data object.
12355          * @param {Connection} conn This Connection object.
12356          * @param {Object} options The options config object passed to the {@link #request} method.
12357          */
12358         "beforerequest" : true,
12359         /**
12360          * @event requestcomplete
12361          * Fires if the request was successfully completed.
12362          * @param {Connection} conn This Connection object.
12363          * @param {Object} response The XHR object containing the response data.
12364          * See {@link http://www.w3.org/TR/XMLHttpRequest/} for details.
12365          * @param {Object} options The options config object passed to the {@link #request} method.
12366          */
12367         "requestcomplete" : true,
12368         /**
12369          * @event requestexception
12370          * Fires if an error HTTP status was returned from the server.
12371          * See {@link http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html} for details of HTTP status codes.
12372          * @param {Connection} conn This Connection object.
12373          * @param {Object} response The XHR object containing the response data.
12374          * See {@link http://www.w3.org/TR/XMLHttpRequest/} for details.
12375          * @param {Object} options The options config object passed to the {@link #request} method.
12376          */
12377         "requestexception" : true
12378     });
12379     Roo.data.Connection.superclass.constructor.call(this);
12380 };
12381
12382 Roo.extend(Roo.data.Connection, Roo.util.Observable, {
12383     /**
12384      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
12385      */
12386     /**
12387      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
12388      * extra parameters to each request made by this object. (defaults to undefined)
12389      */
12390     /**
12391      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
12392      *  to each request made by this object. (defaults to undefined)
12393      */
12394     /**
12395      * @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)
12396      */
12397     /**
12398      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
12399      */
12400     timeout : 30000,
12401     /**
12402      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
12403      * @type Boolean
12404      */
12405     autoAbort:false,
12406
12407     /**
12408      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
12409      * @type Boolean
12410      */
12411     disableCaching: true,
12412
12413     /**
12414      * Sends an HTTP request to a remote server.
12415      * @param {Object} options An object which may contain the following properties:<ul>
12416      * <li><b>url</b> {String} (Optional) The URL to which to send the request. Defaults to configured URL</li>
12417      * <li><b>params</b> {Object/String/Function} (Optional) An object containing properties which are used as parameters to the
12418      * request, a url encoded string or a function to call to get either.</li>
12419      * <li><b>method</b> {String} (Optional) The HTTP method to use for the request. Defaults to the configured method, or
12420      * if no method was configured, "GET" if no parameters are being sent, and "POST" if parameters are being sent.</li>
12421      * <li><b>callback</b> {Function} (Optional) The function to be called upon receipt of the HTTP response.
12422      * The callback is called regardless of success or failure and is passed the following parameters:<ul>
12423      * <li>options {Object} The parameter to the request call.</li>
12424      * <li>success {Boolean} True if the request succeeded.</li>
12425      * <li>response {Object} The XMLHttpRequest object containing the response data.</li>
12426      * </ul></li>
12427      * <li><b>success</b> {Function} (Optional) The function to be called upon success of the request.
12428      * The callback is passed the following parameters:<ul>
12429      * <li>response {Object} The XMLHttpRequest object containing the response data.</li>
12430      * <li>options {Object} The parameter to the request call.</li>
12431      * </ul></li>
12432      * <li><b>failure</b> {Function} (Optional) The function to be called upon failure of the request.
12433      * The callback is passed the following parameters:<ul>
12434      * <li>response {Object} The XMLHttpRequest object containing the response data.</li>
12435      * <li>options {Object} The parameter to the request call.</li>
12436      * </ul></li>
12437      * <li><b>scope</b> {Object} (Optional) The scope in which to execute the callbacks: The "this" object
12438      * for the callback function. Defaults to the browser window.</li>
12439      * <li><b>form</b> {Object/String} (Optional) A form object or id to pull parameters from.</li>
12440      * <li><b>isUpload</b> {Boolean} (Optional) True if the form object is a file upload (will usually be automatically detected).</li>
12441      * <li><b>headers</b> {Object} (Optional) Request headers to set for the request.</li>
12442      * <li><b>xmlData</b> {Object} (Optional) XML document to use for the post. Note: This will be used instead of
12443      * params for the post data. Any params will be appended to the URL.</li>
12444      * <li><b>disableCaching</b> {Boolean} (Optional) True to add a unique cache-buster param to GET requests.</li>
12445      * </ul>
12446      * @return {Number} transactionId
12447      */
12448     request : function(o){
12449         if(this.fireEvent("beforerequest", this, o) !== false){
12450             var p = o.params;
12451
12452             if(typeof p == "function"){
12453                 p = p.call(o.scope||window, o);
12454             }
12455             if(typeof p == "object"){
12456                 p = Roo.urlEncode(o.params);
12457             }
12458             if(this.extraParams){
12459                 var extras = Roo.urlEncode(this.extraParams);
12460                 p = p ? (p + '&' + extras) : extras;
12461             }
12462
12463             var url = o.url || this.url;
12464             if(typeof url == 'function'){
12465                 url = url.call(o.scope||window, o);
12466             }
12467
12468             if(o.form){
12469                 var form = Roo.getDom(o.form);
12470                 url = url || form.action;
12471
12472                 var enctype = form.getAttribute("enctype");
12473                 
12474                 if (o.formData) {
12475                     return this.doFormDataUpload(o, url);
12476                 }
12477                 
12478                 if(o.isUpload || (enctype && enctype.toLowerCase() == 'multipart/form-data')){
12479                     return this.doFormUpload(o, p, url);
12480                 }
12481                 var f = Roo.lib.Ajax.serializeForm(form);
12482                 p = p ? (p + '&' + f) : f;
12483             }
12484             
12485             if (!o.form && o.formData) {
12486                 o.formData = o.formData === true ? new FormData() : o.formData;
12487                 for (var k in o.params) {
12488                     o.formData.append(k,o.params[k]);
12489                 }
12490                     
12491                 return this.doFormDataUpload(o, url);
12492             }
12493             
12494
12495             var hs = o.headers;
12496             if(this.defaultHeaders){
12497                 hs = Roo.apply(hs || {}, this.defaultHeaders);
12498                 if(!o.headers){
12499                     o.headers = hs;
12500                 }
12501             }
12502
12503             var cb = {
12504                 success: this.handleResponse,
12505                 failure: this.handleFailure,
12506                 scope: this,
12507                 argument: {options: o},
12508                 timeout : o.timeout || this.timeout
12509             };
12510
12511             var method = o.method||this.method||(p ? "POST" : "GET");
12512
12513             if(method == 'GET' && (this.disableCaching && o.disableCaching !== false) || o.disableCaching === true){
12514                 url += (url.indexOf('?') != -1 ? '&' : '?') + '_dc=' + (new Date().getTime());
12515             }
12516
12517             if(typeof o.autoAbort == 'boolean'){ // options gets top priority
12518                 if(o.autoAbort){
12519                     this.abort();
12520                 }
12521             }else if(this.autoAbort !== false){
12522                 this.abort();
12523             }
12524
12525             if((method == 'GET' && p) || o.xmlData){
12526                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
12527                 p = '';
12528             }
12529             Roo.lib.Ajax.useDefaultHeader = typeof(o.headers) == 'undefined' || typeof(o.headers['Content-Type']) == 'undefined';
12530             this.transId = Roo.lib.Ajax.request(method, url, cb, p, o);
12531             Roo.lib.Ajax.useDefaultHeader == true;
12532             return this.transId;
12533         }else{
12534             Roo.callback(o.callback, o.scope, [o, null, null]);
12535             return null;
12536         }
12537     },
12538
12539     /**
12540      * Determine whether this object has a request outstanding.
12541      * @param {Number} transactionId (Optional) defaults to the last transaction
12542      * @return {Boolean} True if there is an outstanding request.
12543      */
12544     isLoading : function(transId){
12545         if(transId){
12546             return Roo.lib.Ajax.isCallInProgress(transId);
12547         }else{
12548             return this.transId ? true : false;
12549         }
12550     },
12551
12552     /**
12553      * Aborts any outstanding request.
12554      * @param {Number} transactionId (Optional) defaults to the last transaction
12555      */
12556     abort : function(transId){
12557         if(transId || this.isLoading()){
12558             Roo.lib.Ajax.abort(transId || this.transId);
12559         }
12560     },
12561
12562     // private
12563     handleResponse : function(response){
12564         this.transId = false;
12565         var options = response.argument.options;
12566         response.argument = options ? options.argument : null;
12567         this.fireEvent("requestcomplete", this, response, options);
12568         Roo.callback(options.success, options.scope, [response, options]);
12569         Roo.callback(options.callback, options.scope, [options, true, response]);
12570     },
12571
12572     // private
12573     handleFailure : function(response, e){
12574         this.transId = false;
12575         var options = response.argument.options;
12576         response.argument = options ? options.argument : null;
12577         this.fireEvent("requestexception", this, response, options, e);
12578         Roo.callback(options.failure, options.scope, [response, options]);
12579         Roo.callback(options.callback, options.scope, [options, false, response]);
12580     },
12581
12582     // private
12583     doFormUpload : function(o, ps, url){
12584         var id = Roo.id();
12585         var frame = document.createElement('iframe');
12586         frame.id = id;
12587         frame.name = id;
12588         frame.className = 'x-hidden';
12589         if(Roo.isIE){
12590             frame.src = Roo.SSL_SECURE_URL;
12591         }
12592         document.body.appendChild(frame);
12593
12594         if(Roo.isIE){
12595            document.frames[id].name = id;
12596         }
12597
12598         var form = Roo.getDom(o.form);
12599         form.target = id;
12600         form.method = 'POST';
12601         form.enctype = form.encoding = 'multipart/form-data';
12602         if(url){
12603             form.action = url;
12604         }
12605
12606         var hiddens, hd;
12607         if(ps){ // add dynamic params
12608             hiddens = [];
12609             ps = Roo.urlDecode(ps, false);
12610             for(var k in ps){
12611                 if(ps.hasOwnProperty(k)){
12612                     hd = document.createElement('input');
12613                     hd.type = 'hidden';
12614                     hd.name = k;
12615                     hd.value = ps[k];
12616                     form.appendChild(hd);
12617                     hiddens.push(hd);
12618                 }
12619             }
12620         }
12621
12622         function cb(){
12623             var r = {  // bogus response object
12624                 responseText : '',
12625                 responseXML : null
12626             };
12627
12628             r.argument = o ? o.argument : null;
12629
12630             try { //
12631                 var doc;
12632                 if(Roo.isIE){
12633                     doc = frame.contentWindow.document;
12634                 }else {
12635                     doc = (frame.contentDocument || window.frames[id].document);
12636                 }
12637                 if(doc && doc.body){
12638                     r.responseText = doc.body.innerHTML;
12639                 }
12640                 if(doc && doc.XMLDocument){
12641                     r.responseXML = doc.XMLDocument;
12642                 }else {
12643                     r.responseXML = doc;
12644                 }
12645             }
12646             catch(e) {
12647                 // ignore
12648             }
12649
12650             Roo.EventManager.removeListener(frame, 'load', cb, this);
12651
12652             this.fireEvent("requestcomplete", this, r, o);
12653             Roo.callback(o.success, o.scope, [r, o]);
12654             Roo.callback(o.callback, o.scope, [o, true, r]);
12655
12656             setTimeout(function(){document.body.removeChild(frame);}, 100);
12657         }
12658
12659         Roo.EventManager.on(frame, 'load', cb, this);
12660         form.submit();
12661
12662         if(hiddens){ // remove dynamic params
12663             for(var i = 0, len = hiddens.length; i < len; i++){
12664                 form.removeChild(hiddens[i]);
12665             }
12666         }
12667     },
12668     // this is a 'formdata version???'
12669     
12670     
12671     doFormDataUpload : function(o,  url)
12672     {
12673         var formData;
12674         if (o.form) {
12675             var form =  Roo.getDom(o.form);
12676             form.enctype = form.encoding = 'multipart/form-data';
12677             formData = o.formData === true ? new FormData(form) : o.formData;
12678         } else {
12679             formData = o.formData === true ? new FormData() : o.formData;
12680         }
12681         
12682       
12683         var cb = {
12684             success: this.handleResponse,
12685             failure: this.handleFailure,
12686             scope: this,
12687             argument: {options: o},
12688             timeout : o.timeout || this.timeout
12689         };
12690  
12691         if(typeof o.autoAbort == 'boolean'){ // options gets top priority
12692             if(o.autoAbort){
12693                 this.abort();
12694             }
12695         }else if(this.autoAbort !== false){
12696             this.abort();
12697         }
12698
12699         //Roo.lib.Ajax.defaultPostHeader = null;
12700         Roo.lib.Ajax.useDefaultHeader = false;
12701         this.transId = Roo.lib.Ajax.request( "POST", url, cb,  formData, o);
12702         Roo.lib.Ajax.useDefaultHeader = true;
12703  
12704          
12705     }
12706     
12707 });
12708 /*
12709  * Based on:
12710  * Ext JS Library 1.1.1
12711  * Copyright(c) 2006-2007, Ext JS, LLC.
12712  *
12713  * Originally Released Under LGPL - original licence link has changed is not relivant.
12714  *
12715  * Fork - LGPL
12716  * <script type="text/javascript">
12717  */
12718  
12719 /**
12720  * Global Ajax request class.
12721  * 
12722  * @class Roo.Ajax
12723  * @extends Roo.data.Connection
12724  * @static
12725  * 
12726  * @cfg {String} url  The default URL to be used for requests to the server. (defaults to undefined)
12727  * @cfg {Object} extraParams  An object containing properties which are used as extra parameters to each request made by this object. (defaults to undefined)
12728  * @cfg {Object} defaultHeaders  An object containing request headers which are added to each request made by this object. (defaults to undefined)
12729  * @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)
12730  * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
12731  * @cfg {Boolean} autoAbort (Optional) Whether a new request should abort any pending requests. (defaults to false)
12732  * @cfg {Boolean} disableCaching (Optional)   True to add a unique cache-buster param to GET requests. (defaults to true)
12733  */
12734 Roo.Ajax = new Roo.data.Connection({
12735     // fix up the docs
12736     /**
12737      * @scope Roo.Ajax
12738      * @type {Boolear} 
12739      */
12740     autoAbort : false,
12741
12742     /**
12743      * Serialize the passed form into a url encoded string
12744      * @scope Roo.Ajax
12745      * @param {String/HTMLElement} form
12746      * @return {String}
12747      */
12748     serializeForm : function(form){
12749         return Roo.lib.Ajax.serializeForm(form);
12750     }
12751 });/*
12752  * Based on:
12753  * Ext JS Library 1.1.1
12754  * Copyright(c) 2006-2007, Ext JS, LLC.
12755  *
12756  * Originally Released Under LGPL - original licence link has changed is not relivant.
12757  *
12758  * Fork - LGPL
12759  * <script type="text/javascript">
12760  */
12761
12762  
12763 /**
12764  * @class Roo.UpdateManager
12765  * @extends Roo.util.Observable
12766  * Provides AJAX-style update for Element object.<br><br>
12767  * Usage:<br>
12768  * <pre><code>
12769  * // Get it from a Roo.Element object
12770  * var el = Roo.get("foo");
12771  * var mgr = el.getUpdateManager();
12772  * mgr.update("http://myserver.com/index.php", "param1=1&amp;param2=2");
12773  * ...
12774  * mgr.formUpdate("myFormId", "http://myserver.com/index.php");
12775  * <br>
12776  * // or directly (returns the same UpdateManager instance)
12777  * var mgr = new Roo.UpdateManager("myElementId");
12778  * mgr.startAutoRefresh(60, "http://myserver.com/index.php");
12779  * mgr.on("update", myFcnNeedsToKnow);
12780  * <br>
12781    // short handed call directly from the element object
12782    Roo.get("foo").load({
12783         url: "bar.php",
12784         scripts:true,
12785         params: "for=bar",
12786         text: "Loading Foo..."
12787    });
12788  * </code></pre>
12789  * @constructor
12790  * Create new UpdateManager directly.
12791  * @param {String/HTMLElement/Roo.Element} el The element to update
12792  * @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).
12793  */
12794 Roo.UpdateManager = function(el, forceNew){
12795     el = Roo.get(el);
12796     if(!forceNew && el.updateManager){
12797         return el.updateManager;
12798     }
12799     /**
12800      * The Element object
12801      * @type Roo.Element
12802      */
12803     this.el = el;
12804     /**
12805      * Cached url to use for refreshes. Overwritten every time update() is called unless "discardUrl" param is set to true.
12806      * @type String
12807      */
12808     this.defaultUrl = null;
12809
12810     this.addEvents({
12811         /**
12812          * @event beforeupdate
12813          * Fired before an update is made, return false from your handler and the update is cancelled.
12814          * @param {Roo.Element} el
12815          * @param {String/Object/Function} url
12816          * @param {String/Object} params
12817          */
12818         "beforeupdate": true,
12819         /**
12820          * @event update
12821          * Fired after successful update is made.
12822          * @param {Roo.Element} el
12823          * @param {Object} oResponseObject The response Object
12824          */
12825         "update": true,
12826         /**
12827          * @event failure
12828          * Fired on update failure.
12829          * @param {Roo.Element} el
12830          * @param {Object} oResponseObject The response Object
12831          */
12832         "failure": true
12833     });
12834     var d = Roo.UpdateManager.defaults;
12835     /**
12836      * Blank page URL to use with SSL file uploads (Defaults to Roo.UpdateManager.defaults.sslBlankUrl or "about:blank").
12837      * @type String
12838      */
12839     this.sslBlankUrl = d.sslBlankUrl;
12840     /**
12841      * Whether to append unique parameter on get request to disable caching (Defaults to Roo.UpdateManager.defaults.disableCaching or false).
12842      * @type Boolean
12843      */
12844     this.disableCaching = d.disableCaching;
12845     /**
12846      * Text for loading indicator (Defaults to Roo.UpdateManager.defaults.indicatorText or '&lt;div class="loading-indicator"&gt;Loading...&lt;/div&gt;').
12847      * @type String
12848      */
12849     this.indicatorText = d.indicatorText;
12850     /**
12851      * Whether to show indicatorText when loading (Defaults to Roo.UpdateManager.defaults.showLoadIndicator or true).
12852      * @type String
12853      */
12854     this.showLoadIndicator = d.showLoadIndicator;
12855     /**
12856      * Timeout for requests or form posts in seconds (Defaults to Roo.UpdateManager.defaults.timeout or 30 seconds).
12857      * @type Number
12858      */
12859     this.timeout = d.timeout;
12860
12861     /**
12862      * True to process scripts in the output (Defaults to Roo.UpdateManager.defaults.loadScripts (false)).
12863      * @type Boolean
12864      */
12865     this.loadScripts = d.loadScripts;
12866
12867     /**
12868      * Transaction object of current executing transaction
12869      */
12870     this.transaction = null;
12871
12872     /**
12873      * @private
12874      */
12875     this.autoRefreshProcId = null;
12876     /**
12877      * Delegate for refresh() prebound to "this", use myUpdater.refreshDelegate.createCallback(arg1, arg2) to bind arguments
12878      * @type Function
12879      */
12880     this.refreshDelegate = this.refresh.createDelegate(this);
12881     /**
12882      * Delegate for update() prebound to "this", use myUpdater.updateDelegate.createCallback(arg1, arg2) to bind arguments
12883      * @type Function
12884      */
12885     this.updateDelegate = this.update.createDelegate(this);
12886     /**
12887      * Delegate for formUpdate() prebound to "this", use myUpdater.formUpdateDelegate.createCallback(arg1, arg2) to bind arguments
12888      * @type Function
12889      */
12890     this.formUpdateDelegate = this.formUpdate.createDelegate(this);
12891     /**
12892      * @private
12893      */
12894     this.successDelegate = this.processSuccess.createDelegate(this);
12895     /**
12896      * @private
12897      */
12898     this.failureDelegate = this.processFailure.createDelegate(this);
12899
12900     if(!this.renderer){
12901      /**
12902       * The renderer for this UpdateManager. Defaults to {@link Roo.UpdateManager.BasicRenderer}.
12903       */
12904     this.renderer = new Roo.UpdateManager.BasicRenderer();
12905     }
12906     
12907     Roo.UpdateManager.superclass.constructor.call(this);
12908 };
12909
12910 Roo.extend(Roo.UpdateManager, Roo.util.Observable, {
12911     /**
12912      * Get the Element this UpdateManager is bound to
12913      * @return {Roo.Element} The element
12914      */
12915     getEl : function(){
12916         return this.el;
12917     },
12918     /**
12919      * Performs an async request, updating this element with the response. If params are specified it uses POST, otherwise it uses GET.
12920      * @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:
12921 <pre><code>
12922 um.update({<br/>
12923     url: "your-url.php",<br/>
12924     params: {param1: "foo", param2: "bar"}, // or a URL encoded string<br/>
12925     callback: yourFunction,<br/>
12926     scope: yourObject, //(optional scope)  <br/>
12927     discardUrl: false, <br/>
12928     nocache: false,<br/>
12929     text: "Loading...",<br/>
12930     timeout: 30,<br/>
12931     scripts: false<br/>
12932 });
12933 </code></pre>
12934      * The only required property is url. The optional properties nocache, text and scripts
12935      * are shorthand for disableCaching, indicatorText and loadScripts and are used to set their associated property on this UpdateManager instance.
12936      * @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}
12937      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess, oResponse)
12938      * @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.
12939      */
12940     update : function(url, params, callback, discardUrl){
12941         if(this.fireEvent("beforeupdate", this.el, url, params) !== false){
12942             var method = this.method,
12943                 cfg;
12944             if(typeof url == "object"){ // must be config object
12945                 cfg = url;
12946                 url = cfg.url;
12947                 params = params || cfg.params;
12948                 callback = callback || cfg.callback;
12949                 discardUrl = discardUrl || cfg.discardUrl;
12950                 if(callback && cfg.scope){
12951                     callback = callback.createDelegate(cfg.scope);
12952                 }
12953                 if(typeof cfg.method != "undefined"){method = cfg.method;};
12954                 if(typeof cfg.nocache != "undefined"){this.disableCaching = cfg.nocache;};
12955                 if(typeof cfg.text != "undefined"){this.indicatorText = '<div class="loading-indicator">'+cfg.text+"</div>";};
12956                 if(typeof cfg.scripts != "undefined"){this.loadScripts = cfg.scripts;};
12957                 if(typeof cfg.timeout != "undefined"){this.timeout = cfg.timeout;};
12958             }
12959             this.showLoading();
12960             if(!discardUrl){
12961                 this.defaultUrl = url;
12962             }
12963             if(typeof url == "function"){
12964                 url = url.call(this);
12965             }
12966
12967             method = method || (params ? "POST" : "GET");
12968             if(method == "GET"){
12969                 url = this.prepareUrl(url);
12970             }
12971
12972             var o = Roo.apply(cfg ||{}, {
12973                 url : url,
12974                 params: params,
12975                 success: this.successDelegate,
12976                 failure: this.failureDelegate,
12977                 callback: undefined,
12978                 timeout: (this.timeout*1000),
12979                 argument: {"url": url, "form": null, "callback": callback, "params": params}
12980             });
12981             Roo.log("updated manager called with timeout of " + o.timeout);
12982             this.transaction = Roo.Ajax.request(o);
12983         }
12984     },
12985
12986     /**
12987      * 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.
12988      * Uses this.sslBlankUrl for SSL file uploads to prevent IE security warning.
12989      * @param {String/HTMLElement} form The form Id or form element
12990      * @param {String} url (optional) The url to pass the form to. If omitted the action attribute on the form will be used.
12991      * @param {Boolean} reset (optional) Whether to try to reset the form after the update
12992      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess, oResponse)
12993      */
12994     formUpdate : function(form, url, reset, callback){
12995         if(this.fireEvent("beforeupdate", this.el, form, url) !== false){
12996             if(typeof url == "function"){
12997                 url = url.call(this);
12998             }
12999             form = Roo.getDom(form);
13000             this.transaction = Roo.Ajax.request({
13001                 form: form,
13002                 url:url,
13003                 success: this.successDelegate,
13004                 failure: this.failureDelegate,
13005                 timeout: (this.timeout*1000),
13006                 argument: {"url": url, "form": form, "callback": callback, "reset": reset}
13007             });
13008             this.showLoading.defer(1, this);
13009         }
13010     },
13011
13012     /**
13013      * Refresh the element with the last used url or defaultUrl. If there is no url, it returns immediately
13014      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess)
13015      */
13016     refresh : function(callback){
13017         if(this.defaultUrl == null){
13018             return;
13019         }
13020         this.update(this.defaultUrl, null, callback, true);
13021     },
13022
13023     /**
13024      * Set this element to auto refresh.
13025      * @param {Number} interval How often to update (in seconds).
13026      * @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)
13027      * @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}
13028      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess)
13029      * @param {Boolean} refreshNow (optional) Whether to execute the refresh now, or wait the interval
13030      */
13031     startAutoRefresh : function(interval, url, params, callback, refreshNow){
13032         if(refreshNow){
13033             this.update(url || this.defaultUrl, params, callback, true);
13034         }
13035         if(this.autoRefreshProcId){
13036             clearInterval(this.autoRefreshProcId);
13037         }
13038         this.autoRefreshProcId = setInterval(this.update.createDelegate(this, [url || this.defaultUrl, params, callback, true]), interval*1000);
13039     },
13040
13041     /**
13042      * Stop auto refresh on this element.
13043      */
13044      stopAutoRefresh : function(){
13045         if(this.autoRefreshProcId){
13046             clearInterval(this.autoRefreshProcId);
13047             delete this.autoRefreshProcId;
13048         }
13049     },
13050
13051     isAutoRefreshing : function(){
13052        return this.autoRefreshProcId ? true : false;
13053     },
13054     /**
13055      * Called to update the element to "Loading" state. Override to perform custom action.
13056      */
13057     showLoading : function(){
13058         if(this.showLoadIndicator){
13059             this.el.update(this.indicatorText);
13060         }
13061     },
13062
13063     /**
13064      * Adds unique parameter to query string if disableCaching = true
13065      * @private
13066      */
13067     prepareUrl : function(url){
13068         if(this.disableCaching){
13069             var append = "_dc=" + (new Date().getTime());
13070             if(url.indexOf("?") !== -1){
13071                 url += "&" + append;
13072             }else{
13073                 url += "?" + append;
13074             }
13075         }
13076         return url;
13077     },
13078
13079     /**
13080      * @private
13081      */
13082     processSuccess : function(response){
13083         this.transaction = null;
13084         if(response.argument.form && response.argument.reset){
13085             try{ // put in try/catch since some older FF releases had problems with this
13086                 response.argument.form.reset();
13087             }catch(e){}
13088         }
13089         if(this.loadScripts){
13090             this.renderer.render(this.el, response, this,
13091                 this.updateComplete.createDelegate(this, [response]));
13092         }else{
13093             this.renderer.render(this.el, response, this);
13094             this.updateComplete(response);
13095         }
13096     },
13097
13098     updateComplete : function(response){
13099         this.fireEvent("update", this.el, response);
13100         if(typeof response.argument.callback == "function"){
13101             response.argument.callback(this.el, true, response);
13102         }
13103     },
13104
13105     /**
13106      * @private
13107      */
13108     processFailure : function(response){
13109         this.transaction = null;
13110         this.fireEvent("failure", this.el, response);
13111         if(typeof response.argument.callback == "function"){
13112             response.argument.callback(this.el, false, response);
13113         }
13114     },
13115
13116     /**
13117      * Set the content renderer for this UpdateManager. See {@link Roo.UpdateManager.BasicRenderer#render} for more details.
13118      * @param {Object} renderer The object implementing the render() method
13119      */
13120     setRenderer : function(renderer){
13121         this.renderer = renderer;
13122     },
13123
13124     getRenderer : function(){
13125        return this.renderer;
13126     },
13127
13128     /**
13129      * Set the defaultUrl used for updates
13130      * @param {String/Function} defaultUrl The url or a function to call to get the url
13131      */
13132     setDefaultUrl : function(defaultUrl){
13133         this.defaultUrl = defaultUrl;
13134     },
13135
13136     /**
13137      * Aborts the executing transaction
13138      */
13139     abort : function(){
13140         if(this.transaction){
13141             Roo.Ajax.abort(this.transaction);
13142         }
13143     },
13144
13145     /**
13146      * Returns true if an update is in progress
13147      * @return {Boolean}
13148      */
13149     isUpdating : function(){
13150         if(this.transaction){
13151             return Roo.Ajax.isLoading(this.transaction);
13152         }
13153         return false;
13154     }
13155 });
13156
13157 /**
13158  * @class Roo.UpdateManager.defaults
13159  * @static (not really - but it helps the doc tool)
13160  * The defaults collection enables customizing the default properties of UpdateManager
13161  */
13162    Roo.UpdateManager.defaults = {
13163        /**
13164          * Timeout for requests or form posts in seconds (Defaults 30 seconds).
13165          * @type Number
13166          */
13167          timeout : 30,
13168
13169          /**
13170          * True to process scripts by default (Defaults to false).
13171          * @type Boolean
13172          */
13173         loadScripts : false,
13174
13175         /**
13176         * Blank page URL to use with SSL file uploads (Defaults to "javascript:false").
13177         * @type String
13178         */
13179         sslBlankUrl : (Roo.SSL_SECURE_URL || "javascript:false"),
13180         /**
13181          * Whether to append unique parameter on get request to disable caching (Defaults to false).
13182          * @type Boolean
13183          */
13184         disableCaching : false,
13185         /**
13186          * Whether to show indicatorText when loading (Defaults to true).
13187          * @type Boolean
13188          */
13189         showLoadIndicator : true,
13190         /**
13191          * Text for loading indicator (Defaults to '&lt;div class="loading-indicator"&gt;Loading...&lt;/div&gt;').
13192          * @type String
13193          */
13194         indicatorText : '<div class="loading-indicator">Loading...</div>'
13195    };
13196
13197 /**
13198  * Static convenience method. This method is deprecated in favor of el.load({url:'foo.php', ...}).
13199  *Usage:
13200  * <pre><code>Roo.UpdateManager.updateElement("my-div", "stuff.php");</code></pre>
13201  * @param {String/HTMLElement/Roo.Element} el The element to update
13202  * @param {String} url The url
13203  * @param {String/Object} params (optional) Url encoded param string or an object of name/value pairs
13204  * @param {Object} options (optional) A config object with any of the UpdateManager properties you want to set - for example: {disableCaching:true, indicatorText: "Loading data..."}
13205  * @static
13206  * @deprecated
13207  * @member Roo.UpdateManager
13208  */
13209 Roo.UpdateManager.updateElement = function(el, url, params, options){
13210     var um = Roo.get(el, true).getUpdateManager();
13211     Roo.apply(um, options);
13212     um.update(url, params, options ? options.callback : null);
13213 };
13214 // alias for backwards compat
13215 Roo.UpdateManager.update = Roo.UpdateManager.updateElement;
13216 /**
13217  * @class Roo.UpdateManager.BasicRenderer
13218  * Default Content renderer. Updates the elements innerHTML with the responseText.
13219  */
13220 Roo.UpdateManager.BasicRenderer = function(){};
13221
13222 Roo.UpdateManager.BasicRenderer.prototype = {
13223     /**
13224      * This is called when the transaction is completed and it's time to update the element - The BasicRenderer
13225      * updates the elements innerHTML with the responseText - To perform a custom render (i.e. XML or JSON processing),
13226      * create an object with a "render(el, response)" method and pass it to setRenderer on the UpdateManager.
13227      * @param {Roo.Element} el The element being rendered
13228      * @param {Object} response The YUI Connect response object
13229      * @param {UpdateManager} updateManager The calling update manager
13230      * @param {Function} callback A callback that will need to be called if loadScripts is true on the UpdateManager
13231      */
13232      render : function(el, response, updateManager, callback){
13233         el.update(response.responseText, updateManager.loadScripts, callback);
13234     }
13235 };
13236 /*
13237  * Based on:
13238  * Roo JS
13239  * (c)) Alan Knowles
13240  * Licence : LGPL
13241  */
13242
13243
13244 /**
13245  * @class Roo.DomTemplate
13246  * @extends Roo.Template
13247  * An effort at a dom based template engine..
13248  *
13249  * Similar to XTemplate, except it uses dom parsing to create the template..
13250  *
13251  * Supported features:
13252  *
13253  *  Tags:
13254
13255 <pre><code>
13256       {a_variable} - output encoded.
13257       {a_variable.format:("Y-m-d")} - call a method on the variable
13258       {a_variable:raw} - unencoded output
13259       {a_variable:toFixed(1,2)} - Roo.util.Format."toFixed"
13260       {a_variable:this.method_on_template(...)} - call a method on the template object.
13261  
13262 </code></pre>
13263  *  The tpl tag:
13264 <pre><code>
13265         &lt;div roo-for="a_variable or condition.."&gt;&lt;/div&gt;
13266         &lt;div roo-if="a_variable or condition"&gt;&lt;/div&gt;
13267         &lt;div roo-exec="some javascript"&gt;&lt;/div&gt;
13268         &lt;div roo-name="named_template"&gt;&lt;/div&gt; 
13269   
13270 </code></pre>
13271  *      
13272  */
13273 Roo.DomTemplate = function()
13274 {
13275      Roo.DomTemplate.superclass.constructor.apply(this, arguments);
13276      if (this.html) {
13277         this.compile();
13278      }
13279 };
13280
13281
13282 Roo.extend(Roo.DomTemplate, Roo.Template, {
13283     /**
13284      * id counter for sub templates.
13285      */
13286     id : 0,
13287     /**
13288      * flag to indicate if dom parser is inside a pre,
13289      * it will strip whitespace if not.
13290      */
13291     inPre : false,
13292     
13293     /**
13294      * The various sub templates
13295      */
13296     tpls : false,
13297     
13298     
13299     
13300     /**
13301      *
13302      * basic tag replacing syntax
13303      * WORD:WORD()
13304      *
13305      * // you can fake an object call by doing this
13306      *  x.t:(test,tesT) 
13307      * 
13308      */
13309     re : /(\{|\%7B)([\w-\.]+)(?:\:([\w\.]*)(?:\(([^)]*?)?\))?)?(\}|\%7D)/g,
13310     //re : /\{([\w-\.]+)(?:\:([\w\.]*)(?:\((.*?)?\))?)?\}/g,
13311     
13312     iterChild : function (node, method) {
13313         
13314         var oldPre = this.inPre;
13315         if (node.tagName == 'PRE') {
13316             this.inPre = true;
13317         }
13318         for( var i = 0; i < node.childNodes.length; i++) {
13319             method.call(this, node.childNodes[i]);
13320         }
13321         this.inPre = oldPre;
13322     },
13323     
13324     
13325     
13326     /**
13327      * compile the template
13328      *
13329      * This is not recursive, so I'm not sure how nested templates are really going to be handled..
13330      *
13331      */
13332     compile: function()
13333     {
13334         var s = this.html;
13335         
13336         // covert the html into DOM...
13337         var doc = false;
13338         var div =false;
13339         try {
13340             doc = document.implementation.createHTMLDocument("");
13341             doc.documentElement.innerHTML =   this.html  ;
13342             div = doc.documentElement;
13343         } catch (e) {
13344             // old IE... - nasty -- it causes all sorts of issues.. with
13345             // images getting pulled from server..
13346             div = document.createElement('div');
13347             div.innerHTML = this.html;
13348         }
13349         //doc.documentElement.innerHTML = htmlBody
13350          
13351         
13352         
13353         this.tpls = [];
13354         var _t = this;
13355         this.iterChild(div, function(n) {_t.compileNode(n, true); });
13356         
13357         var tpls = this.tpls;
13358         
13359         // create a top level template from the snippet..
13360         
13361         //Roo.log(div.innerHTML);
13362         
13363         var tpl = {
13364             uid : 'master',
13365             id : this.id++,
13366             attr : false,
13367             value : false,
13368             body : div.innerHTML,
13369             
13370             forCall : false,
13371             execCall : false,
13372             dom : div,
13373             isTop : true
13374             
13375         };
13376         tpls.unshift(tpl);
13377         
13378         
13379         // compile them...
13380         this.tpls = [];
13381         Roo.each(tpls, function(tp){
13382             this.compileTpl(tp);
13383             this.tpls[tp.id] = tp;
13384         }, this);
13385         
13386         this.master = tpls[0];
13387         return this;
13388         
13389         
13390     },
13391     
13392     compileNode : function(node, istop) {
13393         // test for
13394         //Roo.log(node);
13395         
13396         
13397         // skip anything not a tag..
13398         if (node.nodeType != 1) {
13399             if (node.nodeType == 3 && !this.inPre) {
13400                 // reduce white space..
13401                 node.nodeValue = node.nodeValue.replace(/\s+/g, ' '); 
13402                 
13403             }
13404             return;
13405         }
13406         
13407         var tpl = {
13408             uid : false,
13409             id : false,
13410             attr : false,
13411             value : false,
13412             body : '',
13413             
13414             forCall : false,
13415             execCall : false,
13416             dom : false,
13417             isTop : istop
13418             
13419             
13420         };
13421         
13422         
13423         switch(true) {
13424             case (node.hasAttribute('roo-for')): tpl.attr = 'for'; break;
13425             case (node.hasAttribute('roo-if')): tpl.attr = 'if'; break;
13426             case (node.hasAttribute('roo-name')): tpl.attr = 'name'; break;
13427             case (node.hasAttribute('roo-exec')): tpl.attr = 'exec'; break;
13428             // no default..
13429         }
13430         
13431         
13432         if (!tpl.attr) {
13433             // just itterate children..
13434             this.iterChild(node,this.compileNode);
13435             return;
13436         }
13437         tpl.uid = this.id++;
13438         tpl.value = node.getAttribute('roo-' +  tpl.attr);
13439         node.removeAttribute('roo-'+ tpl.attr);
13440         if (tpl.attr != 'name') {
13441             var placeholder = document.createTextNode('{domtpl' + tpl.uid + '}');
13442             node.parentNode.replaceChild(placeholder,  node);
13443         } else {
13444             
13445             var placeholder =  document.createElement('span');
13446             placeholder.className = 'roo-tpl-' + tpl.value;
13447             node.parentNode.replaceChild(placeholder,  node);
13448         }
13449         
13450         // parent now sees '{domtplXXXX}
13451         this.iterChild(node,this.compileNode);
13452         
13453         // we should now have node body...
13454         var div = document.createElement('div');
13455         div.appendChild(node);
13456         tpl.dom = node;
13457         // this has the unfortunate side effect of converting tagged attributes
13458         // eg. href="{...}" into %7C...%7D
13459         // this has been fixed by searching for those combo's although it's a bit hacky..
13460         
13461         
13462         tpl.body = div.innerHTML;
13463         
13464         
13465          
13466         tpl.id = tpl.uid;
13467         switch(tpl.attr) {
13468             case 'for' :
13469                 switch (tpl.value) {
13470                     case '.':  tpl.forCall = new Function('values', 'parent', 'with(values){ return values; }'); break;
13471                     case '..': tpl.forCall= new Function('values', 'parent', 'with(values){ return parent; }'); break;
13472                     default:   tpl.forCall= new Function('values', 'parent', 'with(values){ return '+tpl.value+'; }');
13473                 }
13474                 break;
13475             
13476             case 'exec':
13477                 tpl.execCall = new Function('values', 'parent', 'with(values){ '+(Roo.util.Format.htmlDecode(tpl.value))+'; }');
13478                 break;
13479             
13480             case 'if':     
13481                 tpl.ifCall = new Function('values', 'parent', 'with(values){ return '+(Roo.util.Format.htmlDecode(tpl.value))+'; }');
13482                 break;
13483             
13484             case 'name':
13485                 tpl.id  = tpl.value; // replace non characters???
13486                 break;
13487             
13488         }
13489         
13490         
13491         this.tpls.push(tpl);
13492         
13493         
13494         
13495     },
13496     
13497     
13498     
13499     
13500     /**
13501      * Compile a segment of the template into a 'sub-template'
13502      *
13503      * 
13504      * 
13505      *
13506      */
13507     compileTpl : function(tpl)
13508     {
13509         var fm = Roo.util.Format;
13510         var useF = this.disableFormats !== true;
13511         
13512         var sep = Roo.isGecko ? "+\n" : ",\n";
13513         
13514         var undef = function(str) {
13515             Roo.debug && Roo.log("Property not found :"  + str);
13516             return '';
13517         };
13518           
13519         //Roo.log(tpl.body);
13520         
13521         
13522         
13523         var fn = function(m, lbrace, name, format, args)
13524         {
13525             //Roo.log("ARGS");
13526             //Roo.log(arguments);
13527             args = args ? args.replace(/\\'/g,"'") : args;
13528             //["{TEST:(a,b,c)}", "TEST", "", "a,b,c", 0, "{TEST:(a,b,c)}"]
13529             if (typeof(format) == 'undefined') {
13530                 format =  'htmlEncode'; 
13531             }
13532             if (format == 'raw' ) {
13533                 format = false;
13534             }
13535             
13536             if(name.substr(0, 6) == 'domtpl'){
13537                 return "'"+ sep +'this.applySubTemplate('+name.substr(6)+', values, parent)'+sep+"'";
13538             }
13539             
13540             // build an array of options to determine if value is undefined..
13541             
13542             // basically get 'xxxx.yyyy' then do
13543             // (typeof(xxxx) == 'undefined' || typeof(xxx.yyyy) == 'undefined') ?
13544             //    (function () { Roo.log("Property not found"); return ''; })() :
13545             //    ......
13546             
13547             var udef_ar = [];
13548             var lookfor = '';
13549             Roo.each(name.split('.'), function(st) {
13550                 lookfor += (lookfor.length ? '.': '') + st;
13551                 udef_ar.push(  "(typeof(" + lookfor + ") == 'undefined')"  );
13552             });
13553             
13554             var udef_st = '((' + udef_ar.join(" || ") +") ? undef('" + name + "') : "; // .. needs )
13555             
13556             
13557             if(format && useF){
13558                 
13559                 args = args ? ',' + args : "";
13560                  
13561                 if(format.substr(0, 5) != "this."){
13562                     format = "fm." + format + '(';
13563                 }else{
13564                     format = 'this.call("'+ format.substr(5) + '", ';
13565                     args = ", values";
13566                 }
13567                 
13568                 return "'"+ sep +   udef_st   +    format + name + args + "))"+sep+"'";
13569             }
13570              
13571             if (args && args.length) {
13572                 // called with xxyx.yuu:(test,test)
13573                 // change to ()
13574                 return "'"+ sep + udef_st  + name + '(' +  args + "))"+sep+"'";
13575             }
13576             // raw.. - :raw modifier..
13577             return "'"+ sep + udef_st  + name + ")"+sep+"'";
13578             
13579         };
13580         var body;
13581         // branched to use + in gecko and [].join() in others
13582         if(Roo.isGecko){
13583             body = "tpl.compiled = function(values, parent){  with(values) { return '" +
13584                    tpl.body.replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn) +
13585                     "';};};";
13586         }else{
13587             body = ["tpl.compiled = function(values, parent){  with (values) { return ['"];
13588             body.push(tpl.body.replace(/(\r\n|\n)/g,
13589                             '\\n').replace(/'/g, "\\'").replace(this.re, fn));
13590             body.push("'].join('');};};");
13591             body = body.join('');
13592         }
13593         
13594         Roo.debug && Roo.log(body.replace(/\\n/,'\n'));
13595        
13596         /** eval:var:tpl eval:var:fm eval:var:useF eval:var:undef  */
13597         eval(body);
13598         
13599         return this;
13600     },
13601      
13602     /**
13603      * same as applyTemplate, except it's done to one of the subTemplates
13604      * when using named templates, you can do:
13605      *
13606      * var str = pl.applySubTemplate('your-name', values);
13607      *
13608      * 
13609      * @param {Number} id of the template
13610      * @param {Object} values to apply to template
13611      * @param {Object} parent (normaly the instance of this object)
13612      */
13613     applySubTemplate : function(id, values, parent)
13614     {
13615         
13616         
13617         var t = this.tpls[id];
13618         
13619         
13620         try { 
13621             if(t.ifCall && !t.ifCall.call(this, values, parent)){
13622                 Roo.debug && Roo.log('if call on ' + t.value + ' return false');
13623                 return '';
13624             }
13625         } catch(e) {
13626             Roo.log('Xtemplate.applySubTemplate('+ id+ '): Exception thrown on roo-if="' + t.value + '" - ' + e.toString());
13627             Roo.log(values);
13628           
13629             return '';
13630         }
13631         try { 
13632             
13633             if(t.execCall && t.execCall.call(this, values, parent)){
13634                 return '';
13635             }
13636         } catch(e) {
13637             Roo.log('Xtemplate.applySubTemplate('+ id+ '): Exception thrown on roo-for="' + t.value + '" - ' + e.toString());
13638             Roo.log(values);
13639             return '';
13640         }
13641         
13642         try {
13643             var vs = t.forCall ? t.forCall.call(this, values, parent) : values;
13644             parent = t.target ? values : parent;
13645             if(t.forCall && vs instanceof Array){
13646                 var buf = [];
13647                 for(var i = 0, len = vs.length; i < len; i++){
13648                     try {
13649                         buf[buf.length] = t.compiled.call(this, vs[i], parent);
13650                     } catch (e) {
13651                         Roo.log('Xtemplate.applySubTemplate('+ id+ '): Exception thrown on body="' + t.value + '" - ' + e.toString());
13652                         Roo.log(e.body);
13653                         //Roo.log(t.compiled);
13654                         Roo.log(vs[i]);
13655                     }   
13656                 }
13657                 return buf.join('');
13658             }
13659         } catch (e) {
13660             Roo.log('Xtemplate.applySubTemplate('+ id+ '): Exception thrown on roo-for="' + t.value + '" - ' + e.toString());
13661             Roo.log(values);
13662             return '';
13663         }
13664         try {
13665             return t.compiled.call(this, vs, parent);
13666         } catch (e) {
13667             Roo.log('Xtemplate.applySubTemplate('+ id+ '): Exception thrown on body="' + t.value + '" - ' + e.toString());
13668             Roo.log(e.body);
13669             //Roo.log(t.compiled);
13670             Roo.log(values);
13671             return '';
13672         }
13673     },
13674
13675    
13676
13677     applyTemplate : function(values){
13678         return this.master.compiled.call(this, values, {});
13679         //var s = this.subs;
13680     },
13681
13682     apply : function(){
13683         return this.applyTemplate.apply(this, arguments);
13684     }
13685
13686  });
13687
13688 Roo.DomTemplate.from = function(el){
13689     el = Roo.getDom(el);
13690     return new Roo.Domtemplate(el.value || el.innerHTML);
13691 };/*
13692  * Based on:
13693  * Ext JS Library 1.1.1
13694  * Copyright(c) 2006-2007, Ext JS, LLC.
13695  *
13696  * Originally Released Under LGPL - original licence link has changed is not relivant.
13697  *
13698  * Fork - LGPL
13699  * <script type="text/javascript">
13700  */
13701
13702 /**
13703  * @class Roo.util.DelayedTask
13704  * Provides a convenient method of performing setTimeout where a new
13705  * timeout cancels the old timeout. An example would be performing validation on a keypress.
13706  * You can use this class to buffer
13707  * the keypress events for a certain number of milliseconds, and perform only if they stop
13708  * for that amount of time.
13709  * @constructor The parameters to this constructor serve as defaults and are not required.
13710  * @param {Function} fn (optional) The default function to timeout
13711  * @param {Object} scope (optional) The default scope of that timeout
13712  * @param {Array} args (optional) The default Array of arguments
13713  */
13714 Roo.util.DelayedTask = function(fn, scope, args){
13715     var id = null, d, t;
13716
13717     var call = function(){
13718         var now = new Date().getTime();
13719         if(now - t >= d){
13720             clearInterval(id);
13721             id = null;
13722             fn.apply(scope, args || []);
13723         }
13724     };
13725     /**
13726      * Cancels any pending timeout and queues a new one
13727      * @param {Number} delay The milliseconds to delay
13728      * @param {Function} newFn (optional) Overrides function passed to constructor
13729      * @param {Object} newScope (optional) Overrides scope passed to constructor
13730      * @param {Array} newArgs (optional) Overrides args passed to constructor
13731      */
13732     this.delay = function(delay, newFn, newScope, newArgs){
13733         if(id && delay != d){
13734             this.cancel();
13735         }
13736         d = delay;
13737         t = new Date().getTime();
13738         fn = newFn || fn;
13739         scope = newScope || scope;
13740         args = newArgs || args;
13741         if(!id){
13742             id = setInterval(call, d);
13743         }
13744     };
13745
13746     /**
13747      * Cancel the last queued timeout
13748      */
13749     this.cancel = function(){
13750         if(id){
13751             clearInterval(id);
13752             id = null;
13753         }
13754     };
13755 };/*
13756  * Based on:
13757  * Ext JS Library 1.1.1
13758  * Copyright(c) 2006-2007, Ext JS, LLC.
13759  *
13760  * Originally Released Under LGPL - original licence link has changed is not relivant.
13761  *
13762  * Fork - LGPL
13763  * <script type="text/javascript">
13764  */
13765 /**
13766  * @class Roo.util.TaskRunner
13767  * Manage background tasks - not sure why this is better that setInterval?
13768  * @static
13769  *
13770  */
13771  
13772 Roo.util.TaskRunner = function(interval){
13773     interval = interval || 10;
13774     var tasks = [], removeQueue = [];
13775     var id = 0;
13776     var running = false;
13777
13778     var stopThread = function(){
13779         running = false;
13780         clearInterval(id);
13781         id = 0;
13782     };
13783
13784     var startThread = function(){
13785         if(!running){
13786             running = true;
13787             id = setInterval(runTasks, interval);
13788         }
13789     };
13790
13791     var removeTask = function(task){
13792         removeQueue.push(task);
13793         if(task.onStop){
13794             task.onStop();
13795         }
13796     };
13797
13798     var runTasks = function(){
13799         if(removeQueue.length > 0){
13800             for(var i = 0, len = removeQueue.length; i < len; i++){
13801                 tasks.remove(removeQueue[i]);
13802             }
13803             removeQueue = [];
13804             if(tasks.length < 1){
13805                 stopThread();
13806                 return;
13807             }
13808         }
13809         var now = new Date().getTime();
13810         for(var i = 0, len = tasks.length; i < len; ++i){
13811             var t = tasks[i];
13812             var itime = now - t.taskRunTime;
13813             if(t.interval <= itime){
13814                 var rt = t.run.apply(t.scope || t, t.args || [++t.taskRunCount]);
13815                 t.taskRunTime = now;
13816                 if(rt === false || t.taskRunCount === t.repeat){
13817                     removeTask(t);
13818                     return;
13819                 }
13820             }
13821             if(t.duration && t.duration <= (now - t.taskStartTime)){
13822                 removeTask(t);
13823             }
13824         }
13825     };
13826
13827     /**
13828      * Queues a new task.
13829      * @param {Object} task
13830      *
13831      * Task property : interval = how frequent to run.
13832      * Task object should implement
13833      * function run()
13834      * Task object may implement
13835      * function onStop()
13836      */
13837     this.start = function(task){
13838         tasks.push(task);
13839         task.taskStartTime = new Date().getTime();
13840         task.taskRunTime = 0;
13841         task.taskRunCount = 0;
13842         startThread();
13843         return task;
13844     };
13845     /**
13846      * Stop  new task.
13847      * @param {Object} task
13848      */
13849     this.stop = function(task){
13850         removeTask(task);
13851         return task;
13852     };
13853     /**
13854      * Stop all Tasks
13855      */
13856     this.stopAll = function(){
13857         stopThread();
13858         for(var i = 0, len = tasks.length; i < len; i++){
13859             if(tasks[i].onStop){
13860                 tasks[i].onStop();
13861             }
13862         }
13863         tasks = [];
13864         removeQueue = [];
13865     };
13866 };
13867
13868 Roo.TaskMgr = new Roo.util.TaskRunner();/*
13869  * Based on:
13870  * Ext JS Library 1.1.1
13871  * Copyright(c) 2006-2007, Ext JS, LLC.
13872  *
13873  * Originally Released Under LGPL - original licence link has changed is not relivant.
13874  *
13875  * Fork - LGPL
13876  * <script type="text/javascript">
13877  */
13878
13879  
13880 /**
13881  * @class Roo.util.MixedCollection
13882  * @extends Roo.util.Observable
13883  * A Collection class that maintains both numeric indexes and keys and exposes events.
13884  * @constructor
13885  * @param {Boolean} allowFunctions True if the addAll function should add function references to the
13886  * collection (defaults to false)
13887  * @param {Function} keyFn A function that can accept an item of the type(s) stored in this MixedCollection
13888  * and return the key value for that item.  This is used when available to look up the key on items that
13889  * were passed without an explicit key parameter to a MixedCollection method.  Passing this parameter is
13890  * equivalent to providing an implementation for the {@link #getKey} method.
13891  */
13892 Roo.util.MixedCollection = function(allowFunctions, keyFn){
13893     this.items = [];
13894     this.map = {};
13895     this.keys = [];
13896     this.length = 0;
13897     this.addEvents({
13898         /**
13899          * @event clear
13900          * Fires when the collection is cleared.
13901          */
13902         "clear" : true,
13903         /**
13904          * @event add
13905          * Fires when an item is added to the collection.
13906          * @param {Number} index The index at which the item was added.
13907          * @param {Object} o The item added.
13908          * @param {String} key The key associated with the added item.
13909          */
13910         "add" : true,
13911         /**
13912          * @event replace
13913          * Fires when an item is replaced in the collection.
13914          * @param {String} key he key associated with the new added.
13915          * @param {Object} old The item being replaced.
13916          * @param {Object} new The new item.
13917          */
13918         "replace" : true,
13919         /**
13920          * @event remove
13921          * Fires when an item is removed from the collection.
13922          * @param {Object} o The item being removed.
13923          * @param {String} key (optional) The key associated with the removed item.
13924          */
13925         "remove" : true,
13926         "sort" : true
13927     });
13928     this.allowFunctions = allowFunctions === true;
13929     if(keyFn){
13930         this.getKey = keyFn;
13931     }
13932     Roo.util.MixedCollection.superclass.constructor.call(this);
13933 };
13934
13935 Roo.extend(Roo.util.MixedCollection, Roo.util.Observable, {
13936     allowFunctions : false,
13937     
13938 /**
13939  * Adds an item to the collection.
13940  * @param {String} key The key to associate with the item
13941  * @param {Object} o The item to add.
13942  * @return {Object} The item added.
13943  */
13944     add : function(key, o){
13945         if(arguments.length == 1){
13946             o = arguments[0];
13947             key = this.getKey(o);
13948         }
13949         if(typeof key == "undefined" || key === null){
13950             this.length++;
13951             this.items.push(o);
13952             this.keys.push(null);
13953         }else{
13954             var old = this.map[key];
13955             if(old){
13956                 return this.replace(key, o);
13957             }
13958             this.length++;
13959             this.items.push(o);
13960             this.map[key] = o;
13961             this.keys.push(key);
13962         }
13963         this.fireEvent("add", this.length-1, o, key);
13964         return o;
13965     },
13966        
13967 /**
13968   * MixedCollection has a generic way to fetch keys if you implement getKey.
13969 <pre><code>
13970 // normal way
13971 var mc = new Roo.util.MixedCollection();
13972 mc.add(someEl.dom.id, someEl);
13973 mc.add(otherEl.dom.id, otherEl);
13974 //and so on
13975
13976 // using getKey
13977 var mc = new Roo.util.MixedCollection();
13978 mc.getKey = function(el){
13979    return el.dom.id;
13980 };
13981 mc.add(someEl);
13982 mc.add(otherEl);
13983
13984 // or via the constructor
13985 var mc = new Roo.util.MixedCollection(false, function(el){
13986    return el.dom.id;
13987 });
13988 mc.add(someEl);
13989 mc.add(otherEl);
13990 </code></pre>
13991  * @param o {Object} The item for which to find the key.
13992  * @return {Object} The key for the passed item.
13993  */
13994     getKey : function(o){
13995          return o.id; 
13996     },
13997    
13998 /**
13999  * Replaces an item in the collection.
14000  * @param {String} key The key associated with the item to replace, or the item to replace.
14001  * @param o {Object} o (optional) If the first parameter passed was a key, the item to associate with that key.
14002  * @return {Object}  The new item.
14003  */
14004     replace : function(key, o){
14005         if(arguments.length == 1){
14006             o = arguments[0];
14007             key = this.getKey(o);
14008         }
14009         var old = this.item(key);
14010         if(typeof key == "undefined" || key === null || typeof old == "undefined"){
14011              return this.add(key, o);
14012         }
14013         var index = this.indexOfKey(key);
14014         this.items[index] = o;
14015         this.map[key] = o;
14016         this.fireEvent("replace", key, old, o);
14017         return o;
14018     },
14019    
14020 /**
14021  * Adds all elements of an Array or an Object to the collection.
14022  * @param {Object/Array} objs An Object containing properties which will be added to the collection, or
14023  * an Array of values, each of which are added to the collection.
14024  */
14025     addAll : function(objs){
14026         if(arguments.length > 1 || objs instanceof Array){
14027             var args = arguments.length > 1 ? arguments : objs;
14028             for(var i = 0, len = args.length; i < len; i++){
14029                 this.add(args[i]);
14030             }
14031         }else{
14032             for(var key in objs){
14033                 if(this.allowFunctions || typeof objs[key] != "function"){
14034                     this.add(key, objs[key]);
14035                 }
14036             }
14037         }
14038     },
14039    
14040 /**
14041  * Executes the specified function once for every item in the collection, passing each
14042  * item as the first and only parameter. returning false from the function will stop the iteration.
14043  * @param {Function} fn The function to execute for each item.
14044  * @param {Object} scope (optional) The scope in which to execute the function.
14045  */
14046     each : function(fn, scope){
14047         var items = [].concat(this.items); // each safe for removal
14048         for(var i = 0, len = items.length; i < len; i++){
14049             if(fn.call(scope || items[i], items[i], i, len) === false){
14050                 break;
14051             }
14052         }
14053     },
14054    
14055 /**
14056  * Executes the specified function once for every key in the collection, passing each
14057  * key, and its associated item as the first two parameters.
14058  * @param {Function} fn The function to execute for each item.
14059  * @param {Object} scope (optional) The scope in which to execute the function.
14060  */
14061     eachKey : function(fn, scope){
14062         for(var i = 0, len = this.keys.length; i < len; i++){
14063             fn.call(scope || window, this.keys[i], this.items[i], i, len);
14064         }
14065     },
14066    
14067 /**
14068  * Returns the first item in the collection which elicits a true return value from the
14069  * passed selection function.
14070  * @param {Function} fn The selection function to execute for each item.
14071  * @param {Object} scope (optional) The scope in which to execute the function.
14072  * @return {Object} The first item in the collection which returned true from the selection function.
14073  */
14074     find : function(fn, scope){
14075         for(var i = 0, len = this.items.length; i < len; i++){
14076             if(fn.call(scope || window, this.items[i], this.keys[i])){
14077                 return this.items[i];
14078             }
14079         }
14080         return null;
14081     },
14082    
14083 /**
14084  * Inserts an item at the specified index in the collection.
14085  * @param {Number} index The index to insert the item at.
14086  * @param {String} key The key to associate with the new item, or the item itself.
14087  * @param {Object} o  (optional) If the second parameter was a key, the new item.
14088  * @return {Object} The item inserted.
14089  */
14090     insert : function(index, key, o){
14091         if(arguments.length == 2){
14092             o = arguments[1];
14093             key = this.getKey(o);
14094         }
14095         if(index >= this.length){
14096             return this.add(key, o);
14097         }
14098         this.length++;
14099         this.items.splice(index, 0, o);
14100         if(typeof key != "undefined" && key != null){
14101             this.map[key] = o;
14102         }
14103         this.keys.splice(index, 0, key);
14104         this.fireEvent("add", index, o, key);
14105         return o;
14106     },
14107    
14108 /**
14109  * Removed an item from the collection.
14110  * @param {Object} o The item to remove.
14111  * @return {Object} The item removed.
14112  */
14113     remove : function(o){
14114         return this.removeAt(this.indexOf(o));
14115     },
14116    
14117 /**
14118  * Remove an item from a specified index in the collection.
14119  * @param {Number} index The index within the collection of the item to remove.
14120  */
14121     removeAt : function(index){
14122         if(index < this.length && index >= 0){
14123             this.length--;
14124             var o = this.items[index];
14125             this.items.splice(index, 1);
14126             var key = this.keys[index];
14127             if(typeof key != "undefined"){
14128                 delete this.map[key];
14129             }
14130             this.keys.splice(index, 1);
14131             this.fireEvent("remove", o, key);
14132         }
14133     },
14134    
14135 /**
14136  * Removed an item associated with the passed key fom the collection.
14137  * @param {String} key The key of the item to remove.
14138  */
14139     removeKey : function(key){
14140         return this.removeAt(this.indexOfKey(key));
14141     },
14142    
14143 /**
14144  * Returns the number of items in the collection.
14145  * @return {Number} the number of items in the collection.
14146  */
14147     getCount : function(){
14148         return this.length; 
14149     },
14150    
14151 /**
14152  * Returns index within the collection of the passed Object.
14153  * @param {Object} o The item to find the index of.
14154  * @return {Number} index of the item.
14155  */
14156     indexOf : function(o){
14157         if(!this.items.indexOf){
14158             for(var i = 0, len = this.items.length; i < len; i++){
14159                 if(this.items[i] == o) {
14160                     return i;
14161                 }
14162             }
14163             return -1;
14164         }else{
14165             return this.items.indexOf(o);
14166         }
14167     },
14168    
14169 /**
14170  * Returns index within the collection of the passed key.
14171  * @param {String} key The key to find the index of.
14172  * @return {Number} index of the key.
14173  */
14174     indexOfKey : function(key){
14175         if(!this.keys.indexOf){
14176             for(var i = 0, len = this.keys.length; i < len; i++){
14177                 if(this.keys[i] == key) {
14178                     return i;
14179                 }
14180             }
14181             return -1;
14182         }else{
14183             return this.keys.indexOf(key);
14184         }
14185     },
14186    
14187 /**
14188  * Returns the item associated with the passed key OR index. Key has priority over index.
14189  * @param {String/Number} key The key or index of the item.
14190  * @return {Object} The item associated with the passed key.
14191  */
14192     item : function(key){
14193         if (key === 'length') {
14194             return null;
14195         }
14196         var item = typeof this.map[key] != "undefined" ? this.map[key] : this.items[key];
14197         return typeof item != 'function' || this.allowFunctions ? item : null; // for prototype!
14198     },
14199     
14200 /**
14201  * Returns the item at the specified index.
14202  * @param {Number} index The index of the item.
14203  * @return {Object}
14204  */
14205     itemAt : function(index){
14206         return this.items[index];
14207     },
14208     
14209 /**
14210  * Returns the item associated with the passed key.
14211  * @param {String/Number} key The key of the item.
14212  * @return {Object} The item associated with the passed key.
14213  */
14214     key : function(key){
14215         return this.map[key];
14216     },
14217    
14218 /**
14219  * Returns true if the collection contains the passed Object as an item.
14220  * @param {Object} o  The Object to look for in the collection.
14221  * @return {Boolean} True if the collection contains the Object as an item.
14222  */
14223     contains : function(o){
14224         return this.indexOf(o) != -1;
14225     },
14226    
14227 /**
14228  * Returns true if the collection contains the passed Object as a key.
14229  * @param {String} key The key to look for in the collection.
14230  * @return {Boolean} True if the collection contains the Object as a key.
14231  */
14232     containsKey : function(key){
14233         return typeof this.map[key] != "undefined";
14234     },
14235    
14236 /**
14237  * Removes all items from the collection.
14238  */
14239     clear : function(){
14240         this.length = 0;
14241         this.items = [];
14242         this.keys = [];
14243         this.map = {};
14244         this.fireEvent("clear");
14245     },
14246    
14247 /**
14248  * Returns the first item in the collection.
14249  * @return {Object} the first item in the collection..
14250  */
14251     first : function(){
14252         return this.items[0]; 
14253     },
14254    
14255 /**
14256  * Returns the last item in the collection.
14257  * @return {Object} the last item in the collection..
14258  */
14259     last : function(){
14260         return this.items[this.length-1];   
14261     },
14262     
14263     _sort : function(property, dir, fn){
14264         var dsc = String(dir).toUpperCase() == "DESC" ? -1 : 1;
14265         fn = fn || function(a, b){
14266             return a-b;
14267         };
14268         var c = [], k = this.keys, items = this.items;
14269         for(var i = 0, len = items.length; i < len; i++){
14270             c[c.length] = {key: k[i], value: items[i], index: i};
14271         }
14272         c.sort(function(a, b){
14273             var v = fn(a[property], b[property]) * dsc;
14274             if(v == 0){
14275                 v = (a.index < b.index ? -1 : 1);
14276             }
14277             return v;
14278         });
14279         for(var i = 0, len = c.length; i < len; i++){
14280             items[i] = c[i].value;
14281             k[i] = c[i].key;
14282         }
14283         this.fireEvent("sort", this);
14284     },
14285     
14286     /**
14287      * Sorts this collection with the passed comparison function
14288      * @param {String} direction (optional) "ASC" or "DESC"
14289      * @param {Function} fn (optional) comparison function
14290      */
14291     sort : function(dir, fn){
14292         this._sort("value", dir, fn);
14293     },
14294     
14295     /**
14296      * Sorts this collection by keys
14297      * @param {String} direction (optional) "ASC" or "DESC"
14298      * @param {Function} fn (optional) a comparison function (defaults to case insensitive string)
14299      */
14300     keySort : function(dir, fn){
14301         this._sort("key", dir, fn || function(a, b){
14302             return String(a).toUpperCase()-String(b).toUpperCase();
14303         });
14304     },
14305     
14306     /**
14307      * Returns a range of items in this collection
14308      * @param {Number} startIndex (optional) defaults to 0
14309      * @param {Number} endIndex (optional) default to the last item
14310      * @return {Array} An array of items
14311      */
14312     getRange : function(start, end){
14313         var items = this.items;
14314         if(items.length < 1){
14315             return [];
14316         }
14317         start = start || 0;
14318         end = Math.min(typeof end == "undefined" ? this.length-1 : end, this.length-1);
14319         var r = [];
14320         if(start <= end){
14321             for(var i = start; i <= end; i++) {
14322                     r[r.length] = items[i];
14323             }
14324         }else{
14325             for(var i = start; i >= end; i--) {
14326                     r[r.length] = items[i];
14327             }
14328         }
14329         return r;
14330     },
14331         
14332     /**
14333      * Filter the <i>objects</i> in this collection by a specific property. 
14334      * Returns a new collection that has been filtered.
14335      * @param {String} property A property on your objects
14336      * @param {String/RegExp} value Either string that the property values 
14337      * should start with or a RegExp to test against the property
14338      * @return {MixedCollection} The new filtered collection
14339      */
14340     filter : function(property, value){
14341         if(!value.exec){ // not a regex
14342             value = String(value);
14343             if(value.length == 0){
14344                 return this.clone();
14345             }
14346             value = new RegExp("^" + Roo.escapeRe(value), "i");
14347         }
14348         return this.filterBy(function(o){
14349             return o && value.test(o[property]);
14350         });
14351         },
14352     
14353     /**
14354      * Filter by a function. * Returns a new collection that has been filtered.
14355      * The passed function will be called with each 
14356      * object in the collection. If the function returns true, the value is included 
14357      * otherwise it is filtered.
14358      * @param {Function} fn The function to be called, it will receive the args o (the object), k (the key)
14359      * @param {Object} scope (optional) The scope of the function (defaults to this) 
14360      * @return {MixedCollection} The new filtered collection
14361      */
14362     filterBy : function(fn, scope){
14363         var r = new Roo.util.MixedCollection();
14364         r.getKey = this.getKey;
14365         var k = this.keys, it = this.items;
14366         for(var i = 0, len = it.length; i < len; i++){
14367             if(fn.call(scope||this, it[i], k[i])){
14368                                 r.add(k[i], it[i]);
14369                         }
14370         }
14371         return r;
14372     },
14373     
14374     /**
14375      * Creates a duplicate of this collection
14376      * @return {MixedCollection}
14377      */
14378     clone : function(){
14379         var r = new Roo.util.MixedCollection();
14380         var k = this.keys, it = this.items;
14381         for(var i = 0, len = it.length; i < len; i++){
14382             r.add(k[i], it[i]);
14383         }
14384         r.getKey = this.getKey;
14385         return r;
14386     }
14387 });
14388 /**
14389  * Returns the item associated with the passed key or index.
14390  * @method
14391  * @param {String/Number} key The key or index of the item.
14392  * @return {Object} The item associated with the passed key.
14393  */
14394 Roo.util.MixedCollection.prototype.get = Roo.util.MixedCollection.prototype.item;/*
14395  * Based on:
14396  * Ext JS Library 1.1.1
14397  * Copyright(c) 2006-2007, Ext JS, LLC.
14398  *
14399  * Originally Released Under LGPL - original licence link has changed is not relivant.
14400  *
14401  * Fork - LGPL
14402  * <script type="text/javascript">
14403  */
14404 /**
14405  * @class Roo.util.JSON
14406  * Modified version of Douglas Crockford"s json.js that doesn"t
14407  * mess with the Object prototype 
14408  * http://www.json.org/js.html
14409  * @static
14410  */
14411 Roo.util.JSON = new (function(){
14412     var useHasOwn = {}.hasOwnProperty ? true : false;
14413     
14414     // crashes Safari in some instances
14415     //var validRE = /^("(\\.|[^"\\\n\r])*?"|[,:{}\[\]0-9.\-+Eaeflnr-u \n\r\t])+?$/;
14416     
14417     var pad = function(n) {
14418         return n < 10 ? "0" + n : n;
14419     };
14420     
14421     var m = {
14422         "\b": '\\b',
14423         "\t": '\\t',
14424         "\n": '\\n',
14425         "\f": '\\f',
14426         "\r": '\\r',
14427         '"' : '\\"',
14428         "\\": '\\\\'
14429     };
14430
14431     var encodeString = function(s){
14432         if (/["\\\x00-\x1f]/.test(s)) {
14433             return '"' + s.replace(/([\x00-\x1f\\"])/g, function(a, b) {
14434                 var c = m[b];
14435                 if(c){
14436                     return c;
14437                 }
14438                 c = b.charCodeAt();
14439                 return "\\u00" +
14440                     Math.floor(c / 16).toString(16) +
14441                     (c % 16).toString(16);
14442             }) + '"';
14443         }
14444         return '"' + s + '"';
14445     };
14446     
14447     var encodeArray = function(o){
14448         var a = ["["], b, i, l = o.length, v;
14449             for (i = 0; i < l; i += 1) {
14450                 v = o[i];
14451                 switch (typeof v) {
14452                     case "undefined":
14453                     case "function":
14454                     case "unknown":
14455                         break;
14456                     default:
14457                         if (b) {
14458                             a.push(',');
14459                         }
14460                         a.push(v === null ? "null" : Roo.util.JSON.encode(v));
14461                         b = true;
14462                 }
14463             }
14464             a.push("]");
14465             return a.join("");
14466     };
14467     
14468     var encodeDate = function(o){
14469         return '"' + o.getFullYear() + "-" +
14470                 pad(o.getMonth() + 1) + "-" +
14471                 pad(o.getDate()) + "T" +
14472                 pad(o.getHours()) + ":" +
14473                 pad(o.getMinutes()) + ":" +
14474                 pad(o.getSeconds()) + '"';
14475     };
14476     
14477     /**
14478      * Encodes an Object, Array or other value
14479      * @param {Mixed} o The variable to encode
14480      * @return {String} The JSON string
14481      */
14482     this.encode = function(o)
14483     {
14484         // should this be extended to fully wrap stringify..
14485         
14486         if(typeof o == "undefined" || o === null){
14487             return "null";
14488         }else if(o instanceof Array){
14489             return encodeArray(o);
14490         }else if(o instanceof Date){
14491             return encodeDate(o);
14492         }else if(typeof o == "string"){
14493             return encodeString(o);
14494         }else if(typeof o == "number"){
14495             return isFinite(o) ? String(o) : "null";
14496         }else if(typeof o == "boolean"){
14497             return String(o);
14498         }else {
14499             var a = ["{"], b, i, v;
14500             for (i in o) {
14501                 if(!useHasOwn || o.hasOwnProperty(i)) {
14502                     v = o[i];
14503                     switch (typeof v) {
14504                     case "undefined":
14505                     case "function":
14506                     case "unknown":
14507                         break;
14508                     default:
14509                         if(b){
14510                             a.push(',');
14511                         }
14512                         a.push(this.encode(i), ":",
14513                                 v === null ? "null" : this.encode(v));
14514                         b = true;
14515                     }
14516                 }
14517             }
14518             a.push("}");
14519             return a.join("");
14520         }
14521     };
14522     
14523     /**
14524      * Decodes (parses) a JSON string to an object. If the JSON is invalid, this function throws a SyntaxError.
14525      * @param {String} json The JSON string
14526      * @return {Object} The resulting object
14527      */
14528     this.decode = function(json){
14529         
14530         return  /** eval:var:json */ eval("(" + json + ')');
14531     };
14532 })();
14533 /** 
14534  * Shorthand for {@link Roo.util.JSON#encode}
14535  * @member Roo encode 
14536  * @method */
14537 Roo.encode = typeof(JSON) != 'undefined' && JSON.stringify ? JSON.stringify : Roo.util.JSON.encode;
14538 /** 
14539  * Shorthand for {@link Roo.util.JSON#decode}
14540  * @member Roo decode 
14541  * @method */
14542 Roo.decode = typeof(JSON) != 'undefined' && JSON.parse ? JSON.parse : Roo.util.JSON.decode;
14543 /*
14544  * Based on:
14545  * Ext JS Library 1.1.1
14546  * Copyright(c) 2006-2007, Ext JS, LLC.
14547  *
14548  * Originally Released Under LGPL - original licence link has changed is not relivant.
14549  *
14550  * Fork - LGPL
14551  * <script type="text/javascript">
14552  */
14553  
14554 /**
14555  * @class Roo.util.Format
14556  * Reusable data formatting functions
14557  * @static
14558  */
14559 Roo.util.Format = function(){
14560     var trimRe = /^\s+|\s+$/g;
14561     return {
14562         /**
14563          * Truncate a string and add an ellipsis ('...') to the end if it exceeds the specified length
14564          * @param {String} value The string to truncate
14565          * @param {Number} length The maximum length to allow before truncating
14566          * @return {String} The converted text
14567          */
14568         ellipsis : function(value, len){
14569             if(value && value.length > len){
14570                 return value.substr(0, len-3)+"...";
14571             }
14572             return value;
14573         },
14574
14575         /**
14576          * Checks a reference and converts it to empty string if it is undefined
14577          * @param {Mixed} value Reference to check
14578          * @return {Mixed} Empty string if converted, otherwise the original value
14579          */
14580         undef : function(value){
14581             return typeof value != "undefined" ? value : "";
14582         },
14583
14584         /**
14585          * Convert certain characters (&, <, >, and ') to their HTML character equivalents for literal display in web pages.
14586          * @param {String} value The string to encode
14587          * @return {String} The encoded text
14588          */
14589         htmlEncode : function(value){
14590             return !value ? value : String(value).replace(/&/g, "&amp;").replace(/>/g, "&gt;").replace(/</g, "&lt;").replace(/"/g, "&quot;");
14591         },
14592
14593         /**
14594          * Convert certain characters (&, <, >, and ') from their HTML character equivalents.
14595          * @param {String} value The string to decode
14596          * @return {String} The decoded text
14597          */
14598         htmlDecode : function(value){
14599             return !value ? value : String(value).replace(/&amp;/g, "&").replace(/&gt;/g, ">").replace(/&lt;/g, "<").replace(/&quot;/g, '"');
14600         },
14601
14602         /**
14603          * Trims any whitespace from either side of a string
14604          * @param {String} value The text to trim
14605          * @return {String} The trimmed text
14606          */
14607         trim : function(value){
14608             return String(value).replace(trimRe, "");
14609         },
14610
14611         /**
14612          * Returns a substring from within an original string
14613          * @param {String} value The original text
14614          * @param {Number} start The start index of the substring
14615          * @param {Number} length The length of the substring
14616          * @return {String} The substring
14617          */
14618         substr : function(value, start, length){
14619             return String(value).substr(start, length);
14620         },
14621
14622         /**
14623          * Converts a string to all lower case letters
14624          * @param {String} value The text to convert
14625          * @return {String} The converted text
14626          */
14627         lowercase : function(value){
14628             return String(value).toLowerCase();
14629         },
14630
14631         /**
14632          * Converts a string to all upper case letters
14633          * @param {String} value The text to convert
14634          * @return {String} The converted text
14635          */
14636         uppercase : function(value){
14637             return String(value).toUpperCase();
14638         },
14639
14640         /**
14641          * Converts the first character only of a string to upper case
14642          * @param {String} value The text to convert
14643          * @return {String} The converted text
14644          */
14645         capitalize : function(value){
14646             return !value ? value : value.charAt(0).toUpperCase() + value.substr(1).toLowerCase();
14647         },
14648
14649         // private
14650         call : function(value, fn){
14651             if(arguments.length > 2){
14652                 var args = Array.prototype.slice.call(arguments, 2);
14653                 args.unshift(value);
14654                  
14655                 return /** eval:var:value */  eval(fn).apply(window, args);
14656             }else{
14657                 /** eval:var:value */
14658                 return /** eval:var:value */ eval(fn).call(window, value);
14659             }
14660         },
14661
14662        
14663         /**
14664          * safer version of Math.toFixed..??/
14665          * @param {Number/String} value The numeric value to format
14666          * @param {Number/String} value Decimal places 
14667          * @return {String} The formatted currency string
14668          */
14669         toFixed : function(v, n)
14670         {
14671             // why not use to fixed - precision is buggered???
14672             if (!n) {
14673                 return Math.round(v-0);
14674             }
14675             var fact = Math.pow(10,n+1);
14676             v = (Math.round((v-0)*fact))/fact;
14677             var z = (''+fact).substring(2);
14678             if (v == Math.floor(v)) {
14679                 return Math.floor(v) + '.' + z;
14680             }
14681             
14682             // now just padd decimals..
14683             var ps = String(v).split('.');
14684             var fd = (ps[1] + z);
14685             var r = fd.substring(0,n); 
14686             var rm = fd.substring(n); 
14687             if (rm < 5) {
14688                 return ps[0] + '.' + r;
14689             }
14690             r*=1; // turn it into a number;
14691             r++;
14692             if (String(r).length != n) {
14693                 ps[0]*=1;
14694                 ps[0]++;
14695                 r = String(r).substring(1); // chop the end off.
14696             }
14697             
14698             return ps[0] + '.' + r;
14699              
14700         },
14701         
14702         /**
14703          * Format a number as US currency
14704          * @param {Number/String} value The numeric value to format
14705          * @return {String} The formatted currency string
14706          */
14707         usMoney : function(v){
14708             return '$' + Roo.util.Format.number(v);
14709         },
14710         
14711         /**
14712          * Format a number
14713          * eventually this should probably emulate php's number_format
14714          * @param {Number/String} value The numeric value to format
14715          * @param {Number} decimals number of decimal places
14716          * @param {String} delimiter for thousands (default comma)
14717          * @return {String} The formatted currency string
14718          */
14719         number : function(v, decimals, thousandsDelimiter)
14720         {
14721             // multiply and round.
14722             decimals = typeof(decimals) == 'undefined' ? 2 : decimals;
14723             thousandsDelimiter = typeof(thousandsDelimiter) == 'undefined' ? ',' : thousandsDelimiter;
14724             
14725             var mul = Math.pow(10, decimals);
14726             var zero = String(mul).substring(1);
14727             v = (Math.round((v-0)*mul))/mul;
14728             
14729             // if it's '0' number.. then
14730             
14731             //v = (v == Math.floor(v)) ? v + "." + zero : ((v*10 == Math.floor(v*10)) ? v + "0" : v);
14732             v = String(v);
14733             var ps = v.split('.');
14734             var whole = ps[0];
14735             
14736             var r = /(\d+)(\d{3})/;
14737             // add comma's
14738             
14739             if(thousandsDelimiter.length != 0) {
14740                 whole = whole.replace(/\B(?=(\d{3})+(?!\d))/g, thousandsDelimiter );
14741             } 
14742             
14743             var sub = ps[1] ?
14744                     // has decimals..
14745                     (decimals ?  ('.'+ ps[1] + zero.substring(ps[1].length)) : '') :
14746                     // does not have decimals
14747                     (decimals ? ('.' + zero) : '');
14748             
14749             
14750             return whole + sub ;
14751         },
14752         
14753         /**
14754          * Parse a value into a formatted date using the specified format pattern.
14755          * @param {Mixed} value The value to format
14756          * @param {String} format (optional) Any valid date format string (defaults to 'm/d/Y')
14757          * @return {String} The formatted date string
14758          */
14759         date : function(v, format){
14760             if(!v){
14761                 return "";
14762             }
14763             if(!(v instanceof Date)){
14764                 v = new Date(Date.parse(v));
14765             }
14766             return v.dateFormat(format || Roo.util.Format.defaults.date);
14767         },
14768
14769         /**
14770          * Returns a date rendering function that can be reused to apply a date format multiple times efficiently
14771          * @param {String} format Any valid date format string
14772          * @return {Function} The date formatting function
14773          */
14774         dateRenderer : function(format){
14775             return function(v){
14776                 return Roo.util.Format.date(v, format);  
14777             };
14778         },
14779
14780         // private
14781         stripTagsRE : /<\/?[^>]+>/gi,
14782         
14783         /**
14784          * Strips all HTML tags
14785          * @param {Mixed} value The text from which to strip tags
14786          * @return {String} The stripped text
14787          */
14788         stripTags : function(v){
14789             return !v ? v : String(v).replace(this.stripTagsRE, "");
14790         },
14791         
14792         /**
14793          * Size in Mb,Gb etc.
14794          * @param {Number} value The number to be formated
14795          * @param {number} decimals how many decimal places
14796          * @return {String} the formated string
14797          */
14798         size : function(value, decimals)
14799         {
14800             var sizes = ['b', 'k', 'M', 'G', 'T'];
14801             if (value == 0) {
14802                 return 0;
14803             }
14804             var i = parseInt(Math.floor(Math.log(value) / Math.log(1024)));
14805             return Roo.util.Format.number(value/ Math.pow(1024, i) ,decimals)   + sizes[i];
14806         }
14807         
14808         
14809         
14810     };
14811 }();
14812 Roo.util.Format.defaults = {
14813     date : 'd/M/Y'
14814 };/*
14815  * Based on:
14816  * Ext JS Library 1.1.1
14817  * Copyright(c) 2006-2007, Ext JS, LLC.
14818  *
14819  * Originally Released Under LGPL - original licence link has changed is not relivant.
14820  *
14821  * Fork - LGPL
14822  * <script type="text/javascript">
14823  */
14824
14825
14826  
14827
14828 /**
14829  * @class Roo.MasterTemplate
14830  * @extends Roo.Template
14831  * Provides a template that can have child templates. The syntax is:
14832 <pre><code>
14833 var t = new Roo.MasterTemplate(
14834         '&lt;select name="{name}"&gt;',
14835                 '&lt;tpl name="options"&gt;&lt;option value="{value:trim}"&gt;{text:ellipsis(10)}&lt;/option&gt;&lt;/tpl&gt;',
14836         '&lt;/select&gt;'
14837 );
14838 t.add('options', {value: 'foo', text: 'bar'});
14839 // or you can add multiple child elements in one shot
14840 t.addAll('options', [
14841     {value: 'foo', text: 'bar'},
14842     {value: 'foo2', text: 'bar2'},
14843     {value: 'foo3', text: 'bar3'}
14844 ]);
14845 // then append, applying the master template values
14846 t.append('my-form', {name: 'my-select'});
14847 </code></pre>
14848 * A name attribute for the child template is not required if you have only one child
14849 * template or you want to refer to them by index.
14850  */
14851 Roo.MasterTemplate = function(){
14852     Roo.MasterTemplate.superclass.constructor.apply(this, arguments);
14853     this.originalHtml = this.html;
14854     var st = {};
14855     var m, re = this.subTemplateRe;
14856     re.lastIndex = 0;
14857     var subIndex = 0;
14858     while(m = re.exec(this.html)){
14859         var name = m[1], content = m[2];
14860         st[subIndex] = {
14861             name: name,
14862             index: subIndex,
14863             buffer: [],
14864             tpl : new Roo.Template(content)
14865         };
14866         if(name){
14867             st[name] = st[subIndex];
14868         }
14869         st[subIndex].tpl.compile();
14870         st[subIndex].tpl.call = this.call.createDelegate(this);
14871         subIndex++;
14872     }
14873     this.subCount = subIndex;
14874     this.subs = st;
14875 };
14876 Roo.extend(Roo.MasterTemplate, Roo.Template, {
14877     /**
14878     * The regular expression used to match sub templates
14879     * @type RegExp
14880     * @property
14881     */
14882     subTemplateRe : /<tpl(?:\sname="([\w-]+)")?>((?:.|\n)*?)<\/tpl>/gi,
14883
14884     /**
14885      * Applies the passed values to a child template.
14886      * @param {String/Number} name (optional) The name or index of the child template
14887      * @param {Array/Object} values The values to be applied to the template
14888      * @return {MasterTemplate} this
14889      */
14890      add : function(name, values){
14891         if(arguments.length == 1){
14892             values = arguments[0];
14893             name = 0;
14894         }
14895         var s = this.subs[name];
14896         s.buffer[s.buffer.length] = s.tpl.apply(values);
14897         return this;
14898     },
14899
14900     /**
14901      * Applies all the passed values to a child template.
14902      * @param {String/Number} name (optional) The name or index of the child template
14903      * @param {Array} values The values to be applied to the template, this should be an array of objects.
14904      * @param {Boolean} reset (optional) True to reset the template first
14905      * @return {MasterTemplate} this
14906      */
14907     fill : function(name, values, reset){
14908         var a = arguments;
14909         if(a.length == 1 || (a.length == 2 && typeof a[1] == "boolean")){
14910             values = a[0];
14911             name = 0;
14912             reset = a[1];
14913         }
14914         if(reset){
14915             this.reset();
14916         }
14917         for(var i = 0, len = values.length; i < len; i++){
14918             this.add(name, values[i]);
14919         }
14920         return this;
14921     },
14922
14923     /**
14924      * Resets the template for reuse
14925      * @return {MasterTemplate} this
14926      */
14927      reset : function(){
14928         var s = this.subs;
14929         for(var i = 0; i < this.subCount; i++){
14930             s[i].buffer = [];
14931         }
14932         return this;
14933     },
14934
14935     applyTemplate : function(values){
14936         var s = this.subs;
14937         var replaceIndex = -1;
14938         this.html = this.originalHtml.replace(this.subTemplateRe, function(m, name){
14939             return s[++replaceIndex].buffer.join("");
14940         });
14941         return Roo.MasterTemplate.superclass.applyTemplate.call(this, values);
14942     },
14943
14944     apply : function(){
14945         return this.applyTemplate.apply(this, arguments);
14946     },
14947
14948     compile : function(){return this;}
14949 });
14950
14951 /**
14952  * Alias for fill().
14953  * @method
14954  */
14955 Roo.MasterTemplate.prototype.addAll = Roo.MasterTemplate.prototype.fill;
14956  /**
14957  * Creates a template from the passed element's value (display:none textarea, preferred) or innerHTML. e.g.
14958  * var tpl = Roo.MasterTemplate.from('element-id');
14959  * @param {String/HTMLElement} el
14960  * @param {Object} config
14961  * @static
14962  */
14963 Roo.MasterTemplate.from = function(el, config){
14964     el = Roo.getDom(el);
14965     return new Roo.MasterTemplate(el.value || el.innerHTML, config || '');
14966 };/*
14967  * Based on:
14968  * Ext JS Library 1.1.1
14969  * Copyright(c) 2006-2007, Ext JS, LLC.
14970  *
14971  * Originally Released Under LGPL - original licence link has changed is not relivant.
14972  *
14973  * Fork - LGPL
14974  * <script type="text/javascript">
14975  */
14976
14977  
14978 /**
14979  * @class Roo.util.CSS
14980  * Utility class for manipulating CSS rules
14981  * @static
14982
14983  */
14984 Roo.util.CSS = function(){
14985         var rules = null;
14986         var doc = document;
14987
14988     var camelRe = /(-[a-z])/gi;
14989     var camelFn = function(m, a){ return a.charAt(1).toUpperCase(); };
14990
14991    return {
14992    /**
14993     * Very simple dynamic creation of stylesheets from a text blob of rules.  The text will wrapped in a style
14994     * tag and appended to the HEAD of the document.
14995     * @param {String|Object} cssText The text containing the css rules
14996     * @param {String} id An id to add to the stylesheet for later removal
14997     * @return {StyleSheet}
14998     */
14999     createStyleSheet : function(cssText, id){
15000         var ss;
15001         var head = doc.getElementsByTagName("head")[0];
15002         var nrules = doc.createElement("style");
15003         nrules.setAttribute("type", "text/css");
15004         if(id){
15005             nrules.setAttribute("id", id);
15006         }
15007         if (typeof(cssText) != 'string') {
15008             // support object maps..
15009             // not sure if this a good idea.. 
15010             // perhaps it should be merged with the general css handling
15011             // and handle js style props.
15012             var cssTextNew = [];
15013             for(var n in cssText) {
15014                 var citems = [];
15015                 for(var k in cssText[n]) {
15016                     citems.push( k + ' : ' +cssText[n][k] + ';' );
15017                 }
15018                 cssTextNew.push( n + ' { ' + citems.join(' ') + '} ');
15019                 
15020             }
15021             cssText = cssTextNew.join("\n");
15022             
15023         }
15024        
15025        
15026        if(Roo.isIE){
15027            head.appendChild(nrules);
15028            ss = nrules.styleSheet;
15029            ss.cssText = cssText;
15030        }else{
15031            try{
15032                 nrules.appendChild(doc.createTextNode(cssText));
15033            }catch(e){
15034                nrules.cssText = cssText; 
15035            }
15036            head.appendChild(nrules);
15037            ss = nrules.styleSheet ? nrules.styleSheet : (nrules.sheet || doc.styleSheets[doc.styleSheets.length-1]);
15038        }
15039        this.cacheStyleSheet(ss);
15040        return ss;
15041    },
15042
15043    /**
15044     * Removes a style or link tag by id
15045     * @param {String} id The id of the tag
15046     */
15047    removeStyleSheet : function(id){
15048        var existing = doc.getElementById(id);
15049        if(existing){
15050            existing.parentNode.removeChild(existing);
15051        }
15052    },
15053
15054    /**
15055     * Dynamically swaps an existing stylesheet reference for a new one
15056     * @param {String} id The id of an existing link tag to remove
15057     * @param {String} url The href of the new stylesheet to include
15058     */
15059    swapStyleSheet : function(id, url){
15060        this.removeStyleSheet(id);
15061        var ss = doc.createElement("link");
15062        ss.setAttribute("rel", "stylesheet");
15063        ss.setAttribute("type", "text/css");
15064        ss.setAttribute("id", id);
15065        ss.setAttribute("href", url);
15066        doc.getElementsByTagName("head")[0].appendChild(ss);
15067    },
15068    
15069    /**
15070     * Refresh the rule cache if you have dynamically added stylesheets
15071     * @return {Object} An object (hash) of rules indexed by selector
15072     */
15073    refreshCache : function(){
15074        return this.getRules(true);
15075    },
15076
15077    // private
15078    cacheStyleSheet : function(stylesheet){
15079        if(!rules){
15080            rules = {};
15081        }
15082        try{// try catch for cross domain access issue
15083            var ssRules = stylesheet.cssRules || stylesheet.rules;
15084            for(var j = ssRules.length-1; j >= 0; --j){
15085                rules[ssRules[j].selectorText] = ssRules[j];
15086            }
15087        }catch(e){}
15088    },
15089    
15090    /**
15091     * Gets all css rules for the document
15092     * @param {Boolean} refreshCache true to refresh the internal cache
15093     * @return {Object} An object (hash) of rules indexed by selector
15094     */
15095    getRules : function(refreshCache){
15096                 if(rules == null || refreshCache){
15097                         rules = {};
15098                         var ds = doc.styleSheets;
15099                         for(var i =0, len = ds.length; i < len; i++){
15100                             try{
15101                         this.cacheStyleSheet(ds[i]);
15102                     }catch(e){} 
15103                 }
15104                 }
15105                 return rules;
15106         },
15107         
15108         /**
15109     * Gets an an individual CSS rule by selector(s)
15110     * @param {String/Array} selector The CSS selector or an array of selectors to try. The first selector that is found is returned.
15111     * @param {Boolean} refreshCache true to refresh the internal cache if you have recently updated any rules or added styles dynamically
15112     * @return {CSSRule} The CSS rule or null if one is not found
15113     */
15114    getRule : function(selector, refreshCache){
15115                 var rs = this.getRules(refreshCache);
15116                 if(!(selector instanceof Array)){
15117                     return rs[selector];
15118                 }
15119                 for(var i = 0; i < selector.length; i++){
15120                         if(rs[selector[i]]){
15121                                 return rs[selector[i]];
15122                         }
15123                 }
15124                 return null;
15125         },
15126         
15127         
15128         /**
15129     * Updates a rule property
15130     * @param {String/Array} selector If it's an array it tries each selector until it finds one. Stops immediately once one is found.
15131     * @param {String} property The css property
15132     * @param {String} value The new value for the property
15133     * @return {Boolean} true If a rule was found and updated
15134     */
15135    updateRule : function(selector, property, value){
15136                 if(!(selector instanceof Array)){
15137                         var rule = this.getRule(selector);
15138                         if(rule){
15139                                 rule.style[property.replace(camelRe, camelFn)] = value;
15140                                 return true;
15141                         }
15142                 }else{
15143                         for(var i = 0; i < selector.length; i++){
15144                                 if(this.updateRule(selector[i], property, value)){
15145                                         return true;
15146                                 }
15147                         }
15148                 }
15149                 return false;
15150         }
15151    };   
15152 }();/*
15153  * Based on:
15154  * Ext JS Library 1.1.1
15155  * Copyright(c) 2006-2007, Ext JS, LLC.
15156  *
15157  * Originally Released Under LGPL - original licence link has changed is not relivant.
15158  *
15159  * Fork - LGPL
15160  * <script type="text/javascript">
15161  */
15162
15163  
15164
15165 /**
15166  * @class Roo.util.ClickRepeater
15167  * @extends Roo.util.Observable
15168  * 
15169  * A wrapper class which can be applied to any element. Fires a "click" event while the
15170  * mouse is pressed. The interval between firings may be specified in the config but
15171  * defaults to 10 milliseconds.
15172  * 
15173  * Optionally, a CSS class may be applied to the element during the time it is pressed.
15174  * 
15175  * @cfg {String/HTMLElement/Element} el The element to act as a button.
15176  * @cfg {Number} delay The initial delay before the repeating event begins firing.
15177  * Similar to an autorepeat key delay.
15178  * @cfg {Number} interval The interval between firings of the "click" event. Default 10 ms.
15179  * @cfg {String} pressClass A CSS class name to be applied to the element while pressed.
15180  * @cfg {Boolean} accelerate True if autorepeating should start slowly and accelerate.
15181  *           "interval" and "delay" are ignored. "immediate" is honored.
15182  * @cfg {Boolean} preventDefault True to prevent the default click event
15183  * @cfg {Boolean} stopDefault True to stop the default click event
15184  * 
15185  * @history
15186  *     2007-02-02 jvs Original code contributed by Nige "Animal" White
15187  *     2007-02-02 jvs Renamed to ClickRepeater
15188  *   2007-02-03 jvs Modifications for FF Mac and Safari 
15189  *
15190  *  @constructor
15191  * @param {String/HTMLElement/Element} el The element to listen on
15192  * @param {Object} config
15193  **/
15194 Roo.util.ClickRepeater = function(el, config)
15195 {
15196     this.el = Roo.get(el);
15197     this.el.unselectable();
15198
15199     Roo.apply(this, config);
15200
15201     this.addEvents({
15202     /**
15203      * @event mousedown
15204      * Fires when the mouse button is depressed.
15205      * @param {Roo.util.ClickRepeater} this
15206      */
15207         "mousedown" : true,
15208     /**
15209      * @event click
15210      * Fires on a specified interval during the time the element is pressed.
15211      * @param {Roo.util.ClickRepeater} this
15212      */
15213         "click" : true,
15214     /**
15215      * @event mouseup
15216      * Fires when the mouse key is released.
15217      * @param {Roo.util.ClickRepeater} this
15218      */
15219         "mouseup" : true
15220     });
15221
15222     this.el.on("mousedown", this.handleMouseDown, this);
15223     if(this.preventDefault || this.stopDefault){
15224         this.el.on("click", function(e){
15225             if(this.preventDefault){
15226                 e.preventDefault();
15227             }
15228             if(this.stopDefault){
15229                 e.stopEvent();
15230             }
15231         }, this);
15232     }
15233
15234     // allow inline handler
15235     if(this.handler){
15236         this.on("click", this.handler,  this.scope || this);
15237     }
15238
15239     Roo.util.ClickRepeater.superclass.constructor.call(this);
15240 };
15241
15242 Roo.extend(Roo.util.ClickRepeater, Roo.util.Observable, {
15243     interval : 20,
15244     delay: 250,
15245     preventDefault : true,
15246     stopDefault : false,
15247     timer : 0,
15248
15249     // private
15250     handleMouseDown : function(){
15251         clearTimeout(this.timer);
15252         this.el.blur();
15253         if(this.pressClass){
15254             this.el.addClass(this.pressClass);
15255         }
15256         this.mousedownTime = new Date();
15257
15258         Roo.get(document).on("mouseup", this.handleMouseUp, this);
15259         this.el.on("mouseout", this.handleMouseOut, this);
15260
15261         this.fireEvent("mousedown", this);
15262         this.fireEvent("click", this);
15263         
15264         this.timer = this.click.defer(this.delay || this.interval, this);
15265     },
15266
15267     // private
15268     click : function(){
15269         this.fireEvent("click", this);
15270         this.timer = this.click.defer(this.getInterval(), this);
15271     },
15272
15273     // private
15274     getInterval: function(){
15275         if(!this.accelerate){
15276             return this.interval;
15277         }
15278         var pressTime = this.mousedownTime.getElapsed();
15279         if(pressTime < 500){
15280             return 400;
15281         }else if(pressTime < 1700){
15282             return 320;
15283         }else if(pressTime < 2600){
15284             return 250;
15285         }else if(pressTime < 3500){
15286             return 180;
15287         }else if(pressTime < 4400){
15288             return 140;
15289         }else if(pressTime < 5300){
15290             return 80;
15291         }else if(pressTime < 6200){
15292             return 50;
15293         }else{
15294             return 10;
15295         }
15296     },
15297
15298     // private
15299     handleMouseOut : function(){
15300         clearTimeout(this.timer);
15301         if(this.pressClass){
15302             this.el.removeClass(this.pressClass);
15303         }
15304         this.el.on("mouseover", this.handleMouseReturn, this);
15305     },
15306
15307     // private
15308     handleMouseReturn : function(){
15309         this.el.un("mouseover", this.handleMouseReturn);
15310         if(this.pressClass){
15311             this.el.addClass(this.pressClass);
15312         }
15313         this.click();
15314     },
15315
15316     // private
15317     handleMouseUp : function(){
15318         clearTimeout(this.timer);
15319         this.el.un("mouseover", this.handleMouseReturn);
15320         this.el.un("mouseout", this.handleMouseOut);
15321         Roo.get(document).un("mouseup", this.handleMouseUp);
15322         this.el.removeClass(this.pressClass);
15323         this.fireEvent("mouseup", this);
15324     }
15325 });/**
15326  * @class Roo.util.Clipboard
15327  * @static
15328  * 
15329  * Clipboard UTILS
15330  * 
15331  **/
15332 Roo.util.Clipboard = {
15333     /**
15334      * Writes a string to the clipboard - using the Clipboard API if https, otherwise using text area.
15335      * @param {String} text to copy to clipboard
15336      */
15337     write : function(text) {
15338         // navigator clipboard api needs a secure context (https)
15339         if (navigator.clipboard && window.isSecureContext) {
15340             // navigator clipboard api method'
15341             navigator.clipboard.writeText(text);
15342             return ;
15343         } 
15344         // text area method
15345         var ta = document.createElement("textarea");
15346         ta.value = text;
15347         // make the textarea out of viewport
15348         ta.style.position = "fixed";
15349         ta.style.left = "-999999px";
15350         ta.style.top = "-999999px";
15351         document.body.appendChild(ta);
15352         ta.focus();
15353         ta.select();
15354         document.execCommand('copy');
15355         (function() {
15356             ta.remove();
15357         }).defer(100);
15358         
15359     }
15360         
15361 }
15362     /*
15363  * Based on:
15364  * Ext JS Library 1.1.1
15365  * Copyright(c) 2006-2007, Ext JS, LLC.
15366  *
15367  * Originally Released Under LGPL - original licence link has changed is not relivant.
15368  *
15369  * Fork - LGPL
15370  * <script type="text/javascript">
15371  */
15372
15373  
15374 /**
15375  * @class Roo.KeyNav
15376  * <p>Provides a convenient wrapper for normalized keyboard navigation.  KeyNav allows you to bind
15377  * navigation keys to function calls that will get called when the keys are pressed, providing an easy
15378  * way to implement custom navigation schemes for any UI component.</p>
15379  * <p>The following are all of the possible keys that can be implemented: enter, left, right, up, down, tab, esc,
15380  * pageUp, pageDown, del, home, end.  Usage:</p>
15381  <pre><code>
15382 var nav = new Roo.KeyNav("my-element", {
15383     "left" : function(e){
15384         this.moveLeft(e.ctrlKey);
15385     },
15386     "right" : function(e){
15387         this.moveRight(e.ctrlKey);
15388     },
15389     "enter" : function(e){
15390         this.save();
15391     },
15392     scope : this
15393 });
15394 </code></pre>
15395  * @constructor
15396  * @param {String/HTMLElement/Roo.Element} el The element to bind to
15397  * @param {Object} config The config
15398  */
15399 Roo.KeyNav = function(el, config){
15400     this.el = Roo.get(el);
15401     Roo.apply(this, config);
15402     if(!this.disabled){
15403         this.disabled = true;
15404         this.enable();
15405     }
15406 };
15407
15408 Roo.KeyNav.prototype = {
15409     /**
15410      * @cfg {Boolean} disabled
15411      * True to disable this KeyNav instance (defaults to false)
15412      */
15413     disabled : false,
15414     /**
15415      * @cfg {String} defaultEventAction
15416      * The method to call on the {@link Roo.EventObject} after this KeyNav intercepts a key.  Valid values are
15417      * {@link Roo.EventObject#stopEvent}, {@link Roo.EventObject#preventDefault} and
15418      * {@link Roo.EventObject#stopPropagation} (defaults to 'stopEvent')
15419      */
15420     defaultEventAction: "stopEvent",
15421     /**
15422      * @cfg {Boolean} forceKeyDown
15423      * Handle the keydown event instead of keypress (defaults to false).  KeyNav automatically does this for IE since
15424      * IE does not propagate special keys on keypress, but setting this to true will force other browsers to also
15425      * handle keydown instead of keypress.
15426      */
15427     forceKeyDown : false,
15428
15429     // private
15430     prepareEvent : function(e){
15431         var k = e.getKey();
15432         var h = this.keyToHandler[k];
15433         //if(h && this[h]){
15434         //    e.stopPropagation();
15435         //}
15436         if(Roo.isSafari && h && k >= 37 && k <= 40){
15437             e.stopEvent();
15438         }
15439     },
15440
15441     // private
15442     relay : function(e){
15443         var k = e.getKey();
15444         var h = this.keyToHandler[k];
15445         if(h && this[h]){
15446             if(this.doRelay(e, this[h], h) !== true){
15447                 e[this.defaultEventAction]();
15448             }
15449         }
15450     },
15451
15452     // private
15453     doRelay : function(e, h, hname){
15454         return h.call(this.scope || this, e);
15455     },
15456
15457     // possible handlers
15458     enter : false,
15459     left : false,
15460     right : false,
15461     up : false,
15462     down : false,
15463     tab : false,
15464     esc : false,
15465     pageUp : false,
15466     pageDown : false,
15467     del : false,
15468     home : false,
15469     end : false,
15470
15471     // quick lookup hash
15472     keyToHandler : {
15473         37 : "left",
15474         39 : "right",
15475         38 : "up",
15476         40 : "down",
15477         33 : "pageUp",
15478         34 : "pageDown",
15479         46 : "del",
15480         36 : "home",
15481         35 : "end",
15482         13 : "enter",
15483         27 : "esc",
15484         9  : "tab"
15485     },
15486
15487         /**
15488          * Enable this KeyNav
15489          */
15490         enable: function(){
15491                 if(this.disabled){
15492             // ie won't do special keys on keypress, no one else will repeat keys with keydown
15493             // the EventObject will normalize Safari automatically
15494             if(this.forceKeyDown || Roo.isIE || Roo.isAir){
15495                 this.el.on("keydown", this.relay,  this);
15496             }else{
15497                 this.el.on("keydown", this.prepareEvent,  this);
15498                 this.el.on("keypress", this.relay,  this);
15499             }
15500                     this.disabled = false;
15501                 }
15502         },
15503
15504         /**
15505          * Disable this KeyNav
15506          */
15507         disable: function(){
15508                 if(!this.disabled){
15509                     if(this.forceKeyDown || Roo.isIE || Roo.isAir){
15510                 this.el.un("keydown", this.relay);
15511             }else{
15512                 this.el.un("keydown", this.prepareEvent);
15513                 this.el.un("keypress", this.relay);
15514             }
15515                     this.disabled = true;
15516                 }
15517         }
15518 };/*
15519  * Based on:
15520  * Ext JS Library 1.1.1
15521  * Copyright(c) 2006-2007, Ext JS, LLC.
15522  *
15523  * Originally Released Under LGPL - original licence link has changed is not relivant.
15524  *
15525  * Fork - LGPL
15526  * <script type="text/javascript">
15527  */
15528
15529  
15530 /**
15531  * @class Roo.KeyMap
15532  * Handles mapping keys to actions for an element. One key map can be used for multiple actions.
15533  * The constructor accepts the same config object as defined by {@link #addBinding}.
15534  * If you bind a callback function to a KeyMap, anytime the KeyMap handles an expected key
15535  * combination it will call the function with this signature (if the match is a multi-key
15536  * combination the callback will still be called only once): (String key, Roo.EventObject e)
15537  * A KeyMap can also handle a string representation of keys.<br />
15538  * Usage:
15539  <pre><code>
15540 // map one key by key code
15541 var map = new Roo.KeyMap("my-element", {
15542     key: 13, // or Roo.EventObject.ENTER
15543     fn: myHandler,
15544     scope: myObject
15545 });
15546
15547 // map multiple keys to one action by string
15548 var map = new Roo.KeyMap("my-element", {
15549     key: "a\r\n\t",
15550     fn: myHandler,
15551     scope: myObject
15552 });
15553
15554 // map multiple keys to multiple actions by strings and array of codes
15555 var map = new Roo.KeyMap("my-element", [
15556     {
15557         key: [10,13],
15558         fn: function(){ alert("Return was pressed"); }
15559     }, {
15560         key: "abc",
15561         fn: function(){ alert('a, b or c was pressed'); }
15562     }, {
15563         key: "\t",
15564         ctrl:true,
15565         shift:true,
15566         fn: function(){ alert('Control + shift + tab was pressed.'); }
15567     }
15568 ]);
15569 </code></pre>
15570  * <b>Note: A KeyMap starts enabled</b>
15571  * @constructor
15572  * @param {String/HTMLElement/Roo.Element} el The element to bind to
15573  * @param {Object} config The config (see {@link #addBinding})
15574  * @param {String} eventName (optional) The event to bind to (defaults to "keydown")
15575  */
15576 Roo.KeyMap = function(el, config, eventName){
15577     this.el  = Roo.get(el);
15578     this.eventName = eventName || "keydown";
15579     this.bindings = [];
15580     if(config){
15581         this.addBinding(config);
15582     }
15583     this.enable();
15584 };
15585
15586 Roo.KeyMap.prototype = {
15587     /**
15588      * True to stop the event from bubbling and prevent the default browser action if the
15589      * key was handled by the KeyMap (defaults to false)
15590      * @type Boolean
15591      */
15592     stopEvent : false,
15593
15594     /**
15595      * Add a new binding to this KeyMap. The following config object properties are supported:
15596      * <pre>
15597 Property    Type             Description
15598 ----------  ---------------  ----------------------------------------------------------------------
15599 key         String/Array     A single keycode or an array of keycodes to handle
15600 shift       Boolean          True to handle key only when shift is pressed (defaults to false)
15601 ctrl        Boolean          True to handle key only when ctrl is pressed (defaults to false)
15602 alt         Boolean          True to handle key only when alt is pressed (defaults to false)
15603 fn          Function         The function to call when KeyMap finds the expected key combination
15604 scope       Object           The scope of the callback function
15605 </pre>
15606      *
15607      * Usage:
15608      * <pre><code>
15609 // Create a KeyMap
15610 var map = new Roo.KeyMap(document, {
15611     key: Roo.EventObject.ENTER,
15612     fn: handleKey,
15613     scope: this
15614 });
15615
15616 //Add a new binding to the existing KeyMap later
15617 map.addBinding({
15618     key: 'abc',
15619     shift: true,
15620     fn: handleKey,
15621     scope: this
15622 });
15623 </code></pre>
15624      * @param {Object/Array} config A single KeyMap config or an array of configs
15625      */
15626         addBinding : function(config){
15627         if(config instanceof Array){
15628             for(var i = 0, len = config.length; i < len; i++){
15629                 this.addBinding(config[i]);
15630             }
15631             return;
15632         }
15633         var keyCode = config.key,
15634             shift = config.shift, 
15635             ctrl = config.ctrl, 
15636             alt = config.alt,
15637             fn = config.fn,
15638             scope = config.scope;
15639         if(typeof keyCode == "string"){
15640             var ks = [];
15641             var keyString = keyCode.toUpperCase();
15642             for(var j = 0, len = keyString.length; j < len; j++){
15643                 ks.push(keyString.charCodeAt(j));
15644             }
15645             keyCode = ks;
15646         }
15647         var keyArray = keyCode instanceof Array;
15648         var handler = function(e){
15649             if((!shift || e.shiftKey) && (!ctrl || e.ctrlKey) &&  (!alt || e.altKey)){
15650                 var k = e.getKey();
15651                 if(keyArray){
15652                     for(var i = 0, len = keyCode.length; i < len; i++){
15653                         if(keyCode[i] == k){
15654                           if(this.stopEvent){
15655                               e.stopEvent();
15656                           }
15657                           fn.call(scope || window, k, e);
15658                           return;
15659                         }
15660                     }
15661                 }else{
15662                     if(k == keyCode){
15663                         if(this.stopEvent){
15664                            e.stopEvent();
15665                         }
15666                         fn.call(scope || window, k, e);
15667                     }
15668                 }
15669             }
15670         };
15671         this.bindings.push(handler);  
15672         },
15673
15674     /**
15675      * Shorthand for adding a single key listener
15676      * @param {Number/Array/Object} key Either the numeric key code, array of key codes or an object with the
15677      * following options:
15678      * {key: (number or array), shift: (true/false), ctrl: (true/false), alt: (true/false)}
15679      * @param {Function} fn The function to call
15680      * @param {Object} scope (optional) The scope of the function
15681      */
15682     on : function(key, fn, scope){
15683         var keyCode, shift, ctrl, alt;
15684         if(typeof key == "object" && !(key instanceof Array)){
15685             keyCode = key.key;
15686             shift = key.shift;
15687             ctrl = key.ctrl;
15688             alt = key.alt;
15689         }else{
15690             keyCode = key;
15691         }
15692         this.addBinding({
15693             key: keyCode,
15694             shift: shift,
15695             ctrl: ctrl,
15696             alt: alt,
15697             fn: fn,
15698             scope: scope
15699         })
15700     },
15701
15702     // private
15703     handleKeyDown : function(e){
15704             if(this.enabled){ //just in case
15705             var b = this.bindings;
15706             for(var i = 0, len = b.length; i < len; i++){
15707                 b[i].call(this, e);
15708             }
15709             }
15710         },
15711         
15712         /**
15713          * Returns true if this KeyMap is enabled
15714          * @return {Boolean} 
15715          */
15716         isEnabled : function(){
15717             return this.enabled;  
15718         },
15719         
15720         /**
15721          * Enables this KeyMap
15722          */
15723         enable: function(){
15724                 if(!this.enabled){
15725                     this.el.on(this.eventName, this.handleKeyDown, this);
15726                     this.enabled = true;
15727                 }
15728         },
15729
15730         /**
15731          * Disable this KeyMap
15732          */
15733         disable: function(){
15734                 if(this.enabled){
15735                     this.el.removeListener(this.eventName, this.handleKeyDown, this);
15736                     this.enabled = false;
15737                 }
15738         }
15739 };/*
15740  * Based on:
15741  * Ext JS Library 1.1.1
15742  * Copyright(c) 2006-2007, Ext JS, LLC.
15743  *
15744  * Originally Released Under LGPL - original licence link has changed is not relivant.
15745  *
15746  * Fork - LGPL
15747  * <script type="text/javascript">
15748  */
15749
15750  
15751 /**
15752  * @class Roo.util.TextMetrics
15753  * Provides precise pixel measurements for blocks of text so that you can determine exactly how high and
15754  * wide, in pixels, a given block of text will be.
15755  * @static
15756  */
15757 Roo.util.TextMetrics = function(){
15758     var shared;
15759     return {
15760         /**
15761          * Measures the size of the specified text
15762          * @param {String/HTMLElement} el The element, dom node or id from which to copy existing CSS styles
15763          * that can affect the size of the rendered text
15764          * @param {String} text The text to measure
15765          * @param {Number} fixedWidth (optional) If the text will be multiline, you have to set a fixed width
15766          * in order to accurately measure the text height
15767          * @return {Object} An object containing the text's size {width: (width), height: (height)}
15768          */
15769         measure : function(el, text, fixedWidth){
15770             if(!shared){
15771                 shared = Roo.util.TextMetrics.Instance(el, fixedWidth);
15772             }
15773             shared.bind(el);
15774             shared.setFixedWidth(fixedWidth || 'auto');
15775             return shared.getSize(text);
15776         },
15777
15778         /**
15779          * Return a unique TextMetrics instance that can be bound directly to an element and reused.  This reduces
15780          * the overhead of multiple calls to initialize the style properties on each measurement.
15781          * @param {String/HTMLElement} el The element, dom node or id that the instance will be bound to
15782          * @param {Number} fixedWidth (optional) If the text will be multiline, you have to set a fixed width
15783          * in order to accurately measure the text height
15784          * @return {Roo.util.TextMetrics.Instance} instance The new instance
15785          */
15786         createInstance : function(el, fixedWidth){
15787             return Roo.util.TextMetrics.Instance(el, fixedWidth);
15788         }
15789     };
15790 }();
15791
15792 /**
15793  * @class Roo.util.TextMetrics.Instance
15794  * Instance of  TextMetrics Calcuation
15795  * @constructor
15796  * Create a new TextMetrics Instance
15797  * @param {Object} bindto
15798  * @param {Boolean} fixedWidth
15799  */
15800
15801 Roo.util.TextMetrics.Instance = function(bindTo, fixedWidth)
15802 {
15803     var ml = new Roo.Element(document.createElement('div'));
15804     document.body.appendChild(ml.dom);
15805     ml.position('absolute');
15806     ml.setLeftTop(-1000, -1000);
15807     ml.hide();
15808
15809     if(fixedWidth){
15810         ml.setWidth(fixedWidth);
15811     }
15812      
15813     var instance = {
15814         /**
15815          * Returns the size of the specified text based on the internal element's style and width properties
15816          * @param {String} text The text to measure
15817          * @return {Object} An object containing the text's size {width: (width), height: (height)}
15818          */
15819         getSize : function(text){
15820             ml.update(text);
15821             var s = ml.getSize();
15822             ml.update('');
15823             return s;
15824         },
15825
15826         /**
15827          * Binds this TextMetrics instance to an element from which to copy existing CSS styles
15828          * that can affect the size of the rendered text
15829          * @param {String/HTMLElement} el The element, dom node or id
15830          */
15831         bind : function(el){
15832             ml.setStyle(
15833                 Roo.fly(el).getStyles('font-size','font-style', 'font-weight', 'font-family','line-height')
15834             );
15835         },
15836
15837         /**
15838          * Sets a fixed width on the internal measurement element.  If the text will be multiline, you have
15839          * to set a fixed width in order to accurately measure the text height.
15840          * @param {Number} width The width to set on the element
15841          */
15842         setFixedWidth : function(width){
15843             ml.setWidth(width);
15844         },
15845
15846         /**
15847          * Returns the measured width of the specified text
15848          * @param {String} text The text to measure
15849          * @return {Number} width The width in pixels
15850          */
15851         getWidth : function(text){
15852             ml.dom.style.width = 'auto';
15853             return this.getSize(text).width;
15854         },
15855
15856         /**
15857          * Returns the measured height of the specified text.  For multiline text, be sure to call
15858          * {@link #setFixedWidth} if necessary.
15859          * @param {String} text The text to measure
15860          * @return {Number} height The height in pixels
15861          */
15862         getHeight : function(text){
15863             return this.getSize(text).height;
15864         }
15865     };
15866
15867     instance.bind(bindTo);
15868
15869     return instance;
15870 };
15871
15872 // backwards compat
15873 Roo.Element.measureText = Roo.util.TextMetrics.measure;/*
15874  * Based on:
15875  * Ext JS Library 1.1.1
15876  * Copyright(c) 2006-2007, Ext JS, LLC.
15877  *
15878  * Originally Released Under LGPL - original licence link has changed is not relivant.
15879  *
15880  * Fork - LGPL
15881  * <script type="text/javascript">
15882  */
15883
15884 /**
15885  * @class Roo.state.Provider
15886  * Abstract base class for state provider implementations. This class provides methods
15887  * for encoding and decoding <b>typed</b> variables including dates and defines the 
15888  * Provider interface.
15889  */
15890 Roo.state.Provider = function(){
15891     /**
15892      * @event statechange
15893      * Fires when a state change occurs.
15894      * @param {Provider} this This state provider
15895      * @param {String} key The state key which was changed
15896      * @param {String} value The encoded value for the state
15897      */
15898     this.addEvents({
15899         "statechange": true
15900     });
15901     this.state = {};
15902     Roo.state.Provider.superclass.constructor.call(this);
15903 };
15904 Roo.extend(Roo.state.Provider, Roo.util.Observable, {
15905     /**
15906      * Returns the current value for a key
15907      * @param {String} name The key name
15908      * @param {Mixed} defaultValue A default value to return if the key's value is not found
15909      * @return {Mixed} The state data
15910      */
15911     get : function(name, defaultValue){
15912         return typeof this.state[name] == "undefined" ?
15913             defaultValue : this.state[name];
15914     },
15915     
15916     /**
15917      * Clears a value from the state
15918      * @param {String} name The key name
15919      */
15920     clear : function(name){
15921         delete this.state[name];
15922         this.fireEvent("statechange", this, name, null);
15923     },
15924     
15925     /**
15926      * Sets the value for a key
15927      * @param {String} name The key name
15928      * @param {Mixed} value The value to set
15929      */
15930     set : function(name, value){
15931         this.state[name] = value;
15932         this.fireEvent("statechange", this, name, value);
15933     },
15934     
15935     /**
15936      * Decodes a string previously encoded with {@link #encodeValue}.
15937      * @param {String} value The value to decode
15938      * @return {Mixed} The decoded value
15939      */
15940     decodeValue : function(cookie){
15941         var re = /^(a|n|d|b|s|o)\:(.*)$/;
15942         var matches = re.exec(unescape(cookie));
15943         if(!matches || !matches[1]) {
15944             return; // non state cookie
15945         }
15946         var type = matches[1];
15947         var v = matches[2];
15948         switch(type){
15949             case "n":
15950                 return parseFloat(v);
15951             case "d":
15952                 return new Date(Date.parse(v));
15953             case "b":
15954                 return (v == "1");
15955             case "a":
15956                 var all = [];
15957                 var values = v.split("^");
15958                 for(var i = 0, len = values.length; i < len; i++){
15959                     all.push(this.decodeValue(values[i]));
15960                 }
15961                 return all;
15962            case "o":
15963                 var all = {};
15964                 var values = v.split("^");
15965                 for(var i = 0, len = values.length; i < len; i++){
15966                     var kv = values[i].split("=");
15967                     all[kv[0]] = this.decodeValue(kv[1]);
15968                 }
15969                 return all;
15970            default:
15971                 return v;
15972         }
15973     },
15974     
15975     /**
15976      * Encodes a value including type information.  Decode with {@link #decodeValue}.
15977      * @param {Mixed} value The value to encode
15978      * @return {String} The encoded value
15979      */
15980     encodeValue : function(v){
15981         var enc;
15982         if(typeof v == "number"){
15983             enc = "n:" + v;
15984         }else if(typeof v == "boolean"){
15985             enc = "b:" + (v ? "1" : "0");
15986         }else if(v instanceof Date){
15987             enc = "d:" + v.toGMTString();
15988         }else if(v instanceof Array){
15989             var flat = "";
15990             for(var i = 0, len = v.length; i < len; i++){
15991                 flat += this.encodeValue(v[i]);
15992                 if(i != len-1) {
15993                     flat += "^";
15994                 }
15995             }
15996             enc = "a:" + flat;
15997         }else if(typeof v == "object"){
15998             var flat = "";
15999             for(var key in v){
16000                 if(typeof v[key] != "function"){
16001                     flat += key + "=" + this.encodeValue(v[key]) + "^";
16002                 }
16003             }
16004             enc = "o:" + flat.substring(0, flat.length-1);
16005         }else{
16006             enc = "s:" + v;
16007         }
16008         return escape(enc);        
16009     }
16010 });
16011
16012 /*
16013  * Based on:
16014  * Ext JS Library 1.1.1
16015  * Copyright(c) 2006-2007, Ext JS, LLC.
16016  *
16017  * Originally Released Under LGPL - original licence link has changed is not relivant.
16018  *
16019  * Fork - LGPL
16020  * <script type="text/javascript">
16021  */
16022 /**
16023  * @class Roo.state.Manager
16024  * This is the global state manager. By default all components that are "state aware" check this class
16025  * for state information if you don't pass them a custom state provider. In order for this class
16026  * to be useful, it must be initialized with a provider when your application initializes.
16027  <pre><code>
16028 // in your initialization function
16029 init : function(){
16030    Roo.state.Manager.setProvider(new Roo.state.CookieProvider());
16031    ...
16032    // supposed you have a {@link Roo.BorderLayout}
16033    var layout = new Roo.BorderLayout(...);
16034    layout.restoreState();
16035    // or a {Roo.BasicDialog}
16036    var dialog = new Roo.BasicDialog(...);
16037    dialog.restoreState();
16038  </code></pre>
16039  * @static
16040  */
16041 Roo.state.Manager = function(){
16042     var provider = new Roo.state.Provider();
16043     
16044     return {
16045         /**
16046          * Configures the default state provider for your application
16047          * @param {Provider} stateProvider The state provider to set
16048          */
16049         setProvider : function(stateProvider){
16050             provider = stateProvider;
16051         },
16052         
16053         /**
16054          * Returns the current value for a key
16055          * @param {String} name The key name
16056          * @param {Mixed} defaultValue The default value to return if the key lookup does not match
16057          * @return {Mixed} The state data
16058          */
16059         get : function(key, defaultValue){
16060             return provider.get(key, defaultValue);
16061         },
16062         
16063         /**
16064          * Sets the value for a key
16065          * @param {String} name The key name
16066          * @param {Mixed} value The state data
16067          */
16068          set : function(key, value){
16069             provider.set(key, value);
16070         },
16071         
16072         /**
16073          * Clears a value from the state
16074          * @param {String} name The key name
16075          */
16076         clear : function(key){
16077             provider.clear(key);
16078         },
16079         
16080         /**
16081          * Gets the currently configured state provider
16082          * @return {Provider} The state provider
16083          */
16084         getProvider : function(){
16085             return provider;
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.CookieProvider
16101  * @extends Roo.state.Provider
16102  * The default Provider implementation which saves state via cookies.
16103  * <br />Usage:
16104  <pre><code>
16105    var cp = new Roo.state.CookieProvider({
16106        path: "/cgi-bin/",
16107        expires: new Date(new Date().getTime()+(1000*60*60*24*30)); //30 days
16108        domain: "roojs.com"
16109    })
16110    Roo.state.Manager.setProvider(cp);
16111  </code></pre>
16112  * @cfg {String} path The path for which the cookie is active (defaults to root '/' which makes it active for all pages in the site)
16113  * @cfg {Date} expires The cookie expiration date (defaults to 7 days from now)
16114  * @cfg {String} domain The domain to save the cookie for.  Note that you cannot specify a different domain than
16115  * your page is on, but you can specify a sub-domain, or simply the domain itself like 'roojs.com' to include
16116  * all sub-domains if you need to access cookies across different sub-domains (defaults to null which uses the same
16117  * domain the page is running on including the 'www' like 'www.roojs.com')
16118  * @cfg {Boolean} secure True if the site is using SSL (defaults to false)
16119  * @constructor
16120  * Create a new CookieProvider
16121  * @param {Object} config The configuration object
16122  */
16123 Roo.state.CookieProvider = function(config){
16124     Roo.state.CookieProvider.superclass.constructor.call(this);
16125     this.path = "/";
16126     this.expires = new Date(new Date().getTime()+(1000*60*60*24*7)); //7 days
16127     this.domain = null;
16128     this.secure = false;
16129     Roo.apply(this, config);
16130     this.state = this.readCookies();
16131 };
16132
16133 Roo.extend(Roo.state.CookieProvider, Roo.state.Provider, {
16134     // private
16135     set : function(name, value){
16136         if(typeof value == "undefined" || value === null){
16137             this.clear(name);
16138             return;
16139         }
16140         this.setCookie(name, value);
16141         Roo.state.CookieProvider.superclass.set.call(this, name, value);
16142     },
16143
16144     // private
16145     clear : function(name){
16146         this.clearCookie(name);
16147         Roo.state.CookieProvider.superclass.clear.call(this, name);
16148     },
16149
16150     // private
16151     readCookies : function(){
16152         var cookies = {};
16153         var c = document.cookie + ";";
16154         var re = /\s?(.*?)=(.*?);/g;
16155         var matches;
16156         while((matches = re.exec(c)) != null){
16157             var name = matches[1];
16158             var value = matches[2];
16159             if(name && name.substring(0,3) == "ys-"){
16160                 cookies[name.substr(3)] = this.decodeValue(value);
16161             }
16162         }
16163         return cookies;
16164     },
16165
16166     // private
16167     setCookie : function(name, value){
16168         document.cookie = "ys-"+ name + "=" + this.encodeValue(value) +
16169            ((this.expires == null) ? "" : ("; expires=" + this.expires.toGMTString())) +
16170            ((this.path == null) ? "" : ("; path=" + this.path)) +
16171            ((this.domain == null) ? "" : ("; domain=" + this.domain)) +
16172            ((this.secure == true) ? "; secure" : "");
16173     },
16174
16175     // private
16176     clearCookie : function(name){
16177         document.cookie = "ys-" + name + "=null; expires=Thu, 01-Jan-70 00:00:01 GMT" +
16178            ((this.path == null) ? "" : ("; path=" + this.path)) +
16179            ((this.domain == null) ? "" : ("; domain=" + this.domain)) +
16180            ((this.secure == true) ? "; secure" : "");
16181     }
16182 });/*
16183  * Based on:
16184  * Ext JS Library 1.1.1
16185  * Copyright(c) 2006-2007, Ext JS, LLC.
16186  *
16187  * Originally Released Under LGPL - original licence link has changed is not relivant.
16188  *
16189  * Fork - LGPL
16190  * <script type="text/javascript">
16191  */
16192  
16193
16194 /**
16195  * @class Roo.ComponentMgr
16196  * Provides a common registry of all components on a page so that they can be easily accessed by component id (see {@link Roo.getCmp}).
16197  * @static
16198  */
16199 Roo.ComponentMgr = function(){
16200     var all = new Roo.util.MixedCollection();
16201
16202     return {
16203         /**
16204          * Registers a component.
16205          * @param {Roo.Component} c The component
16206          */
16207         register : function(c){
16208             all.add(c);
16209         },
16210
16211         /**
16212          * Unregisters a component.
16213          * @param {Roo.Component} c The component
16214          */
16215         unregister : function(c){
16216             all.remove(c);
16217         },
16218
16219         /**
16220          * Returns a component by id
16221          * @param {String} id The component id
16222          */
16223         get : function(id){
16224             return all.get(id);
16225         },
16226
16227         /**
16228          * Registers a function that will be called when a specified component is added to ComponentMgr
16229          * @param {String} id The component id
16230          * @param {Funtction} fn The callback function
16231          * @param {Object} scope The scope of the callback
16232          */
16233         onAvailable : function(id, fn, scope){
16234             all.on("add", function(index, o){
16235                 if(o.id == id){
16236                     fn.call(scope || o, o);
16237                     all.un("add", fn, scope);
16238                 }
16239             });
16240         }
16241     };
16242 }();/*
16243  * Based on:
16244  * Ext JS Library 1.1.1
16245  * Copyright(c) 2006-2007, Ext JS, LLC.
16246  *
16247  * Originally Released Under LGPL - original licence link has changed is not relivant.
16248  *
16249  * Fork - LGPL
16250  * <script type="text/javascript">
16251  */
16252  
16253 /**
16254  * @class Roo.Component
16255  * @extends Roo.util.Observable
16256  * Base class for all major Roo components.  All subclasses of Component can automatically participate in the standard
16257  * Roo component lifecycle of creation, rendering and destruction.  They also have automatic support for basic hide/show
16258  * and enable/disable behavior.  Component allows any subclass to be lazy-rendered into any {@link Roo.Container} and
16259  * to be automatically registered with the {@link Roo.ComponentMgr} so that it can be referenced at any time via {@link Roo.getCmp}.
16260  * All visual components (widgets) that require rendering into a layout should subclass Component.
16261  * @constructor
16262  * @param {Roo.Element/String/Object} config The configuration options.  If an element is passed, it is set as the internal
16263  * 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
16264  * and is used as the component id.  Otherwise, it is assumed to be a standard config object and is applied to the component.
16265  */
16266 Roo.Component = function(config){
16267     config = config || {};
16268     if(config.tagName || config.dom || typeof config == "string"){ // element object
16269         config = {el: config, id: config.id || config};
16270     }
16271     this.initialConfig = config;
16272
16273     Roo.apply(this, config);
16274     this.addEvents({
16275         /**
16276          * @event disable
16277          * Fires after the component is disabled.
16278              * @param {Roo.Component} this
16279              */
16280         disable : true,
16281         /**
16282          * @event enable
16283          * Fires after the component is enabled.
16284              * @param {Roo.Component} this
16285              */
16286         enable : true,
16287         /**
16288          * @event beforeshow
16289          * Fires before the component is shown.  Return false to stop the show.
16290              * @param {Roo.Component} this
16291              */
16292         beforeshow : true,
16293         /**
16294          * @event show
16295          * Fires after the component is shown.
16296              * @param {Roo.Component} this
16297              */
16298         show : true,
16299         /**
16300          * @event beforehide
16301          * Fires before the component is hidden. Return false to stop the hide.
16302              * @param {Roo.Component} this
16303              */
16304         beforehide : true,
16305         /**
16306          * @event hide
16307          * Fires after the component is hidden.
16308              * @param {Roo.Component} this
16309              */
16310         hide : true,
16311         /**
16312          * @event beforerender
16313          * Fires before the component is rendered. Return false to stop the render.
16314              * @param {Roo.Component} this
16315              */
16316         beforerender : true,
16317         /**
16318          * @event render
16319          * Fires after the component is rendered.
16320              * @param {Roo.Component} this
16321              */
16322         render : true,
16323         /**
16324          * @event beforedestroy
16325          * Fires before the component is destroyed. Return false to stop the destroy.
16326              * @param {Roo.Component} this
16327              */
16328         beforedestroy : true,
16329         /**
16330          * @event destroy
16331          * Fires after the component is destroyed.
16332              * @param {Roo.Component} this
16333              */
16334         destroy : true
16335     });
16336     if(!this.id){
16337         this.id = "roo-comp-" + (++Roo.Component.AUTO_ID);
16338     }
16339     Roo.ComponentMgr.register(this);
16340     Roo.Component.superclass.constructor.call(this);
16341     this.initComponent();
16342     if(this.renderTo){ // not supported by all components yet. use at your own risk!
16343         this.render(this.renderTo);
16344         delete this.renderTo;
16345     }
16346 };
16347
16348 /** @private */
16349 Roo.Component.AUTO_ID = 1000;
16350
16351 Roo.extend(Roo.Component, Roo.util.Observable, {
16352     /**
16353      * @scope Roo.Component.prototype
16354      * @type {Boolean}
16355      * true if this component is hidden. Read-only.
16356      */
16357     hidden : false,
16358     /**
16359      * @type {Boolean}
16360      * true if this component is disabled. Read-only.
16361      */
16362     disabled : false,
16363     /**
16364      * @type {Boolean}
16365      * true if this component has been rendered. Read-only.
16366      */
16367     rendered : false,
16368     
16369     /** @cfg {String} disableClass
16370      * CSS class added to the component when it is disabled (defaults to "x-item-disabled").
16371      */
16372     disabledClass : "x-item-disabled",
16373         /** @cfg {Boolean} allowDomMove
16374          * Whether the component can move the Dom node when rendering (defaults to true).
16375          */
16376     allowDomMove : true,
16377     /** @cfg {String} hideMode (display|visibility)
16378      * How this component should hidden. Supported values are
16379      * "visibility" (css visibility), "offsets" (negative offset position) and
16380      * "display" (css display) - defaults to "display".
16381      */
16382     hideMode: 'display',
16383
16384     /** @private */
16385     ctype : "Roo.Component",
16386
16387     /**
16388      * @cfg {String} actionMode 
16389      * which property holds the element that used for  hide() / show() / disable() / enable()
16390      * default is 'el' for forms you probably want to set this to fieldEl 
16391      */
16392     actionMode : "el",
16393
16394     /** @private */
16395     getActionEl : function(){
16396         return this[this.actionMode];
16397     },
16398
16399     initComponent : Roo.emptyFn,
16400     /**
16401      * If this is a lazy rendering component, render it to its container element.
16402      * @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.
16403      */
16404     render : function(container, position){
16405         
16406         if(this.rendered){
16407             return this;
16408         }
16409         
16410         if(this.fireEvent("beforerender", this) === false){
16411             return false;
16412         }
16413         
16414         if(!container && this.el){
16415             this.el = Roo.get(this.el);
16416             container = this.el.dom.parentNode;
16417             this.allowDomMove = false;
16418         }
16419         this.container = Roo.get(container);
16420         this.rendered = true;
16421         if(position !== undefined){
16422             if(typeof position == 'number'){
16423                 position = this.container.dom.childNodes[position];
16424             }else{
16425                 position = Roo.getDom(position);
16426             }
16427         }
16428         this.onRender(this.container, position || null);
16429         if(this.cls){
16430             this.el.addClass(this.cls);
16431             delete this.cls;
16432         }
16433         if(this.style){
16434             this.el.applyStyles(this.style);
16435             delete this.style;
16436         }
16437         this.fireEvent("render", this);
16438         this.afterRender(this.container);
16439         if(this.hidden){
16440             this.hide();
16441         }
16442         if(this.disabled){
16443             this.disable();
16444         }
16445
16446         return this;
16447         
16448     },
16449
16450     /** @private */
16451     // default function is not really useful
16452     onRender : function(ct, position){
16453         if(this.el){
16454             this.el = Roo.get(this.el);
16455             if(this.allowDomMove !== false){
16456                 ct.dom.insertBefore(this.el.dom, position);
16457             }
16458         }
16459     },
16460
16461     /** @private */
16462     getAutoCreate : function(){
16463         var cfg = typeof this.autoCreate == "object" ?
16464                       this.autoCreate : Roo.apply({}, this.defaultAutoCreate);
16465         if(this.id && !cfg.id){
16466             cfg.id = this.id;
16467         }
16468         return cfg;
16469     },
16470
16471     /** @private */
16472     afterRender : Roo.emptyFn,
16473
16474     /**
16475      * Destroys this component by purging any event listeners, removing the component's element from the DOM,
16476      * removing the component from its {@link Roo.Container} (if applicable) and unregistering it from {@link Roo.ComponentMgr}.
16477      */
16478     destroy : function(){
16479         if(this.fireEvent("beforedestroy", this) !== false){
16480             this.purgeListeners();
16481             this.beforeDestroy();
16482             if(this.rendered){
16483                 this.el.removeAllListeners();
16484                 this.el.remove();
16485                 if(this.actionMode == "container"){
16486                     this.container.remove();
16487                 }
16488             }
16489             this.onDestroy();
16490             Roo.ComponentMgr.unregister(this);
16491             this.fireEvent("destroy", this);
16492         }
16493     },
16494
16495         /** @private */
16496     beforeDestroy : function(){
16497
16498     },
16499
16500         /** @private */
16501         onDestroy : function(){
16502
16503     },
16504
16505     /**
16506      * Returns the underlying {@link Roo.Element}.
16507      * @return {Roo.Element} The element
16508      */
16509     getEl : function(){
16510         return this.el;
16511     },
16512
16513     /**
16514      * Returns the id of this component.
16515      * @return {String}
16516      */
16517     getId : function(){
16518         return this.id;
16519     },
16520
16521     /**
16522      * Try to focus this component.
16523      * @param {Boolean} selectText True to also select the text in this component (if applicable)
16524      * @return {Roo.Component} this
16525      */
16526     focus : function(selectText){
16527         if(this.rendered){
16528             this.el.focus();
16529             if(selectText === true){
16530                 this.el.dom.select();
16531             }
16532         }
16533         return this;
16534     },
16535
16536     /** @private */
16537     blur : function(){
16538         if(this.rendered){
16539             this.el.blur();
16540         }
16541         return this;
16542     },
16543
16544     /**
16545      * Disable this component.
16546      * @return {Roo.Component} this
16547      */
16548     disable : function(){
16549         if(this.rendered){
16550             this.onDisable();
16551         }
16552         this.disabled = true;
16553         this.fireEvent("disable", this);
16554         return this;
16555     },
16556
16557         // private
16558     onDisable : function(){
16559         this.getActionEl().addClass(this.disabledClass);
16560         this.el.dom.disabled = true;
16561     },
16562
16563     /**
16564      * Enable this component.
16565      * @return {Roo.Component} this
16566      */
16567     enable : function(){
16568         if(this.rendered){
16569             this.onEnable();
16570         }
16571         this.disabled = false;
16572         this.fireEvent("enable", this);
16573         return this;
16574     },
16575
16576         // private
16577     onEnable : function(){
16578         this.getActionEl().removeClass(this.disabledClass);
16579         this.el.dom.disabled = false;
16580     },
16581
16582     /**
16583      * Convenience function for setting disabled/enabled by boolean.
16584      * @param {Boolean} disabled
16585      */
16586     setDisabled : function(disabled){
16587         this[disabled ? "disable" : "enable"]();
16588     },
16589
16590     /**
16591      * Show this component.
16592      * @return {Roo.Component} this
16593      */
16594     show: function(){
16595         if(this.fireEvent("beforeshow", this) !== false){
16596             this.hidden = false;
16597             if(this.rendered){
16598                 this.onShow();
16599             }
16600             this.fireEvent("show", this);
16601         }
16602         return this;
16603     },
16604
16605     // private
16606     onShow : function(){
16607         var ae = this.getActionEl();
16608         if(this.hideMode == 'visibility'){
16609             ae.dom.style.visibility = "visible";
16610         }else if(this.hideMode == 'offsets'){
16611             ae.removeClass('x-hidden');
16612         }else{
16613             ae.dom.style.display = "";
16614         }
16615     },
16616
16617     /**
16618      * Hide this component.
16619      * @return {Roo.Component} this
16620      */
16621     hide: function(){
16622         if(this.fireEvent("beforehide", this) !== false){
16623             this.hidden = true;
16624             if(this.rendered){
16625                 this.onHide();
16626             }
16627             this.fireEvent("hide", this);
16628         }
16629         return this;
16630     },
16631
16632     // private
16633     onHide : function(){
16634         var ae = this.getActionEl();
16635         if(this.hideMode == 'visibility'){
16636             ae.dom.style.visibility = "hidden";
16637         }else if(this.hideMode == 'offsets'){
16638             ae.addClass('x-hidden');
16639         }else{
16640             ae.dom.style.display = "none";
16641         }
16642     },
16643
16644     /**
16645      * Convenience function to hide or show this component by boolean.
16646      * @param {Boolean} visible True to show, false to hide
16647      * @return {Roo.Component} this
16648      */
16649     setVisible: function(visible){
16650         if(visible) {
16651             this.show();
16652         }else{
16653             this.hide();
16654         }
16655         return this;
16656     },
16657
16658     /**
16659      * Returns true if this component is visible.
16660      */
16661     isVisible : function(){
16662         return this.getActionEl().isVisible();
16663     },
16664
16665     cloneConfig : function(overrides){
16666         overrides = overrides || {};
16667         var id = overrides.id || Roo.id();
16668         var cfg = Roo.applyIf(overrides, this.initialConfig);
16669         cfg.id = id; // prevent dup id
16670         return new this.constructor(cfg);
16671     }
16672 });/*
16673  * Based on:
16674  * Ext JS Library 1.1.1
16675  * Copyright(c) 2006-2007, Ext JS, LLC.
16676  *
16677  * Originally Released Under LGPL - original licence link has changed is not relivant.
16678  *
16679  * Fork - LGPL
16680  * <script type="text/javascript">
16681  */
16682
16683 /**
16684  * @class Roo.BoxComponent
16685  * @extends Roo.Component
16686  * Base class for any visual {@link Roo.Component} that uses a box container.  BoxComponent provides automatic box
16687  * model adjustments for sizing and positioning and will work correctly withnin the Component rendering model.  All
16688  * container classes should subclass BoxComponent so that they will work consistently when nested within other Roo
16689  * layout containers.
16690  * @constructor
16691  * @param {Roo.Element/String/Object} config The configuration options.
16692  */
16693 Roo.BoxComponent = function(config){
16694     Roo.Component.call(this, config);
16695     this.addEvents({
16696         /**
16697          * @event resize
16698          * Fires after the component is resized.
16699              * @param {Roo.Component} this
16700              * @param {Number} adjWidth The box-adjusted width that was set
16701              * @param {Number} adjHeight The box-adjusted height that was set
16702              * @param {Number} rawWidth The width that was originally specified
16703              * @param {Number} rawHeight The height that was originally specified
16704              */
16705         resize : true,
16706         /**
16707          * @event move
16708          * Fires after the component is moved.
16709              * @param {Roo.Component} this
16710              * @param {Number} x The new x position
16711              * @param {Number} y The new y position
16712              */
16713         move : true
16714     });
16715 };
16716
16717 Roo.extend(Roo.BoxComponent, Roo.Component, {
16718     // private, set in afterRender to signify that the component has been rendered
16719     boxReady : false,
16720     // private, used to defer height settings to subclasses
16721     deferHeight: false,
16722     /** @cfg {Number} width
16723      * width (optional) size of component
16724      */
16725      /** @cfg {Number} height
16726      * height (optional) size of component
16727      */
16728      
16729     /**
16730      * Sets the width and height of the component.  This method fires the resize event.  This method can accept
16731      * either width and height as separate numeric arguments, or you can pass a size object like {width:10, height:20}.
16732      * @param {Number/Object} width The new width to set, or a size object in the format {width, height}
16733      * @param {Number} height The new height to set (not required if a size object is passed as the first arg)
16734      * @return {Roo.BoxComponent} this
16735      */
16736     setSize : function(w, h){
16737         // support for standard size objects
16738         if(typeof w == 'object'){
16739             h = w.height;
16740             w = w.width;
16741         }
16742         // not rendered
16743         if(!this.boxReady){
16744             this.width = w;
16745             this.height = h;
16746             return this;
16747         }
16748
16749         // prevent recalcs when not needed
16750         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
16751             return this;
16752         }
16753         this.lastSize = {width: w, height: h};
16754
16755         var adj = this.adjustSize(w, h);
16756         var aw = adj.width, ah = adj.height;
16757         if(aw !== undefined || ah !== undefined){ // this code is nasty but performs better with floaters
16758             var rz = this.getResizeEl();
16759             if(!this.deferHeight && aw !== undefined && ah !== undefined){
16760                 rz.setSize(aw, ah);
16761             }else if(!this.deferHeight && ah !== undefined){
16762                 rz.setHeight(ah);
16763             }else if(aw !== undefined){
16764                 rz.setWidth(aw);
16765             }
16766             this.onResize(aw, ah, w, h);
16767             this.fireEvent('resize', this, aw, ah, w, h);
16768         }
16769         return this;
16770     },
16771
16772     /**
16773      * Gets the current size of the component's underlying element.
16774      * @return {Object} An object containing the element's size {width: (element width), height: (element height)}
16775      */
16776     getSize : function(){
16777         return this.el.getSize();
16778     },
16779
16780     /**
16781      * Gets the current XY position of the component's underlying element.
16782      * @param {Boolean} local (optional) If true the element's left and top are returned instead of page XY (defaults to false)
16783      * @return {Array} The XY position of the element (e.g., [100, 200])
16784      */
16785     getPosition : function(local){
16786         if(local === true){
16787             return [this.el.getLeft(true), this.el.getTop(true)];
16788         }
16789         return this.xy || this.el.getXY();
16790     },
16791
16792     /**
16793      * Gets the current box measurements of the component's underlying element.
16794      * @param {Boolean} local (optional) If true the element's left and top are returned instead of page XY (defaults to false)
16795      * @returns {Object} box An object in the format {x, y, width, height}
16796      */
16797     getBox : function(local){
16798         var s = this.el.getSize();
16799         if(local){
16800             s.x = this.el.getLeft(true);
16801             s.y = this.el.getTop(true);
16802         }else{
16803             var xy = this.xy || this.el.getXY();
16804             s.x = xy[0];
16805             s.y = xy[1];
16806         }
16807         return s;
16808     },
16809
16810     /**
16811      * Sets the current box measurements of the component's underlying element.
16812      * @param {Object} box An object in the format {x, y, width, height}
16813      * @returns {Roo.BoxComponent} this
16814      */
16815     updateBox : function(box){
16816         this.setSize(box.width, box.height);
16817         this.setPagePosition(box.x, box.y);
16818         return this;
16819     },
16820
16821     // protected
16822     getResizeEl : function(){
16823         return this.resizeEl || this.el;
16824     },
16825
16826     // protected
16827     getPositionEl : function(){
16828         return this.positionEl || this.el;
16829     },
16830
16831     /**
16832      * Sets the left and top of the component.  To set the page XY position instead, use {@link #setPagePosition}.
16833      * This method fires the move event.
16834      * @param {Number} left The new left
16835      * @param {Number} top The new top
16836      * @returns {Roo.BoxComponent} this
16837      */
16838     setPosition : function(x, y){
16839         this.x = x;
16840         this.y = y;
16841         if(!this.boxReady){
16842             return this;
16843         }
16844         var adj = this.adjustPosition(x, y);
16845         var ax = adj.x, ay = adj.y;
16846
16847         var el = this.getPositionEl();
16848         if(ax !== undefined || ay !== undefined){
16849             if(ax !== undefined && ay !== undefined){
16850                 el.setLeftTop(ax, ay);
16851             }else if(ax !== undefined){
16852                 el.setLeft(ax);
16853             }else if(ay !== undefined){
16854                 el.setTop(ay);
16855             }
16856             this.onPosition(ax, ay);
16857             this.fireEvent('move', this, ax, ay);
16858         }
16859         return this;
16860     },
16861
16862     /**
16863      * Sets the page XY position of the component.  To set the left and top instead, use {@link #setPosition}.
16864      * This method fires the move event.
16865      * @param {Number} x The new x position
16866      * @param {Number} y The new y position
16867      * @returns {Roo.BoxComponent} this
16868      */
16869     setPagePosition : function(x, y){
16870         this.pageX = x;
16871         this.pageY = y;
16872         if(!this.boxReady){
16873             return;
16874         }
16875         if(x === undefined || y === undefined){ // cannot translate undefined points
16876             return;
16877         }
16878         var p = this.el.translatePoints(x, y);
16879         this.setPosition(p.left, p.top);
16880         return this;
16881     },
16882
16883     // private
16884     onRender : function(ct, position){
16885         Roo.BoxComponent.superclass.onRender.call(this, ct, position);
16886         if(this.resizeEl){
16887             this.resizeEl = Roo.get(this.resizeEl);
16888         }
16889         if(this.positionEl){
16890             this.positionEl = Roo.get(this.positionEl);
16891         }
16892     },
16893
16894     // private
16895     afterRender : function(){
16896         Roo.BoxComponent.superclass.afterRender.call(this);
16897         this.boxReady = true;
16898         this.setSize(this.width, this.height);
16899         if(this.x || this.y){
16900             this.setPosition(this.x, this.y);
16901         }
16902         if(this.pageX || this.pageY){
16903             this.setPagePosition(this.pageX, this.pageY);
16904         }
16905     },
16906
16907     /**
16908      * Force the component's size to recalculate based on the underlying element's current height and width.
16909      * @returns {Roo.BoxComponent} this
16910      */
16911     syncSize : function(){
16912         delete this.lastSize;
16913         this.setSize(this.el.getWidth(), this.el.getHeight());
16914         return this;
16915     },
16916
16917     /**
16918      * Called after the component is resized, this method is empty by default but can be implemented by any
16919      * subclass that needs to perform custom logic after a resize occurs.
16920      * @param {Number} adjWidth The box-adjusted width that was set
16921      * @param {Number} adjHeight The box-adjusted height that was set
16922      * @param {Number} rawWidth The width that was originally specified
16923      * @param {Number} rawHeight The height that was originally specified
16924      */
16925     onResize : function(adjWidth, adjHeight, rawWidth, rawHeight){
16926
16927     },
16928
16929     /**
16930      * Called after the component is moved, this method is empty by default but can be implemented by any
16931      * subclass that needs to perform custom logic after a move occurs.
16932      * @param {Number} x The new x position
16933      * @param {Number} y The new y position
16934      */
16935     onPosition : function(x, y){
16936
16937     },
16938
16939     // private
16940     adjustSize : function(w, h){
16941         if(this.autoWidth){
16942             w = 'auto';
16943         }
16944         if(this.autoHeight){
16945             h = 'auto';
16946         }
16947         return {width : w, height: h};
16948     },
16949
16950     // private
16951     adjustPosition : function(x, y){
16952         return {x : x, y: y};
16953     }
16954 });/*
16955  * Based on:
16956  * Ext JS Library 1.1.1
16957  * Copyright(c) 2006-2007, Ext JS, LLC.
16958  *
16959  * Originally Released Under LGPL - original licence link has changed is not relivant.
16960  *
16961  * Fork - LGPL
16962  * <script type="text/javascript">
16963  */
16964  (function(){ 
16965 /**
16966  * @class Roo.Layer
16967  * @extends Roo.Element
16968  * An extended {@link Roo.Element} object that supports a shadow and shim, constrain to viewport and
16969  * automatic maintaining of shadow/shim positions.
16970  * @cfg {Boolean} shim False to disable the iframe shim in browsers which need one (defaults to true)
16971  * @cfg {String/Boolean} shadow True to create a shadow element with default class "x-layer-shadow", or
16972  * you can pass a string with a CSS class name. False turns off the shadow.
16973  * @cfg {Object} dh DomHelper object config to create element with (defaults to {tag: "div", cls: "x-layer"}).
16974  * @cfg {Boolean} constrain False to disable constrain to viewport (defaults to true)
16975  * @cfg {String} cls CSS class to add to the element
16976  * @cfg {Number} zindex Starting z-index (defaults to 11000)
16977  * @cfg {Number} shadowOffset Number of pixels to offset the shadow (defaults to 3)
16978  * @constructor
16979  * @param {Object} config An object with config options.
16980  * @param {String/HTMLElement} existingEl (optional) Uses an existing DOM element. If the element is not found it creates it.
16981  */
16982
16983 Roo.Layer = function(config, existingEl){
16984     config = config || {};
16985     var dh = Roo.DomHelper;
16986     var cp = config.parentEl, pel = cp ? Roo.getDom(cp) : document.body;
16987     if(existingEl){
16988         this.dom = Roo.getDom(existingEl);
16989     }
16990     if(!this.dom){
16991         var o = config.dh || {tag: "div", cls: "x-layer"};
16992         this.dom = dh.append(pel, o);
16993     }
16994     if(config.cls){
16995         this.addClass(config.cls);
16996     }
16997     this.constrain = config.constrain !== false;
16998     this.visibilityMode = Roo.Element.VISIBILITY;
16999     if(config.id){
17000         this.id = this.dom.id = config.id;
17001     }else{
17002         this.id = Roo.id(this.dom);
17003     }
17004     this.zindex = config.zindex || this.getZIndex();
17005     this.position("absolute", this.zindex);
17006     if(config.shadow){
17007         this.shadowOffset = config.shadowOffset || 4;
17008         this.shadow = new Roo.Shadow({
17009             offset : this.shadowOffset,
17010             mode : config.shadow
17011         });
17012     }else{
17013         this.shadowOffset = 0;
17014     }
17015     this.useShim = config.shim !== false && Roo.useShims;
17016     this.useDisplay = config.useDisplay;
17017     this.hide();
17018 };
17019
17020 var supr = Roo.Element.prototype;
17021
17022 // shims are shared among layer to keep from having 100 iframes
17023 var shims = [];
17024
17025 Roo.extend(Roo.Layer, Roo.Element, {
17026
17027     getZIndex : function(){
17028         return this.zindex || parseInt(this.getStyle("z-index"), 10) || 11000;
17029     },
17030
17031     getShim : function(){
17032         if(!this.useShim){
17033             return null;
17034         }
17035         if(this.shim){
17036             return this.shim;
17037         }
17038         var shim = shims.shift();
17039         if(!shim){
17040             shim = this.createShim();
17041             shim.enableDisplayMode('block');
17042             shim.dom.style.display = 'none';
17043             shim.dom.style.visibility = 'visible';
17044         }
17045         var pn = this.dom.parentNode;
17046         if(shim.dom.parentNode != pn){
17047             pn.insertBefore(shim.dom, this.dom);
17048         }
17049         shim.setStyle('z-index', this.getZIndex()-2);
17050         this.shim = shim;
17051         return shim;
17052     },
17053
17054     hideShim : function(){
17055         if(this.shim){
17056             this.shim.setDisplayed(false);
17057             shims.push(this.shim);
17058             delete this.shim;
17059         }
17060     },
17061
17062     disableShadow : function(){
17063         if(this.shadow){
17064             this.shadowDisabled = true;
17065             this.shadow.hide();
17066             this.lastShadowOffset = this.shadowOffset;
17067             this.shadowOffset = 0;
17068         }
17069     },
17070
17071     enableShadow : function(show){
17072         if(this.shadow){
17073             this.shadowDisabled = false;
17074             this.shadowOffset = this.lastShadowOffset;
17075             delete this.lastShadowOffset;
17076             if(show){
17077                 this.sync(true);
17078             }
17079         }
17080     },
17081
17082     // private
17083     // this code can execute repeatedly in milliseconds (i.e. during a drag) so
17084     // code size was sacrificed for effeciency (e.g. no getBox/setBox, no XY calls)
17085     sync : function(doShow){
17086         var sw = this.shadow;
17087         if(!this.updating && this.isVisible() && (sw || this.useShim)){
17088             var sh = this.getShim();
17089
17090             var w = this.getWidth(),
17091                 h = this.getHeight();
17092
17093             var l = this.getLeft(true),
17094                 t = this.getTop(true);
17095
17096             if(sw && !this.shadowDisabled){
17097                 if(doShow && !sw.isVisible()){
17098                     sw.show(this);
17099                 }else{
17100                     sw.realign(l, t, w, h);
17101                 }
17102                 if(sh){
17103                     if(doShow){
17104                        sh.show();
17105                     }
17106                     // fit the shim behind the shadow, so it is shimmed too
17107                     var a = sw.adjusts, s = sh.dom.style;
17108                     s.left = (Math.min(l, l+a.l))+"px";
17109                     s.top = (Math.min(t, t+a.t))+"px";
17110                     s.width = (w+a.w)+"px";
17111                     s.height = (h+a.h)+"px";
17112                 }
17113             }else if(sh){
17114                 if(doShow){
17115                    sh.show();
17116                 }
17117                 sh.setSize(w, h);
17118                 sh.setLeftTop(l, t);
17119             }
17120             
17121         }
17122     },
17123
17124     // private
17125     destroy : function(){
17126         this.hideShim();
17127         if(this.shadow){
17128             this.shadow.hide();
17129         }
17130         this.removeAllListeners();
17131         var pn = this.dom.parentNode;
17132         if(pn){
17133             pn.removeChild(this.dom);
17134         }
17135         Roo.Element.uncache(this.id);
17136     },
17137
17138     remove : function(){
17139         this.destroy();
17140     },
17141
17142     // private
17143     beginUpdate : function(){
17144         this.updating = true;
17145     },
17146
17147     // private
17148     endUpdate : function(){
17149         this.updating = false;
17150         this.sync(true);
17151     },
17152
17153     // private
17154     hideUnders : function(negOffset){
17155         if(this.shadow){
17156             this.shadow.hide();
17157         }
17158         this.hideShim();
17159     },
17160
17161     // private
17162     constrainXY : function(){
17163         if(this.constrain){
17164             var vw = Roo.lib.Dom.getViewWidth(),
17165                 vh = Roo.lib.Dom.getViewHeight();
17166             var s = Roo.get(document).getScroll();
17167
17168             var xy = this.getXY();
17169             var x = xy[0], y = xy[1];   
17170             var w = this.dom.offsetWidth+this.shadowOffset, h = this.dom.offsetHeight+this.shadowOffset;
17171             // only move it if it needs it
17172             var moved = false;
17173             // first validate right/bottom
17174             if((x + w) > vw+s.left){
17175                 x = vw - w - this.shadowOffset;
17176                 moved = true;
17177             }
17178             if((y + h) > vh+s.top){
17179                 y = vh - h - this.shadowOffset;
17180                 moved = true;
17181             }
17182             // then make sure top/left isn't negative
17183             if(x < s.left){
17184                 x = s.left;
17185                 moved = true;
17186             }
17187             if(y < s.top){
17188                 y = s.top;
17189                 moved = true;
17190             }
17191             if(moved){
17192                 if(this.avoidY){
17193                     var ay = this.avoidY;
17194                     if(y <= ay && (y+h) >= ay){
17195                         y = ay-h-5;   
17196                     }
17197                 }
17198                 xy = [x, y];
17199                 this.storeXY(xy);
17200                 supr.setXY.call(this, xy);
17201                 this.sync();
17202             }
17203         }
17204     },
17205
17206     isVisible : function(){
17207         return this.visible;    
17208     },
17209
17210     // private
17211     showAction : function(){
17212         this.visible = true; // track visibility to prevent getStyle calls
17213         if(this.useDisplay === true){
17214             this.setDisplayed("");
17215         }else if(this.lastXY){
17216             supr.setXY.call(this, this.lastXY);
17217         }else if(this.lastLT){
17218             supr.setLeftTop.call(this, this.lastLT[0], this.lastLT[1]);
17219         }
17220     },
17221
17222     // private
17223     hideAction : function(){
17224         this.visible = false;
17225         if(this.useDisplay === true){
17226             this.setDisplayed(false);
17227         }else{
17228             this.setLeftTop(-10000,-10000);
17229         }
17230     },
17231
17232     // overridden Element method
17233     setVisible : function(v, a, d, c, e){
17234         if(v){
17235             this.showAction();
17236         }
17237         if(a && v){
17238             var cb = function(){
17239                 this.sync(true);
17240                 if(c){
17241                     c();
17242                 }
17243             }.createDelegate(this);
17244             supr.setVisible.call(this, true, true, d, cb, e);
17245         }else{
17246             if(!v){
17247                 this.hideUnders(true);
17248             }
17249             var cb = c;
17250             if(a){
17251                 cb = function(){
17252                     this.hideAction();
17253                     if(c){
17254                         c();
17255                     }
17256                 }.createDelegate(this);
17257             }
17258             supr.setVisible.call(this, v, a, d, cb, e);
17259             if(v){
17260                 this.sync(true);
17261             }else if(!a){
17262                 this.hideAction();
17263             }
17264         }
17265     },
17266
17267     storeXY : function(xy){
17268         delete this.lastLT;
17269         this.lastXY = xy;
17270     },
17271
17272     storeLeftTop : function(left, top){
17273         delete this.lastXY;
17274         this.lastLT = [left, top];
17275     },
17276
17277     // private
17278     beforeFx : function(){
17279         this.beforeAction();
17280         return Roo.Layer.superclass.beforeFx.apply(this, arguments);
17281     },
17282
17283     // private
17284     afterFx : function(){
17285         Roo.Layer.superclass.afterFx.apply(this, arguments);
17286         this.sync(this.isVisible());
17287     },
17288
17289     // private
17290     beforeAction : function(){
17291         if(!this.updating && this.shadow){
17292             this.shadow.hide();
17293         }
17294     },
17295
17296     // overridden Element method
17297     setLeft : function(left){
17298         this.storeLeftTop(left, this.getTop(true));
17299         supr.setLeft.apply(this, arguments);
17300         this.sync();
17301     },
17302
17303     setTop : function(top){
17304         this.storeLeftTop(this.getLeft(true), top);
17305         supr.setTop.apply(this, arguments);
17306         this.sync();
17307     },
17308
17309     setLeftTop : function(left, top){
17310         this.storeLeftTop(left, top);
17311         supr.setLeftTop.apply(this, arguments);
17312         this.sync();
17313     },
17314
17315     setXY : function(xy, a, d, c, e){
17316         this.fixDisplay();
17317         this.beforeAction();
17318         this.storeXY(xy);
17319         var cb = this.createCB(c);
17320         supr.setXY.call(this, xy, a, d, cb, e);
17321         if(!a){
17322             cb();
17323         }
17324     },
17325
17326     // private
17327     createCB : function(c){
17328         var el = this;
17329         return function(){
17330             el.constrainXY();
17331             el.sync(true);
17332             if(c){
17333                 c();
17334             }
17335         };
17336     },
17337
17338     // overridden Element method
17339     setX : function(x, a, d, c, e){
17340         this.setXY([x, this.getY()], a, d, c, e);
17341     },
17342
17343     // overridden Element method
17344     setY : function(y, a, d, c, e){
17345         this.setXY([this.getX(), y], a, d, c, e);
17346     },
17347
17348     // overridden Element method
17349     setSize : function(w, h, a, d, c, e){
17350         this.beforeAction();
17351         var cb = this.createCB(c);
17352         supr.setSize.call(this, w, h, a, d, cb, e);
17353         if(!a){
17354             cb();
17355         }
17356     },
17357
17358     // overridden Element method
17359     setWidth : function(w, a, d, c, e){
17360         this.beforeAction();
17361         var cb = this.createCB(c);
17362         supr.setWidth.call(this, w, a, d, cb, e);
17363         if(!a){
17364             cb();
17365         }
17366     },
17367
17368     // overridden Element method
17369     setHeight : function(h, a, d, c, e){
17370         this.beforeAction();
17371         var cb = this.createCB(c);
17372         supr.setHeight.call(this, h, a, d, cb, e);
17373         if(!a){
17374             cb();
17375         }
17376     },
17377
17378     // overridden Element method
17379     setBounds : function(x, y, w, h, a, d, c, e){
17380         this.beforeAction();
17381         var cb = this.createCB(c);
17382         if(!a){
17383             this.storeXY([x, y]);
17384             supr.setXY.call(this, [x, y]);
17385             supr.setSize.call(this, w, h, a, d, cb, e);
17386             cb();
17387         }else{
17388             supr.setBounds.call(this, x, y, w, h, a, d, cb, e);
17389         }
17390         return this;
17391     },
17392     
17393     /**
17394      * Sets the z-index of this layer and adjusts any shadow and shim z-indexes. The layer z-index is automatically
17395      * incremented by two more than the value passed in so that it always shows above any shadow or shim (the shadow
17396      * element, if any, will be assigned z-index + 1, and the shim element, if any, will be assigned the unmodified z-index).
17397      * @param {Number} zindex The new z-index to set
17398      * @return {this} The Layer
17399      */
17400     setZIndex : function(zindex){
17401         this.zindex = zindex;
17402         this.setStyle("z-index", zindex + 2);
17403         if(this.shadow){
17404             this.shadow.setZIndex(zindex + 1);
17405         }
17406         if(this.shim){
17407             this.shim.setStyle("z-index", zindex);
17408         }
17409     }
17410 });
17411 })();/*
17412  * Original code for Roojs - LGPL
17413  * <script type="text/javascript">
17414  */
17415  
17416 /**
17417  * @class Roo.XComponent
17418  * A delayed Element creator...
17419  * Or a way to group chunks of interface together.
17420  * technically this is a wrapper around a tree of Roo elements (which defines a 'module'),
17421  *  used in conjunction with XComponent.build() it will create an instance of each element,
17422  *  then call addxtype() to build the User interface.
17423  * 
17424  * Mypart.xyx = new Roo.XComponent({
17425
17426     parent : 'Mypart.xyz', // empty == document.element.!!
17427     order : '001',
17428     name : 'xxxx'
17429     region : 'xxxx'
17430     disabled : function() {} 
17431      
17432     tree : function() { // return an tree of xtype declared components
17433         var MODULE = this;
17434         return 
17435         {
17436             xtype : 'NestedLayoutPanel',
17437             // technicall
17438         }
17439      ]
17440  *})
17441  *
17442  *
17443  * It can be used to build a big heiracy, with parent etc.
17444  * or you can just use this to render a single compoent to a dom element
17445  * MYPART.render(Roo.Element | String(id) | dom_element )
17446  *
17447  *
17448  * Usage patterns.
17449  *
17450  * Classic Roo
17451  *
17452  * Roo is designed primarily as a single page application, so the UI build for a standard interface will
17453  * expect a single 'TOP' level module normally indicated by the 'parent' of the XComponent definition being defined as false.
17454  *
17455  * Each sub module is expected to have a parent pointing to the class name of it's parent module.
17456  *
17457  * When the top level is false, a 'Roo.BorderLayout' is created and the element is flagged as 'topModule'
17458  * - if mulitple topModules exist, the last one is defined as the top module.
17459  *
17460  * Embeded Roo
17461  * 
17462  * When the top level or multiple modules are to embedded into a existing HTML page,
17463  * the parent element can container '#id' of the element where the module will be drawn.
17464  *
17465  * Bootstrap Roo
17466  *
17467  * Unlike classic Roo, the bootstrap tends not to be used as a single page.
17468  * it relies more on a include mechanism, where sub modules are included into an outer page.
17469  * This is normally managed by the builder tools using Roo.apply( options, Included.Sub.Module )
17470  * 
17471  * Bootstrap Roo Included elements
17472  *
17473  * Our builder application needs the ability to preview these sub compoennts. They will normally have parent=false set,
17474  * hence confusing the component builder as it thinks there are multiple top level elements. 
17475  *
17476  * String Over-ride & Translations
17477  *
17478  * Our builder application writes all the strings as _strings and _named_strings. This is to enable the translation of elements,
17479  * and also the 'overlaying of string values - needed when different versions of the same application with different text content
17480  * are needed. @see Roo.XComponent.overlayString  
17481  * 
17482  * 
17483  * 
17484  * @extends Roo.util.Observable
17485  * @constructor
17486  * @param cfg {Object} configuration of component
17487  * 
17488  */
17489 Roo.XComponent = function(cfg) {
17490     Roo.apply(this, cfg);
17491     this.addEvents({ 
17492         /**
17493              * @event built
17494              * Fires when this the componnt is built
17495              * @param {Roo.XComponent} c the component
17496              */
17497         'built' : true
17498         
17499     });
17500     this.region = this.region || 'center'; // default..
17501     Roo.XComponent.register(this);
17502     this.modules = false;
17503     this.el = false; // where the layout goes..
17504     
17505     
17506 }
17507 Roo.extend(Roo.XComponent, Roo.util.Observable, {
17508     /**
17509      * @property el
17510      * The created element (with Roo.factory())
17511      * @type {Roo.Layout}
17512      */
17513     el  : false,
17514     
17515     /**
17516      * @property el
17517      * for BC  - use el in new code
17518      * @type {Roo.Layout}
17519      */
17520     panel : false,
17521     
17522     /**
17523      * @property layout
17524      * for BC  - use el in new code
17525      * @type {Roo.Layout}
17526      */
17527     layout : false,
17528     
17529      /**
17530      * @cfg {Function|boolean} disabled
17531      * If this module is disabled by some rule, return true from the funtion
17532      */
17533     disabled : false,
17534     
17535     /**
17536      * @cfg {String} parent 
17537      * Name of parent element which it get xtype added to..
17538      */
17539     parent: false,
17540     
17541     /**
17542      * @cfg {String} order
17543      * Used to set the order in which elements are created (usefull for multiple tabs)
17544      */
17545     
17546     order : false,
17547     /**
17548      * @cfg {String} name
17549      * String to display while loading.
17550      */
17551     name : false,
17552     /**
17553      * @cfg {String} region
17554      * Region to render component to (defaults to center)
17555      */
17556     region : 'center',
17557     
17558     /**
17559      * @cfg {Array} items
17560      * A single item array - the first element is the root of the tree..
17561      * It's done this way to stay compatible with the Xtype system...
17562      */
17563     items : false,
17564     
17565     /**
17566      * @property _tree
17567      * The method that retuns the tree of parts that make up this compoennt 
17568      * @type {function}
17569      */
17570     _tree  : false,
17571     
17572      /**
17573      * render
17574      * render element to dom or tree
17575      * @param {Roo.Element|String|DomElement} optional render to if parent is not set.
17576      */
17577     
17578     render : function(el)
17579     {
17580         
17581         el = el || false;
17582         var hp = this.parent ? 1 : 0;
17583         Roo.debug &&  Roo.log(this);
17584         
17585         var tree = this._tree ? this._tree() : this.tree();
17586
17587         
17588         if (!el && typeof(this.parent) == 'string' && this.parent.substring(0,1) == '#') {
17589             // if parent is a '#.....' string, then let's use that..
17590             var ename = this.parent.substr(1);
17591             this.parent = false;
17592             Roo.debug && Roo.log(ename);
17593             switch (ename) {
17594                 case 'bootstrap-body':
17595                     if (typeof(tree.el) != 'undefined' && tree.el == document.body)  {
17596                         // this is the BorderLayout standard?
17597                        this.parent = { el : true };
17598                        break;
17599                     }
17600                     if (["Nest", "Content", "Grid", "Tree"].indexOf(tree.xtype)  > -1)  {
17601                         // need to insert stuff...
17602                         this.parent =  {
17603                              el : new Roo.bootstrap.layout.Border({
17604                                  el : document.body, 
17605                      
17606                                  center: {
17607                                     titlebar: false,
17608                                     autoScroll:false,
17609                                     closeOnTab: true,
17610                                     tabPosition: 'top',
17611                                       //resizeTabs: true,
17612                                     alwaysShowTabs: true,
17613                                     hideTabs: false
17614                                      //minTabWidth: 140
17615                                  }
17616                              })
17617                         
17618                          };
17619                          break;
17620                     }
17621                          
17622                     if (typeof(Roo.bootstrap.Body) != 'undefined' ) {
17623                         this.parent = { el :  new  Roo.bootstrap.Body() };
17624                         Roo.debug && Roo.log("setting el to doc body");
17625                          
17626                     } else {
17627                         throw "Container is bootstrap body, but Roo.bootstrap.Body is not defined";
17628                     }
17629                     break;
17630                 case 'bootstrap':
17631                     this.parent = { el : true};
17632                     // fall through
17633                 default:
17634                     el = Roo.get(ename);
17635                     if (typeof(Roo.bootstrap) != 'undefined' && tree['|xns'] == 'Roo.bootstrap') {
17636                         this.parent = { el : true};
17637                     }
17638                     
17639                     break;
17640             }
17641                 
17642             
17643             if (!el && !this.parent) {
17644                 Roo.debug && Roo.log("Warning - element can not be found :#" + ename );
17645                 return;
17646             }
17647         }
17648         
17649         Roo.debug && Roo.log("EL:");
17650         Roo.debug && Roo.log(el);
17651         Roo.debug && Roo.log("this.parent.el:");
17652         Roo.debug && Roo.log(this.parent.el);
17653         
17654
17655         // altertive root elements ??? - we need a better way to indicate these.
17656         var is_alt = Roo.XComponent.is_alt ||
17657                     (typeof(tree.el) != 'undefined' && tree.el == document.body) ||
17658                     (typeof(Roo.bootstrap) != 'undefined' && tree.xns == Roo.bootstrap) ||
17659                     (typeof(Roo.mailer) != 'undefined' && tree.xns == Roo.mailer) ;
17660         
17661         
17662         
17663         if (!this.parent && is_alt) {
17664             //el = Roo.get(document.body);
17665             this.parent = { el : true };
17666         }
17667             
17668             
17669         
17670         if (!this.parent) {
17671             
17672             Roo.debug && Roo.log("no parent - creating one");
17673             
17674             el = el ? Roo.get(el) : false;      
17675             
17676             if (typeof(Roo.BorderLayout) == 'undefined' ) {
17677                 
17678                 this.parent =  {
17679                     el : new Roo.bootstrap.layout.Border({
17680                         el: el || document.body,
17681                     
17682                         center: {
17683                             titlebar: false,
17684                             autoScroll:false,
17685                             closeOnTab: true,
17686                             tabPosition: 'top',
17687                              //resizeTabs: true,
17688                             alwaysShowTabs: false,
17689                             hideTabs: true,
17690                             minTabWidth: 140,
17691                             overflow: 'visible'
17692                          }
17693                      })
17694                 };
17695             } else {
17696             
17697                 // it's a top level one..
17698                 this.parent =  {
17699                     el : new Roo.BorderLayout(el || document.body, {
17700                         center: {
17701                             titlebar: false,
17702                             autoScroll:false,
17703                             closeOnTab: true,
17704                             tabPosition: 'top',
17705                              //resizeTabs: true,
17706                             alwaysShowTabs: el && hp? false :  true,
17707                             hideTabs: el || !hp ? true :  false,
17708                             minTabWidth: 140
17709                          }
17710                     })
17711                 };
17712             }
17713         }
17714         
17715         if (!this.parent.el) {
17716                 // probably an old style ctor, which has been disabled.
17717                 return;
17718
17719         }
17720                 // The 'tree' method is  '_tree now' 
17721             
17722         tree.region = tree.region || this.region;
17723         var is_body = false;
17724         if (this.parent.el === true) {
17725             // bootstrap... - body..
17726             if (el) {
17727                 tree.el = el;
17728             }
17729             this.parent.el = Roo.factory(tree);
17730             is_body = true;
17731         }
17732         
17733         this.el = this.parent.el.addxtype(tree, undefined, is_body);
17734         this.fireEvent('built', this);
17735         
17736         this.panel = this.el;
17737         this.layout = this.panel.layout;
17738         this.parentLayout = this.parent.layout  || false;  
17739          
17740     }
17741     
17742 });
17743
17744 Roo.apply(Roo.XComponent, {
17745     /**
17746      * @property  hideProgress
17747      * true to disable the building progress bar.. usefull on single page renders.
17748      * @type Boolean
17749      */
17750     hideProgress : false,
17751     /**
17752      * @property  buildCompleted
17753      * True when the builder has completed building the interface.
17754      * @type Boolean
17755      */
17756     buildCompleted : false,
17757      
17758     /**
17759      * @property  topModule
17760      * the upper most module - uses document.element as it's constructor.
17761      * @type Object
17762      */
17763      
17764     topModule  : false,
17765       
17766     /**
17767      * @property  modules
17768      * array of modules to be created by registration system.
17769      * @type {Array} of Roo.XComponent
17770      */
17771     
17772     modules : [],
17773     /**
17774      * @property  elmodules
17775      * array of modules to be created by which use #ID 
17776      * @type {Array} of Roo.XComponent
17777      */
17778      
17779     elmodules : [],
17780
17781      /**
17782      * @property  is_alt
17783      * Is an alternative Root - normally used by bootstrap or other systems,
17784      *    where the top element in the tree can wrap 'body' 
17785      * @type {boolean}  (default false)
17786      */
17787      
17788     is_alt : false,
17789     /**
17790      * @property  build_from_html
17791      * Build elements from html - used by bootstrap HTML stuff 
17792      *    - this is cleared after build is completed
17793      * @type {boolean}    (default false)
17794      */
17795      
17796     build_from_html : false,
17797     /**
17798      * Register components to be built later.
17799      *
17800      * This solves the following issues
17801      * - Building is not done on page load, but after an authentication process has occured.
17802      * - Interface elements are registered on page load
17803      * - Parent Interface elements may not be loaded before child, so this handles that..
17804      * 
17805      *
17806      * example:
17807      * 
17808      * MyApp.register({
17809           order : '000001',
17810           module : 'Pman.Tab.projectMgr',
17811           region : 'center',
17812           parent : 'Pman.layout',
17813           disabled : false,  // or use a function..
17814         })
17815      
17816      * * @param {Object} details about module
17817      */
17818     register : function(obj) {
17819                 
17820         Roo.XComponent.event.fireEvent('register', obj);
17821         switch(typeof(obj.disabled) ) {
17822                 
17823             case 'undefined':
17824                 break;
17825             
17826             case 'function':
17827                 if ( obj.disabled() ) {
17828                         return;
17829                 }
17830                 break;
17831             
17832             default:
17833                 if (obj.disabled || obj.region == '#disabled') {
17834                         return;
17835                 }
17836                 break;
17837         }
17838                 
17839         this.modules.push(obj);
17840          
17841     },
17842     /**
17843      * convert a string to an object..
17844      * eg. 'AAA.BBB' -> finds AAA.BBB
17845
17846      */
17847     
17848     toObject : function(str)
17849     {
17850         if (!str || typeof(str) == 'object') {
17851             return str;
17852         }
17853         if (str.substring(0,1) == '#') {
17854             return str;
17855         }
17856
17857         var ar = str.split('.');
17858         var rt, o;
17859         rt = ar.shift();
17860             /** eval:var:o */
17861         try {
17862             eval('if (typeof ' + rt + ' == "undefined"){ o = false;} o = ' + rt + ';');
17863         } catch (e) {
17864             throw "Module not found : " + str;
17865         }
17866         
17867         if (o === false) {
17868             throw "Module not found : " + str;
17869         }
17870         Roo.each(ar, function(e) {
17871             if (typeof(o[e]) == 'undefined') {
17872                 throw "Module not found : " + str;
17873             }
17874             o = o[e];
17875         });
17876         
17877         return o;
17878         
17879     },
17880     
17881     
17882     /**
17883      * move modules into their correct place in the tree..
17884      * 
17885      */
17886     preBuild : function ()
17887     {
17888         var _t = this;
17889         Roo.each(this.modules , function (obj)
17890         {
17891             Roo.XComponent.event.fireEvent('beforebuild', obj);
17892             
17893             var opar = obj.parent;
17894             try { 
17895                 obj.parent = this.toObject(opar);
17896             } catch(e) {
17897                 Roo.debug && Roo.log("parent:toObject failed: " + e.toString());
17898                 return;
17899             }
17900             
17901             if (!obj.parent) {
17902                 Roo.debug && Roo.log("GOT top level module");
17903                 Roo.debug && Roo.log(obj);
17904                 obj.modules = new Roo.util.MixedCollection(false, 
17905                     function(o) { return o.order + '' }
17906                 );
17907                 this.topModule = obj;
17908                 return;
17909             }
17910                         // parent is a string (usually a dom element name..)
17911             if (typeof(obj.parent) == 'string') {
17912                 this.elmodules.push(obj);
17913                 return;
17914             }
17915             if (obj.parent.constructor != Roo.XComponent) {
17916                 Roo.debug && Roo.log("Warning : Object Parent is not instance of XComponent:" + obj.name)
17917             }
17918             if (!obj.parent.modules) {
17919                 obj.parent.modules = new Roo.util.MixedCollection(false, 
17920                     function(o) { return o.order + '' }
17921                 );
17922             }
17923             if (obj.parent.disabled) {
17924                 obj.disabled = true;
17925             }
17926             obj.parent.modules.add(obj);
17927         }, this);
17928     },
17929     
17930      /**
17931      * make a list of modules to build.
17932      * @return {Array} list of modules. 
17933      */ 
17934     
17935     buildOrder : function()
17936     {
17937         var _this = this;
17938         var cmp = function(a,b) {   
17939             return String(a).toUpperCase() > String(b).toUpperCase() ? 1 : -1;
17940         };
17941         if ((!this.topModule || !this.topModule.modules) && !this.elmodules.length) {
17942             throw "No top level modules to build";
17943         }
17944         
17945         // make a flat list in order of modules to build.
17946         var mods = this.topModule ? [ this.topModule ] : [];
17947                 
17948         
17949         // elmodules (is a list of DOM based modules )
17950         Roo.each(this.elmodules, function(e) {
17951             mods.push(e);
17952             if (!this.topModule &&
17953                 typeof(e.parent) == 'string' &&
17954                 e.parent.substring(0,1) == '#' &&
17955                 Roo.get(e.parent.substr(1))
17956                ) {
17957                 
17958                 _this.topModule = e;
17959             }
17960             
17961         });
17962
17963         
17964         // add modules to their parents..
17965         var addMod = function(m) {
17966             Roo.debug && Roo.log("build Order: add: " + m.name);
17967                 
17968             mods.push(m);
17969             if (m.modules && !m.disabled) {
17970                 Roo.debug && Roo.log("build Order: " + m.modules.length + " child modules");
17971                 m.modules.keySort('ASC',  cmp );
17972                 Roo.debug && Roo.log("build Order: " + m.modules.length + " child modules (after sort)");
17973     
17974                 m.modules.each(addMod);
17975             } else {
17976                 Roo.debug && Roo.log("build Order: no child modules");
17977             }
17978             // not sure if this is used any more..
17979             if (m.finalize) {
17980                 m.finalize.name = m.name + " (clean up) ";
17981                 mods.push(m.finalize);
17982             }
17983             
17984         }
17985         if (this.topModule && this.topModule.modules) { 
17986             this.topModule.modules.keySort('ASC',  cmp );
17987             this.topModule.modules.each(addMod);
17988         } 
17989         return mods;
17990     },
17991     
17992      /**
17993      * Build the registered modules.
17994      * @param {Object} parent element.
17995      * @param {Function} optional method to call after module has been added.
17996      * 
17997      */ 
17998    
17999     build : function(opts) 
18000     {
18001         
18002         if (typeof(opts) != 'undefined') {
18003             Roo.apply(this,opts);
18004         }
18005         
18006         this.preBuild();
18007         var mods = this.buildOrder();
18008       
18009         //this.allmods = mods;
18010         //Roo.debug && Roo.log(mods);
18011         //return;
18012         if (!mods.length) { // should not happen
18013             throw "NO modules!!!";
18014         }
18015         
18016         
18017         var msg = "Building Interface...";
18018         // flash it up as modal - so we store the mask!?
18019         if (!this.hideProgress && Roo.MessageBox) {
18020             Roo.MessageBox.show({ title: 'loading' });
18021             Roo.MessageBox.show({
18022                title: "Please wait...",
18023                msg: msg,
18024                width:450,
18025                progress:true,
18026                buttons : false,
18027                closable:false,
18028                modal: false
18029               
18030             });
18031         }
18032         var total = mods.length;
18033         
18034         var _this = this;
18035         var progressRun = function() {
18036             if (!mods.length) {
18037                 Roo.debug && Roo.log('hide?');
18038                 if (!this.hideProgress && Roo.MessageBox) {
18039                     Roo.MessageBox.hide();
18040                 }
18041                 Roo.XComponent.build_from_html = false; // reset, so dialogs will be build from javascript
18042                 
18043                 Roo.XComponent.event.fireEvent('buildcomplete', _this.topModule);
18044                 
18045                 // THE END...
18046                 return false;   
18047             }
18048             
18049             var m = mods.shift();
18050             
18051             
18052             Roo.debug && Roo.log(m);
18053             // not sure if this is supported any more.. - modules that are are just function
18054             if (typeof(m) == 'function') { 
18055                 m.call(this);
18056                 return progressRun.defer(10, _this);
18057             } 
18058             
18059             
18060             msg = "Building Interface " + (total  - mods.length) + 
18061                     " of " + total + 
18062                     (m.name ? (' - ' + m.name) : '');
18063                         Roo.debug && Roo.log(msg);
18064             if (!_this.hideProgress &&  Roo.MessageBox) { 
18065                 Roo.MessageBox.updateProgress(  (total  - mods.length)/total, msg  );
18066             }
18067             
18068          
18069             // is the module disabled?
18070             var disabled = (typeof(m.disabled) == 'function') ?
18071                 m.disabled.call(m.module.disabled) : m.disabled;    
18072             
18073             
18074             if (disabled) {
18075                 return progressRun(); // we do not update the display!
18076             }
18077             
18078             // now build 
18079             
18080                         
18081                         
18082             m.render();
18083             // it's 10 on top level, and 1 on others??? why...
18084             return progressRun.defer(10, _this);
18085              
18086         }
18087         progressRun.defer(1, _this);
18088      
18089         
18090         
18091     },
18092     /**
18093      * Overlay a set of modified strings onto a component
18094      * This is dependant on our builder exporting the strings and 'named strings' elements.
18095      * 
18096      * @param {Object} element to overlay on - eg. Pman.Dialog.Login
18097      * @param {Object} associative array of 'named' string and it's new value.
18098      * 
18099      */
18100         overlayStrings : function( component, strings )
18101     {
18102         if (typeof(component['_named_strings']) == 'undefined') {
18103             throw "ERROR: component does not have _named_strings";
18104         }
18105         for ( var k in strings ) {
18106             var md = typeof(component['_named_strings'][k]) == 'undefined' ? false : component['_named_strings'][k];
18107             if (md !== false) {
18108                 component['_strings'][md] = strings[k];
18109             } else {
18110                 Roo.log('could not find named string: ' + k + ' in');
18111                 Roo.log(component);
18112             }
18113             
18114         }
18115         
18116     },
18117     
18118         
18119         /**
18120          * Event Object.
18121          *
18122          *
18123          */
18124         event: false, 
18125     /**
18126          * wrapper for event.on - aliased later..  
18127          * Typically use to register a event handler for register:
18128          *
18129          * eg. Roo.XComponent.on('register', function(comp) { comp.disable = true } );
18130          *
18131          */
18132     on : false
18133    
18134     
18135     
18136 });
18137
18138 Roo.XComponent.event = new Roo.util.Observable({
18139                 events : { 
18140                         /**
18141                          * @event register
18142                          * Fires when an Component is registered,
18143                          * set the disable property on the Component to stop registration.
18144                          * @param {Roo.XComponent} c the component being registerd.
18145                          * 
18146                          */
18147                         'register' : true,
18148             /**
18149                          * @event beforebuild
18150                          * Fires before each Component is built
18151                          * can be used to apply permissions.
18152                          * @param {Roo.XComponent} c the component being registerd.
18153                          * 
18154                          */
18155                         'beforebuild' : true,
18156                         /**
18157                          * @event buildcomplete
18158                          * Fires on the top level element when all elements have been built
18159                          * @param {Roo.XComponent} the top level component.
18160                          */
18161                         'buildcomplete' : true
18162                         
18163                 }
18164 });
18165
18166 Roo.XComponent.on = Roo.XComponent.event.on.createDelegate(Roo.XComponent.event); 
18167  //
18168  /**
18169  * marked - a markdown parser
18170  * Copyright (c) 2011-2014, Christopher Jeffrey. (MIT Licensed)
18171  * https://github.com/chjj/marked
18172  */
18173
18174
18175 /**
18176  *
18177  * Roo.Markdown - is a very crude wrapper around marked..
18178  *
18179  * usage:
18180  * 
18181  * alert( Roo.Markdown.toHtml("Markdown *rocks*.") );
18182  * 
18183  * Note: move the sample code to the bottom of this
18184  * file before uncommenting it.
18185  *
18186  */
18187
18188 Roo.Markdown = {};
18189 Roo.Markdown.toHtml = function(text) {
18190     
18191     var c = new Roo.Markdown.marked.setOptions({
18192             renderer: new Roo.Markdown.marked.Renderer(),
18193             gfm: true,
18194             tables: true,
18195             breaks: false,
18196             pedantic: false,
18197             sanitize: false,
18198             smartLists: true,
18199             smartypants: false
18200           });
18201     // A FEW HACKS!!?
18202     
18203     text = text.replace(/\\\n/g,' ');
18204     return Roo.Markdown.marked(text);
18205 };
18206 //
18207 // converter
18208 //
18209 // Wraps all "globals" so that the only thing
18210 // exposed is makeHtml().
18211 //
18212 (function() {
18213     
18214      /**
18215          * eval:var:escape
18216          * eval:var:unescape
18217          * eval:var:replace
18218          */
18219       
18220     /**
18221      * Helpers
18222      */
18223     
18224     var escape = function (html, encode) {
18225       return html
18226         .replace(!encode ? /&(?!#?\w+;)/g : /&/g, '&amp;')
18227         .replace(/</g, '&lt;')
18228         .replace(/>/g, '&gt;')
18229         .replace(/"/g, '&quot;')
18230         .replace(/'/g, '&#39;');
18231     }
18232     
18233     var unescape = function (html) {
18234         // explicitly match decimal, hex, and named HTML entities 
18235       return html.replace(/&(#(?:\d+)|(?:#x[0-9A-Fa-f]+)|(?:\w+));?/g, function(_, n) {
18236         n = n.toLowerCase();
18237         if (n === 'colon') { return ':'; }
18238         if (n.charAt(0) === '#') {
18239           return n.charAt(1) === 'x'
18240             ? String.fromCharCode(parseInt(n.substring(2), 16))
18241             : String.fromCharCode(+n.substring(1));
18242         }
18243         return '';
18244       });
18245     }
18246     
18247     var replace = function (regex, opt) {
18248       regex = regex.source;
18249       opt = opt || '';
18250       return function self(name, val) {
18251         if (!name) { return new RegExp(regex, opt); }
18252         val = val.source || val;
18253         val = val.replace(/(^|[^\[])\^/g, '$1');
18254         regex = regex.replace(name, val);
18255         return self;
18256       };
18257     }
18258
18259
18260          /**
18261          * eval:var:noop
18262     */
18263     var noop = function () {}
18264     noop.exec = noop;
18265     
18266          /**
18267          * eval:var:merge
18268     */
18269     var merge = function (obj) {
18270       var i = 1
18271         , target
18272         , key;
18273     
18274       for (; i < arguments.length; i++) {
18275         target = arguments[i];
18276         for (key in target) {
18277           if (Object.prototype.hasOwnProperty.call(target, key)) {
18278             obj[key] = target[key];
18279           }
18280         }
18281       }
18282     
18283       return obj;
18284     }
18285     
18286     
18287     /**
18288      * Block-Level Grammar
18289      */
18290     
18291     
18292     
18293     
18294     var block = {
18295       newline: /^\n+/,
18296       code: /^( {4}[^\n]+\n*)+/,
18297       fences: noop,
18298       hr: /^( *[-*_]){3,} *(?:\n+|$)/,
18299       heading: /^ *(#{1,6}) *([^\n]+?) *#* *(?:\n+|$)/,
18300       nptable: noop,
18301       lheading: /^([^\n]+)\n *(=|-){2,} *(?:\n+|$)/,
18302       blockquote: /^( *>[^\n]+(\n(?!def)[^\n]+)*\n*)+/,
18303       list: /^( *)(bull) [\s\S]+?(?:hr|def|\n{2,}(?! )(?!\1bull )\n*|\s*$)/,
18304       html: /^ *(?:comment *(?:\n|\s*$)|closed *(?:\n{2,}|\s*$)|closing *(?:\n{2,}|\s*$))/,
18305       def: /^ *\[([^\]]+)\]: *<?([^\s>]+)>?(?: +["(]([^\n]+)[")])? *(?:\n+|$)/,
18306       table: noop,
18307       paragraph: /^((?:[^\n]+\n?(?!hr|heading|lheading|blockquote|tag|def))+)\n*/,
18308       text: /^[^\n]+/
18309     };
18310     
18311     block.bullet = /(?:[*+-]|\d+\.)/;
18312     block.item = /^( *)(bull) [^\n]*(?:\n(?!\1bull )[^\n]*)*/;
18313     block.item = replace(block.item, 'gm')
18314       (/bull/g, block.bullet)
18315       ();
18316     
18317     block.list = replace(block.list)
18318       (/bull/g, block.bullet)
18319       ('hr', '\\n+(?=\\1?(?:[-*_] *){3,}(?:\\n+|$))')
18320       ('def', '\\n+(?=' + block.def.source + ')')
18321       ();
18322     
18323     block.blockquote = replace(block.blockquote)
18324       ('def', block.def)
18325       ();
18326     
18327     block._tag = '(?!(?:'
18328       + 'a|em|strong|small|s|cite|q|dfn|abbr|data|time|code'
18329       + '|var|samp|kbd|sub|sup|i|b|u|mark|ruby|rt|rp|bdi|bdo'
18330       + '|span|br|wbr|ins|del|img)\\b)\\w+(?!:/|[^\\w\\s@]*@)\\b';
18331     
18332     block.html = replace(block.html)
18333       ('comment', /<!--[\s\S]*?-->/)
18334       ('closed', /<(tag)[\s\S]+?<\/\1>/)
18335       ('closing', /<tag(?:"[^"]*"|'[^']*'|[^'">])*?>/)
18336       (/tag/g, block._tag)
18337       ();
18338     
18339     block.paragraph = replace(block.paragraph)
18340       ('hr', block.hr)
18341       ('heading', block.heading)
18342       ('lheading', block.lheading)
18343       ('blockquote', block.blockquote)
18344       ('tag', '<' + block._tag)
18345       ('def', block.def)
18346       ();
18347     
18348     /**
18349      * Normal Block Grammar
18350      */
18351     
18352     block.normal = merge({}, block);
18353     
18354     /**
18355      * GFM Block Grammar
18356      */
18357     
18358     block.gfm = merge({}, block.normal, {
18359       fences: /^ *(`{3,}|~{3,})[ \.]*(\S+)? *\n([\s\S]*?)\s*\1 *(?:\n+|$)/,
18360       paragraph: /^/,
18361       heading: /^ *(#{1,6}) +([^\n]+?) *#* *(?:\n+|$)/
18362     });
18363     
18364     block.gfm.paragraph = replace(block.paragraph)
18365       ('(?!', '(?!'
18366         + block.gfm.fences.source.replace('\\1', '\\2') + '|'
18367         + block.list.source.replace('\\1', '\\3') + '|')
18368       ();
18369     
18370     /**
18371      * GFM + Tables Block Grammar
18372      */
18373     
18374     block.tables = merge({}, block.gfm, {
18375       nptable: /^ *(\S.*\|.*)\n *([-:]+ *\|[-| :]*)\n((?:.*\|.*(?:\n|$))*)\n*/,
18376       table: /^ *\|(.+)\n *\|( *[-:]+[-| :]*)\n((?: *\|.*(?:\n|$))*)\n*/
18377     });
18378     
18379     /**
18380      * Block Lexer
18381      */
18382     
18383     var Lexer = function (options) {
18384       this.tokens = [];
18385       this.tokens.links = {};
18386       this.options = options || marked.defaults;
18387       this.rules = block.normal;
18388     
18389       if (this.options.gfm) {
18390         if (this.options.tables) {
18391           this.rules = block.tables;
18392         } else {
18393           this.rules = block.gfm;
18394         }
18395       }
18396     }
18397     
18398     /**
18399      * Expose Block Rules
18400      */
18401     
18402     Lexer.rules = block;
18403     
18404     /**
18405      * Static Lex Method
18406      */
18407     
18408     Lexer.lex = function(src, options) {
18409       var lexer = new Lexer(options);
18410       return lexer.lex(src);
18411     };
18412     
18413     /**
18414      * Preprocessing
18415      */
18416     
18417     Lexer.prototype.lex = function(src) {
18418       src = src
18419         .replace(/\r\n|\r/g, '\n')
18420         .replace(/\t/g, '    ')
18421         .replace(/\u00a0/g, ' ')
18422         .replace(/\u2424/g, '\n');
18423     
18424       return this.token(src, true);
18425     };
18426     
18427     /**
18428      * Lexing
18429      */
18430     
18431     Lexer.prototype.token = function(src, top, bq) {
18432       var src = src.replace(/^ +$/gm, '')
18433         , next
18434         , loose
18435         , cap
18436         , bull
18437         , b
18438         , item
18439         , space
18440         , i
18441         , l;
18442     
18443       while (src) {
18444         // newline
18445         if (cap = this.rules.newline.exec(src)) {
18446           src = src.substring(cap[0].length);
18447           if (cap[0].length > 1) {
18448             this.tokens.push({
18449               type: 'space'
18450             });
18451           }
18452         }
18453     
18454         // code
18455         if (cap = this.rules.code.exec(src)) {
18456           src = src.substring(cap[0].length);
18457           cap = cap[0].replace(/^ {4}/gm, '');
18458           this.tokens.push({
18459             type: 'code',
18460             text: !this.options.pedantic
18461               ? cap.replace(/\n+$/, '')
18462               : cap
18463           });
18464           continue;
18465         }
18466     
18467         // fences (gfm)
18468         if (cap = this.rules.fences.exec(src)) {
18469           src = src.substring(cap[0].length);
18470           this.tokens.push({
18471             type: 'code',
18472             lang: cap[2],
18473             text: cap[3] || ''
18474           });
18475           continue;
18476         }
18477     
18478         // heading
18479         if (cap = this.rules.heading.exec(src)) {
18480           src = src.substring(cap[0].length);
18481           this.tokens.push({
18482             type: 'heading',
18483             depth: cap[1].length,
18484             text: cap[2]
18485           });
18486           continue;
18487         }
18488     
18489         // table no leading pipe (gfm)
18490         if (top && (cap = this.rules.nptable.exec(src))) {
18491           src = src.substring(cap[0].length);
18492     
18493           item = {
18494             type: 'table',
18495             header: cap[1].replace(/^ *| *\| *$/g, '').split(/ *\| */),
18496             align: cap[2].replace(/^ *|\| *$/g, '').split(/ *\| */),
18497             cells: cap[3].replace(/\n$/, '').split('\n')
18498           };
18499     
18500           for (i = 0; i < item.align.length; i++) {
18501             if (/^ *-+: *$/.test(item.align[i])) {
18502               item.align[i] = 'right';
18503             } else if (/^ *:-+: *$/.test(item.align[i])) {
18504               item.align[i] = 'center';
18505             } else if (/^ *:-+ *$/.test(item.align[i])) {
18506               item.align[i] = 'left';
18507             } else {
18508               item.align[i] = null;
18509             }
18510           }
18511     
18512           for (i = 0; i < item.cells.length; i++) {
18513             item.cells[i] = item.cells[i].split(/ *\| */);
18514           }
18515     
18516           this.tokens.push(item);
18517     
18518           continue;
18519         }
18520     
18521         // lheading
18522         if (cap = this.rules.lheading.exec(src)) {
18523           src = src.substring(cap[0].length);
18524           this.tokens.push({
18525             type: 'heading',
18526             depth: cap[2] === '=' ? 1 : 2,
18527             text: cap[1]
18528           });
18529           continue;
18530         }
18531     
18532         // hr
18533         if (cap = this.rules.hr.exec(src)) {
18534           src = src.substring(cap[0].length);
18535           this.tokens.push({
18536             type: 'hr'
18537           });
18538           continue;
18539         }
18540     
18541         // blockquote
18542         if (cap = this.rules.blockquote.exec(src)) {
18543           src = src.substring(cap[0].length);
18544     
18545           this.tokens.push({
18546             type: 'blockquote_start'
18547           });
18548     
18549           cap = cap[0].replace(/^ *> ?/gm, '');
18550     
18551           // Pass `top` to keep the current
18552           // "toplevel" state. This is exactly
18553           // how markdown.pl works.
18554           this.token(cap, top, true);
18555     
18556           this.tokens.push({
18557             type: 'blockquote_end'
18558           });
18559     
18560           continue;
18561         }
18562     
18563         // list
18564         if (cap = this.rules.list.exec(src)) {
18565           src = src.substring(cap[0].length);
18566           bull = cap[2];
18567     
18568           this.tokens.push({
18569             type: 'list_start',
18570             ordered: bull.length > 1
18571           });
18572     
18573           // Get each top-level item.
18574           cap = cap[0].match(this.rules.item);
18575     
18576           next = false;
18577           l = cap.length;
18578           i = 0;
18579     
18580           for (; i < l; i++) {
18581             item = cap[i];
18582     
18583             // Remove the list item's bullet
18584             // so it is seen as the next token.
18585             space = item.length;
18586             item = item.replace(/^ *([*+-]|\d+\.) +/, '');
18587     
18588             // Outdent whatever the
18589             // list item contains. Hacky.
18590             if (~item.indexOf('\n ')) {
18591               space -= item.length;
18592               item = !this.options.pedantic
18593                 ? item.replace(new RegExp('^ {1,' + space + '}', 'gm'), '')
18594                 : item.replace(/^ {1,4}/gm, '');
18595             }
18596     
18597             // Determine whether the next list item belongs here.
18598             // Backpedal if it does not belong in this list.
18599             if (this.options.smartLists && i !== l - 1) {
18600               b = block.bullet.exec(cap[i + 1])[0];
18601               if (bull !== b && !(bull.length > 1 && b.length > 1)) {
18602                 src = cap.slice(i + 1).join('\n') + src;
18603                 i = l - 1;
18604               }
18605             }
18606     
18607             // Determine whether item is loose or not.
18608             // Use: /(^|\n)(?! )[^\n]+\n\n(?!\s*$)/
18609             // for discount behavior.
18610             loose = next || /\n\n(?!\s*$)/.test(item);
18611             if (i !== l - 1) {
18612               next = item.charAt(item.length - 1) === '\n';
18613               if (!loose) { loose = next; }
18614             }
18615     
18616             this.tokens.push({
18617               type: loose
18618                 ? 'loose_item_start'
18619                 : 'list_item_start'
18620             });
18621     
18622             // Recurse.
18623             this.token(item, false, bq);
18624     
18625             this.tokens.push({
18626               type: 'list_item_end'
18627             });
18628           }
18629     
18630           this.tokens.push({
18631             type: 'list_end'
18632           });
18633     
18634           continue;
18635         }
18636     
18637         // html
18638         if (cap = this.rules.html.exec(src)) {
18639           src = src.substring(cap[0].length);
18640           this.tokens.push({
18641             type: this.options.sanitize
18642               ? 'paragraph'
18643               : 'html',
18644             pre: !this.options.sanitizer
18645               && (cap[1] === 'pre' || cap[1] === 'script' || cap[1] === 'style'),
18646             text: cap[0]
18647           });
18648           continue;
18649         }
18650     
18651         // def
18652         if ((!bq && top) && (cap = this.rules.def.exec(src))) {
18653           src = src.substring(cap[0].length);
18654           this.tokens.links[cap[1].toLowerCase()] = {
18655             href: cap[2],
18656             title: cap[3]
18657           };
18658           continue;
18659         }
18660     
18661         // table (gfm)
18662         if (top && (cap = this.rules.table.exec(src))) {
18663           src = src.substring(cap[0].length);
18664     
18665           item = {
18666             type: 'table',
18667             header: cap[1].replace(/^ *| *\| *$/g, '').split(/ *\| */),
18668             align: cap[2].replace(/^ *|\| *$/g, '').split(/ *\| */),
18669             cells: cap[3].replace(/(?: *\| *)?\n$/, '').split('\n')
18670           };
18671     
18672           for (i = 0; i < item.align.length; i++) {
18673             if (/^ *-+: *$/.test(item.align[i])) {
18674               item.align[i] = 'right';
18675             } else if (/^ *:-+: *$/.test(item.align[i])) {
18676               item.align[i] = 'center';
18677             } else if (/^ *:-+ *$/.test(item.align[i])) {
18678               item.align[i] = 'left';
18679             } else {
18680               item.align[i] = null;
18681             }
18682           }
18683     
18684           for (i = 0; i < item.cells.length; i++) {
18685             item.cells[i] = item.cells[i]
18686               .replace(/^ *\| *| *\| *$/g, '')
18687               .split(/ *\| */);
18688           }
18689     
18690           this.tokens.push(item);
18691     
18692           continue;
18693         }
18694     
18695         // top-level paragraph
18696         if (top && (cap = this.rules.paragraph.exec(src))) {
18697           src = src.substring(cap[0].length);
18698           this.tokens.push({
18699             type: 'paragraph',
18700             text: cap[1].charAt(cap[1].length - 1) === '\n'
18701               ? cap[1].slice(0, -1)
18702               : cap[1]
18703           });
18704           continue;
18705         }
18706     
18707         // text
18708         if (cap = this.rules.text.exec(src)) {
18709           // Top-level should never reach here.
18710           src = src.substring(cap[0].length);
18711           this.tokens.push({
18712             type: 'text',
18713             text: cap[0]
18714           });
18715           continue;
18716         }
18717     
18718         if (src) {
18719           throw new
18720             Error('Infinite loop on byte: ' + src.charCodeAt(0));
18721         }
18722       }
18723     
18724       return this.tokens;
18725     };
18726     
18727     /**
18728      * Inline-Level Grammar
18729      */
18730     
18731     var inline = {
18732       escape: /^\\([\\`*{}\[\]()#+\-.!_>])/,
18733       autolink: /^<([^ >]+(@|:\/)[^ >]+)>/,
18734       url: noop,
18735       tag: /^<!--[\s\S]*?-->|^<\/?\w+(?:"[^"]*"|'[^']*'|[^'">])*?>/,
18736       link: /^!?\[(inside)\]\(href\)/,
18737       reflink: /^!?\[(inside)\]\s*\[([^\]]*)\]/,
18738       nolink: /^!?\[((?:\[[^\]]*\]|[^\[\]])*)\]/,
18739       strong: /^__([\s\S]+?)__(?!_)|^\*\*([\s\S]+?)\*\*(?!\*)/,
18740       em: /^\b_((?:[^_]|__)+?)_\b|^\*((?:\*\*|[\s\S])+?)\*(?!\*)/,
18741       code: /^(`+)\s*([\s\S]*?[^`])\s*\1(?!`)/,
18742       br: /^ {2,}\n(?!\s*$)/,
18743       del: noop,
18744       text: /^[\s\S]+?(?=[\\<!\[_*`]| {2,}\n|$)/
18745     };
18746     
18747     inline._inside = /(?:\[[^\]]*\]|[^\[\]]|\](?=[^\[]*\]))*/;
18748     inline._href = /\s*<?([\s\S]*?)>?(?:\s+['"]([\s\S]*?)['"])?\s*/;
18749     
18750     inline.link = replace(inline.link)
18751       ('inside', inline._inside)
18752       ('href', inline._href)
18753       ();
18754     
18755     inline.reflink = replace(inline.reflink)
18756       ('inside', inline._inside)
18757       ();
18758     
18759     /**
18760      * Normal Inline Grammar
18761      */
18762     
18763     inline.normal = merge({}, inline);
18764     
18765     /**
18766      * Pedantic Inline Grammar
18767      */
18768     
18769     inline.pedantic = merge({}, inline.normal, {
18770       strong: /^__(?=\S)([\s\S]*?\S)__(?!_)|^\*\*(?=\S)([\s\S]*?\S)\*\*(?!\*)/,
18771       em: /^_(?=\S)([\s\S]*?\S)_(?!_)|^\*(?=\S)([\s\S]*?\S)\*(?!\*)/
18772     });
18773     
18774     /**
18775      * GFM Inline Grammar
18776      */
18777     
18778     inline.gfm = merge({}, inline.normal, {
18779       escape: replace(inline.escape)('])', '~|])')(),
18780       url: /^(https?:\/\/[^\s<]+[^<.,:;"')\]\s])/,
18781       del: /^~~(?=\S)([\s\S]*?\S)~~/,
18782       text: replace(inline.text)
18783         (']|', '~]|')
18784         ('|', '|https?://|')
18785         ()
18786     });
18787     
18788     /**
18789      * GFM + Line Breaks Inline Grammar
18790      */
18791     
18792     inline.breaks = merge({}, inline.gfm, {
18793       br: replace(inline.br)('{2,}', '*')(),
18794       text: replace(inline.gfm.text)('{2,}', '*')()
18795     });
18796     
18797     /**
18798      * Inline Lexer & Compiler
18799      */
18800     
18801     var InlineLexer  = function (links, options) {
18802       this.options = options || marked.defaults;
18803       this.links = links;
18804       this.rules = inline.normal;
18805       this.renderer = this.options.renderer || new Renderer;
18806       this.renderer.options = this.options;
18807     
18808       if (!this.links) {
18809         throw new
18810           Error('Tokens array requires a `links` property.');
18811       }
18812     
18813       if (this.options.gfm) {
18814         if (this.options.breaks) {
18815           this.rules = inline.breaks;
18816         } else {
18817           this.rules = inline.gfm;
18818         }
18819       } else if (this.options.pedantic) {
18820         this.rules = inline.pedantic;
18821       }
18822     }
18823     
18824     /**
18825      * Expose Inline Rules
18826      */
18827     
18828     InlineLexer.rules = inline;
18829     
18830     /**
18831      * Static Lexing/Compiling Method
18832      */
18833     
18834     InlineLexer.output = function(src, links, options) {
18835       var inline = new InlineLexer(links, options);
18836       return inline.output(src);
18837     };
18838     
18839     /**
18840      * Lexing/Compiling
18841      */
18842     
18843     InlineLexer.prototype.output = function(src) {
18844       var out = ''
18845         , link
18846         , text
18847         , href
18848         , cap;
18849     
18850       while (src) {
18851         // escape
18852         if (cap = this.rules.escape.exec(src)) {
18853           src = src.substring(cap[0].length);
18854           out += cap[1];
18855           continue;
18856         }
18857     
18858         // autolink
18859         if (cap = this.rules.autolink.exec(src)) {
18860           src = src.substring(cap[0].length);
18861           if (cap[2] === '@') {
18862             text = cap[1].charAt(6) === ':'
18863               ? this.mangle(cap[1].substring(7))
18864               : this.mangle(cap[1]);
18865             href = this.mangle('mailto:') + text;
18866           } else {
18867             text = escape(cap[1]);
18868             href = text;
18869           }
18870           out += this.renderer.link(href, null, text);
18871           continue;
18872         }
18873     
18874         // url (gfm)
18875         if (!this.inLink && (cap = this.rules.url.exec(src))) {
18876           src = src.substring(cap[0].length);
18877           text = escape(cap[1]);
18878           href = text;
18879           out += this.renderer.link(href, null, text);
18880           continue;
18881         }
18882     
18883         // tag
18884         if (cap = this.rules.tag.exec(src)) {
18885           if (!this.inLink && /^<a /i.test(cap[0])) {
18886             this.inLink = true;
18887           } else if (this.inLink && /^<\/a>/i.test(cap[0])) {
18888             this.inLink = false;
18889           }
18890           src = src.substring(cap[0].length);
18891           out += this.options.sanitize
18892             ? this.options.sanitizer
18893               ? this.options.sanitizer(cap[0])
18894               : escape(cap[0])
18895             : cap[0];
18896           continue;
18897         }
18898     
18899         // link
18900         if (cap = this.rules.link.exec(src)) {
18901           src = src.substring(cap[0].length);
18902           this.inLink = true;
18903           out += this.outputLink(cap, {
18904             href: cap[2],
18905             title: cap[3]
18906           });
18907           this.inLink = false;
18908           continue;
18909         }
18910     
18911         // reflink, nolink
18912         if ((cap = this.rules.reflink.exec(src))
18913             || (cap = this.rules.nolink.exec(src))) {
18914           src = src.substring(cap[0].length);
18915           link = (cap[2] || cap[1]).replace(/\s+/g, ' ');
18916           link = this.links[link.toLowerCase()];
18917           if (!link || !link.href) {
18918             out += cap[0].charAt(0);
18919             src = cap[0].substring(1) + src;
18920             continue;
18921           }
18922           this.inLink = true;
18923           out += this.outputLink(cap, link);
18924           this.inLink = false;
18925           continue;
18926         }
18927     
18928         // strong
18929         if (cap = this.rules.strong.exec(src)) {
18930           src = src.substring(cap[0].length);
18931           out += this.renderer.strong(this.output(cap[2] || cap[1]));
18932           continue;
18933         }
18934     
18935         // em
18936         if (cap = this.rules.em.exec(src)) {
18937           src = src.substring(cap[0].length);
18938           out += this.renderer.em(this.output(cap[2] || cap[1]));
18939           continue;
18940         }
18941     
18942         // code
18943         if (cap = this.rules.code.exec(src)) {
18944           src = src.substring(cap[0].length);
18945           out += this.renderer.codespan(escape(cap[2], true));
18946           continue;
18947         }
18948     
18949         // br
18950         if (cap = this.rules.br.exec(src)) {
18951           src = src.substring(cap[0].length);
18952           out += this.renderer.br();
18953           continue;
18954         }
18955     
18956         // del (gfm)
18957         if (cap = this.rules.del.exec(src)) {
18958           src = src.substring(cap[0].length);
18959           out += this.renderer.del(this.output(cap[1]));
18960           continue;
18961         }
18962     
18963         // text
18964         if (cap = this.rules.text.exec(src)) {
18965           src = src.substring(cap[0].length);
18966           out += this.renderer.text(escape(this.smartypants(cap[0])));
18967           continue;
18968         }
18969     
18970         if (src) {
18971           throw new
18972             Error('Infinite loop on byte: ' + src.charCodeAt(0));
18973         }
18974       }
18975     
18976       return out;
18977     };
18978     
18979     /**
18980      * Compile Link
18981      */
18982     
18983     InlineLexer.prototype.outputLink = function(cap, link) {
18984       var href = escape(link.href)
18985         , title = link.title ? escape(link.title) : null;
18986     
18987       return cap[0].charAt(0) !== '!'
18988         ? this.renderer.link(href, title, this.output(cap[1]))
18989         : this.renderer.image(href, title, escape(cap[1]));
18990     };
18991     
18992     /**
18993      * Smartypants Transformations
18994      */
18995     
18996     InlineLexer.prototype.smartypants = function(text) {
18997       if (!this.options.smartypants)  { return text; }
18998       return text
18999         // em-dashes
19000         .replace(/---/g, '\u2014')
19001         // en-dashes
19002         .replace(/--/g, '\u2013')
19003         // opening singles
19004         .replace(/(^|[-\u2014/(\[{"\s])'/g, '$1\u2018')
19005         // closing singles & apostrophes
19006         .replace(/'/g, '\u2019')
19007         // opening doubles
19008         .replace(/(^|[-\u2014/(\[{\u2018\s])"/g, '$1\u201c')
19009         // closing doubles
19010         .replace(/"/g, '\u201d')
19011         // ellipses
19012         .replace(/\.{3}/g, '\u2026');
19013     };
19014     
19015     /**
19016      * Mangle Links
19017      */
19018     
19019     InlineLexer.prototype.mangle = function(text) {
19020       if (!this.options.mangle) { return text; }
19021       var out = ''
19022         , l = text.length
19023         , i = 0
19024         , ch;
19025     
19026       for (; i < l; i++) {
19027         ch = text.charCodeAt(i);
19028         if (Math.random() > 0.5) {
19029           ch = 'x' + ch.toString(16);
19030         }
19031         out += '&#' + ch + ';';
19032       }
19033     
19034       return out;
19035     };
19036     
19037     /**
19038      * Renderer
19039      */
19040     
19041      /**
19042          * eval:var:Renderer
19043     */
19044     
19045     var Renderer   = function (options) {
19046       this.options = options || {};
19047     }
19048     
19049     Renderer.prototype.code = function(code, lang, escaped) {
19050       if (this.options.highlight) {
19051         var out = this.options.highlight(code, lang);
19052         if (out != null && out !== code) {
19053           escaped = true;
19054           code = out;
19055         }
19056       } else {
19057             // hack!!! - it's already escapeD?
19058             escaped = true;
19059       }
19060     
19061       if (!lang) {
19062         return '<pre><code>'
19063           + (escaped ? code : escape(code, true))
19064           + '\n</code></pre>';
19065       }
19066     
19067       return '<pre><code class="'
19068         + this.options.langPrefix
19069         + escape(lang, true)
19070         + '">'
19071         + (escaped ? code : escape(code, true))
19072         + '\n</code></pre>\n';
19073     };
19074     
19075     Renderer.prototype.blockquote = function(quote) {
19076       return '<blockquote>\n' + quote + '</blockquote>\n';
19077     };
19078     
19079     Renderer.prototype.html = function(html) {
19080       return html;
19081     };
19082     
19083     Renderer.prototype.heading = function(text, level, raw) {
19084       return '<h'
19085         + level
19086         + ' id="'
19087         + this.options.headerPrefix
19088         + raw.toLowerCase().replace(/[^\w]+/g, '-')
19089         + '">'
19090         + text
19091         + '</h'
19092         + level
19093         + '>\n';
19094     };
19095     
19096     Renderer.prototype.hr = function() {
19097       return this.options.xhtml ? '<hr/>\n' : '<hr>\n';
19098     };
19099     
19100     Renderer.prototype.list = function(body, ordered) {
19101       var type = ordered ? 'ol' : 'ul';
19102       return '<' + type + '>\n' + body + '</' + type + '>\n';
19103     };
19104     
19105     Renderer.prototype.listitem = function(text) {
19106       return '<li>' + text + '</li>\n';
19107     };
19108     
19109     Renderer.prototype.paragraph = function(text) {
19110       return '<p>' + text + '</p>\n';
19111     };
19112     
19113     Renderer.prototype.table = function(header, body) {
19114       return '<table class="table table-striped">\n'
19115         + '<thead>\n'
19116         + header
19117         + '</thead>\n'
19118         + '<tbody>\n'
19119         + body
19120         + '</tbody>\n'
19121         + '</table>\n';
19122     };
19123     
19124     Renderer.prototype.tablerow = function(content) {
19125       return '<tr>\n' + content + '</tr>\n';
19126     };
19127     
19128     Renderer.prototype.tablecell = function(content, flags) {
19129       var type = flags.header ? 'th' : 'td';
19130       var tag = flags.align
19131         ? '<' + type + ' style="text-align:' + flags.align + '">'
19132         : '<' + type + '>';
19133       return tag + content + '</' + type + '>\n';
19134     };
19135     
19136     // span level renderer
19137     Renderer.prototype.strong = function(text) {
19138       return '<strong>' + text + '</strong>';
19139     };
19140     
19141     Renderer.prototype.em = function(text) {
19142       return '<em>' + text + '</em>';
19143     };
19144     
19145     Renderer.prototype.codespan = function(text) {
19146       return '<code>' + text + '</code>';
19147     };
19148     
19149     Renderer.prototype.br = function() {
19150       return this.options.xhtml ? '<br/>' : '<br>';
19151     };
19152     
19153     Renderer.prototype.del = function(text) {
19154       return '<del>' + text + '</del>';
19155     };
19156     
19157     Renderer.prototype.link = function(href, title, text) {
19158       if (this.options.sanitize) {
19159         try {
19160           var prot = decodeURIComponent(unescape(href))
19161             .replace(/[^\w:]/g, '')
19162             .toLowerCase();
19163         } catch (e) {
19164           return '';
19165         }
19166         if (prot.indexOf('javascript:') === 0 || prot.indexOf('vbscript:') === 0) {
19167           return '';
19168         }
19169       }
19170       var out = '<a href="' + href + '"';
19171       if (title) {
19172         out += ' title="' + title + '"';
19173       }
19174       out += '>' + text + '</a>';
19175       return out;
19176     };
19177     
19178     Renderer.prototype.image = function(href, title, text) {
19179       var out = '<img src="' + href + '" alt="' + text + '"';
19180       if (title) {
19181         out += ' title="' + title + '"';
19182       }
19183       out += this.options.xhtml ? '/>' : '>';
19184       return out;
19185     };
19186     
19187     Renderer.prototype.text = function(text) {
19188       return text;
19189     };
19190     
19191     /**
19192      * Parsing & Compiling
19193      */
19194          /**
19195          * eval:var:Parser
19196     */
19197     
19198     var Parser= function (options) {
19199       this.tokens = [];
19200       this.token = null;
19201       this.options = options || marked.defaults;
19202       this.options.renderer = this.options.renderer || new Renderer;
19203       this.renderer = this.options.renderer;
19204       this.renderer.options = this.options;
19205     }
19206     
19207     /**
19208      * Static Parse Method
19209      */
19210     
19211     Parser.parse = function(src, options, renderer) {
19212       var parser = new Parser(options, renderer);
19213       return parser.parse(src);
19214     };
19215     
19216     /**
19217      * Parse Loop
19218      */
19219     
19220     Parser.prototype.parse = function(src) {
19221       this.inline = new InlineLexer(src.links, this.options, this.renderer);
19222       this.tokens = src.reverse();
19223     
19224       var out = '';
19225       while (this.next()) {
19226         out += this.tok();
19227       }
19228     
19229       return out;
19230     };
19231     
19232     /**
19233      * Next Token
19234      */
19235     
19236     Parser.prototype.next = function() {
19237       return this.token = this.tokens.pop();
19238     };
19239     
19240     /**
19241      * Preview Next Token
19242      */
19243     
19244     Parser.prototype.peek = function() {
19245       return this.tokens[this.tokens.length - 1] || 0;
19246     };
19247     
19248     /**
19249      * Parse Text Tokens
19250      */
19251     
19252     Parser.prototype.parseText = function() {
19253       var body = this.token.text;
19254     
19255       while (this.peek().type === 'text') {
19256         body += '\n' + this.next().text;
19257       }
19258     
19259       return this.inline.output(body);
19260     };
19261     
19262     /**
19263      * Parse Current Token
19264      */
19265     
19266     Parser.prototype.tok = function() {
19267       switch (this.token.type) {
19268         case 'space': {
19269           return '';
19270         }
19271         case 'hr': {
19272           return this.renderer.hr();
19273         }
19274         case 'heading': {
19275           return this.renderer.heading(
19276             this.inline.output(this.token.text),
19277             this.token.depth,
19278             this.token.text);
19279         }
19280         case 'code': {
19281           return this.renderer.code(this.token.text,
19282             this.token.lang,
19283             this.token.escaped);
19284         }
19285         case 'table': {
19286           var header = ''
19287             , body = ''
19288             , i
19289             , row
19290             , cell
19291             , flags
19292             , j;
19293     
19294           // header
19295           cell = '';
19296           for (i = 0; i < this.token.header.length; i++) {
19297             flags = { header: true, align: this.token.align[i] };
19298             cell += this.renderer.tablecell(
19299               this.inline.output(this.token.header[i]),
19300               { header: true, align: this.token.align[i] }
19301             );
19302           }
19303           header += this.renderer.tablerow(cell);
19304     
19305           for (i = 0; i < this.token.cells.length; i++) {
19306             row = this.token.cells[i];
19307     
19308             cell = '';
19309             for (j = 0; j < row.length; j++) {
19310               cell += this.renderer.tablecell(
19311                 this.inline.output(row[j]),
19312                 { header: false, align: this.token.align[j] }
19313               );
19314             }
19315     
19316             body += this.renderer.tablerow(cell);
19317           }
19318           return this.renderer.table(header, body);
19319         }
19320         case 'blockquote_start': {
19321           var body = '';
19322     
19323           while (this.next().type !== 'blockquote_end') {
19324             body += this.tok();
19325           }
19326     
19327           return this.renderer.blockquote(body);
19328         }
19329         case 'list_start': {
19330           var body = ''
19331             , ordered = this.token.ordered;
19332     
19333           while (this.next().type !== 'list_end') {
19334             body += this.tok();
19335           }
19336     
19337           return this.renderer.list(body, ordered);
19338         }
19339         case 'list_item_start': {
19340           var body = '';
19341     
19342           while (this.next().type !== 'list_item_end') {
19343             body += this.token.type === 'text'
19344               ? this.parseText()
19345               : this.tok();
19346           }
19347     
19348           return this.renderer.listitem(body);
19349         }
19350         case 'loose_item_start': {
19351           var body = '';
19352     
19353           while (this.next().type !== 'list_item_end') {
19354             body += this.tok();
19355           }
19356     
19357           return this.renderer.listitem(body);
19358         }
19359         case 'html': {
19360           var html = !this.token.pre && !this.options.pedantic
19361             ? this.inline.output(this.token.text)
19362             : this.token.text;
19363           return this.renderer.html(html);
19364         }
19365         case 'paragraph': {
19366           return this.renderer.paragraph(this.inline.output(this.token.text));
19367         }
19368         case 'text': {
19369           return this.renderer.paragraph(this.parseText());
19370         }
19371       }
19372     };
19373   
19374     
19375     /**
19376      * Marked
19377      */
19378          /**
19379          * eval:var:marked
19380     */
19381     var marked = function (src, opt, callback) {
19382       if (callback || typeof opt === 'function') {
19383         if (!callback) {
19384           callback = opt;
19385           opt = null;
19386         }
19387     
19388         opt = merge({}, marked.defaults, opt || {});
19389     
19390         var highlight = opt.highlight
19391           , tokens
19392           , pending
19393           , i = 0;
19394     
19395         try {
19396           tokens = Lexer.lex(src, opt)
19397         } catch (e) {
19398           return callback(e);
19399         }
19400     
19401         pending = tokens.length;
19402          /**
19403          * eval:var:done
19404     */
19405         var done = function(err) {
19406           if (err) {
19407             opt.highlight = highlight;
19408             return callback(err);
19409           }
19410     
19411           var out;
19412     
19413           try {
19414             out = Parser.parse(tokens, opt);
19415           } catch (e) {
19416             err = e;
19417           }
19418     
19419           opt.highlight = highlight;
19420     
19421           return err
19422             ? callback(err)
19423             : callback(null, out);
19424         };
19425     
19426         if (!highlight || highlight.length < 3) {
19427           return done();
19428         }
19429     
19430         delete opt.highlight;
19431     
19432         if (!pending) { return done(); }
19433     
19434         for (; i < tokens.length; i++) {
19435           (function(token) {
19436             if (token.type !== 'code') {
19437               return --pending || done();
19438             }
19439             return highlight(token.text, token.lang, function(err, code) {
19440               if (err) { return done(err); }
19441               if (code == null || code === token.text) {
19442                 return --pending || done();
19443               }
19444               token.text = code;
19445               token.escaped = true;
19446               --pending || done();
19447             });
19448           })(tokens[i]);
19449         }
19450     
19451         return;
19452       }
19453       try {
19454         if (opt) { opt = merge({}, marked.defaults, opt); }
19455         return Parser.parse(Lexer.lex(src, opt), opt);
19456       } catch (e) {
19457         e.message += '\nPlease report this to https://github.com/chjj/marked.';
19458         if ((opt || marked.defaults).silent) {
19459           return '<p>An error occured:</p><pre>'
19460             + escape(e.message + '', true)
19461             + '</pre>';
19462         }
19463         throw e;
19464       }
19465     }
19466     
19467     /**
19468      * Options
19469      */
19470     
19471     marked.options =
19472     marked.setOptions = function(opt) {
19473       merge(marked.defaults, opt);
19474       return marked;
19475     };
19476     
19477     marked.defaults = {
19478       gfm: true,
19479       tables: true,
19480       breaks: false,
19481       pedantic: false,
19482       sanitize: false,
19483       sanitizer: null,
19484       mangle: true,
19485       smartLists: false,
19486       silent: false,
19487       highlight: null,
19488       langPrefix: 'lang-',
19489       smartypants: false,
19490       headerPrefix: '',
19491       renderer: new Renderer,
19492       xhtml: false
19493     };
19494     
19495     /**
19496      * Expose
19497      */
19498     
19499     marked.Parser = Parser;
19500     marked.parser = Parser.parse;
19501     
19502     marked.Renderer = Renderer;
19503     
19504     marked.Lexer = Lexer;
19505     marked.lexer = Lexer.lex;
19506     
19507     marked.InlineLexer = InlineLexer;
19508     marked.inlineLexer = InlineLexer.output;
19509     
19510     marked.parse = marked;
19511     
19512     Roo.Markdown.marked = marked;
19513
19514 })();/*
19515  * Based on:
19516  * Ext JS Library 1.1.1
19517  * Copyright(c) 2006-2007, Ext JS, LLC.
19518  *
19519  * Originally Released Under LGPL - original licence link has changed is not relivant.
19520  *
19521  * Fork - LGPL
19522  * <script type="text/javascript">
19523  */
19524
19525
19526
19527 /*
19528  * These classes are derivatives of the similarly named classes in the YUI Library.
19529  * The original license:
19530  * Copyright (c) 2006, Yahoo! Inc. All rights reserved.
19531  * Code licensed under the BSD License:
19532  * http://developer.yahoo.net/yui/license.txt
19533  */
19534
19535 (function() {
19536
19537 var Event=Roo.EventManager;
19538 var Dom=Roo.lib.Dom;
19539
19540 /**
19541  * @class Roo.dd.DragDrop
19542  * @extends Roo.util.Observable
19543  * Defines the interface and base operation of items that that can be
19544  * dragged or can be drop targets.  It was designed to be extended, overriding
19545  * the event handlers for startDrag, onDrag, onDragOver and onDragOut.
19546  * Up to three html elements can be associated with a DragDrop instance:
19547  * <ul>
19548  * <li>linked element: the element that is passed into the constructor.
19549  * This is the element which defines the boundaries for interaction with
19550  * other DragDrop objects.</li>
19551  * <li>handle element(s): The drag operation only occurs if the element that
19552  * was clicked matches a handle element.  By default this is the linked
19553  * element, but there are times that you will want only a portion of the
19554  * linked element to initiate the drag operation, and the setHandleElId()
19555  * method provides a way to define this.</li>
19556  * <li>drag element: this represents the element that would be moved along
19557  * with the cursor during a drag operation.  By default, this is the linked
19558  * element itself as in {@link Roo.dd.DD}.  setDragElId() lets you define
19559  * a separate element that would be moved, as in {@link Roo.dd.DDProxy}.
19560  * </li>
19561  * </ul>
19562  * This class should not be instantiated until the onload event to ensure that
19563  * the associated elements are available.
19564  * The following would define a DragDrop obj that would interact with any
19565  * other DragDrop obj in the "group1" group:
19566  * <pre>
19567  *  dd = new Roo.dd.DragDrop("div1", "group1");
19568  * </pre>
19569  * Since none of the event handlers have been implemented, nothing would
19570  * actually happen if you were to run the code above.  Normally you would
19571  * override this class or one of the default implementations, but you can
19572  * also override the methods you want on an instance of the class...
19573  * <pre>
19574  *  dd.onDragDrop = function(e, id) {
19575  *  &nbsp;&nbsp;alert("dd was dropped on " + id);
19576  *  }
19577  * </pre>
19578  * @constructor
19579  * @param {String} id of the element that is linked to this instance
19580  * @param {String} sGroup the group of related DragDrop objects
19581  * @param {object} config an object containing configurable attributes
19582  *                Valid properties for DragDrop:
19583  *                    padding, isTarget, maintainOffset, primaryButtonOnly
19584  */
19585 Roo.dd.DragDrop = function(id, sGroup, config) {
19586     if (id) {
19587         this.init(id, sGroup, config);
19588     }
19589     
19590 };
19591
19592 Roo.extend(Roo.dd.DragDrop, Roo.util.Observable , {
19593
19594     /**
19595      * The id of the element associated with this object.  This is what we
19596      * refer to as the "linked element" because the size and position of
19597      * this element is used to determine when the drag and drop objects have
19598      * interacted.
19599      * @property id
19600      * @type String
19601      */
19602     id: null,
19603
19604     /**
19605      * Configuration attributes passed into the constructor
19606      * @property config
19607      * @type object
19608      */
19609     config: null,
19610
19611     /**
19612      * The id of the element that will be dragged.  By default this is same
19613      * as the linked element , but could be changed to another element. Ex:
19614      * Roo.dd.DDProxy
19615      * @property dragElId
19616      * @type String
19617      * @private
19618      */
19619     dragElId: null,
19620
19621     /**
19622      * the id of the element that initiates the drag operation.  By default
19623      * this is the linked element, but could be changed to be a child of this
19624      * element.  This lets us do things like only starting the drag when the
19625      * header element within the linked html element is clicked.
19626      * @property handleElId
19627      * @type String
19628      * @private
19629      */
19630     handleElId: null,
19631
19632     /**
19633      * An associative array of HTML tags that will be ignored if clicked.
19634      * @property invalidHandleTypes
19635      * @type {string: string}
19636      */
19637     invalidHandleTypes: null,
19638
19639     /**
19640      * An associative array of ids for elements that will be ignored if clicked
19641      * @property invalidHandleIds
19642      * @type {string: string}
19643      */
19644     invalidHandleIds: null,
19645
19646     /**
19647      * An indexted array of css class names for elements that will be ignored
19648      * if clicked.
19649      * @property invalidHandleClasses
19650      * @type string[]
19651      */
19652     invalidHandleClasses: null,
19653
19654     /**
19655      * The linked element's absolute X position at the time the drag was
19656      * started
19657      * @property startPageX
19658      * @type int
19659      * @private
19660      */
19661     startPageX: 0,
19662
19663     /**
19664      * The linked element's absolute X position at the time the drag was
19665      * started
19666      * @property startPageY
19667      * @type int
19668      * @private
19669      */
19670     startPageY: 0,
19671
19672     /**
19673      * The group defines a logical collection of DragDrop objects that are
19674      * related.  Instances only get events when interacting with other
19675      * DragDrop object in the same group.  This lets us define multiple
19676      * groups using a single DragDrop subclass if we want.
19677      * @property groups
19678      * @type {string: string}
19679      */
19680     groups: null,
19681
19682     /**
19683      * Individual drag/drop instances can be locked.  This will prevent
19684      * onmousedown start drag.
19685      * @property locked
19686      * @type boolean
19687      * @private
19688      */
19689     locked: false,
19690
19691     /**
19692      * Lock this instance
19693      * @method lock
19694      */
19695     lock: function() { this.locked = true; },
19696
19697     /**
19698      * Unlock this instace
19699      * @method unlock
19700      */
19701     unlock: function() { this.locked = false; },
19702
19703     /**
19704      * By default, all insances can be a drop target.  This can be disabled by
19705      * setting isTarget to false.
19706      * @method isTarget
19707      * @type boolean
19708      */
19709     isTarget: true,
19710
19711     /**
19712      * The padding configured for this drag and drop object for calculating
19713      * the drop zone intersection with this object.
19714      * @method padding
19715      * @type int[]
19716      */
19717     padding: null,
19718
19719     /**
19720      * Cached reference to the linked element
19721      * @property _domRef
19722      * @private
19723      */
19724     _domRef: null,
19725
19726     /**
19727      * Internal typeof flag
19728      * @property __ygDragDrop
19729      * @private
19730      */
19731     __ygDragDrop: true,
19732
19733     /**
19734      * Set to true when horizontal contraints are applied
19735      * @property constrainX
19736      * @type boolean
19737      * @private
19738      */
19739     constrainX: false,
19740
19741     /**
19742      * Set to true when vertical contraints are applied
19743      * @property constrainY
19744      * @type boolean
19745      * @private
19746      */
19747     constrainY: false,
19748
19749     /**
19750      * The left constraint
19751      * @property minX
19752      * @type int
19753      * @private
19754      */
19755     minX: 0,
19756
19757     /**
19758      * The right constraint
19759      * @property maxX
19760      * @type int
19761      * @private
19762      */
19763     maxX: 0,
19764
19765     /**
19766      * The up constraint
19767      * @property minY
19768      * @type int
19769      * @type int
19770      * @private
19771      */
19772     minY: 0,
19773
19774     /**
19775      * The down constraint
19776      * @property maxY
19777      * @type int
19778      * @private
19779      */
19780     maxY: 0,
19781
19782     /**
19783      * Maintain offsets when we resetconstraints.  Set to true when you want
19784      * the position of the element relative to its parent to stay the same
19785      * when the page changes
19786      *
19787      * @property maintainOffset
19788      * @type boolean
19789      */
19790     maintainOffset: false,
19791
19792     /**
19793      * Array of pixel locations the element will snap to if we specified a
19794      * horizontal graduation/interval.  This array is generated automatically
19795      * when you define a tick interval.
19796      * @property xTicks
19797      * @type int[]
19798      */
19799     xTicks: null,
19800
19801     /**
19802      * Array of pixel locations the element will snap to if we specified a
19803      * vertical graduation/interval.  This array is generated automatically
19804      * when you define a tick interval.
19805      * @property yTicks
19806      * @type int[]
19807      */
19808     yTicks: null,
19809
19810     /**
19811      * By default the drag and drop instance will only respond to the primary
19812      * button click (left button for a right-handed mouse).  Set to true to
19813      * allow drag and drop to start with any mouse click that is propogated
19814      * by the browser
19815      * @property primaryButtonOnly
19816      * @type boolean
19817      */
19818     primaryButtonOnly: true,
19819
19820     /**
19821      * The availabe property is false until the linked dom element is accessible.
19822      * @property available
19823      * @type boolean
19824      */
19825     available: false,
19826
19827     /**
19828      * By default, drags can only be initiated if the mousedown occurs in the
19829      * region the linked element is.  This is done in part to work around a
19830      * bug in some browsers that mis-report the mousedown if the previous
19831      * mouseup happened outside of the window.  This property is set to true
19832      * if outer handles are defined.
19833      *
19834      * @property hasOuterHandles
19835      * @type boolean
19836      * @default false
19837      */
19838     hasOuterHandles: false,
19839
19840     /**
19841      * Code that executes immediately before the startDrag event
19842      * @method b4StartDrag
19843      * @private
19844      */
19845     b4StartDrag: function(x, y) { },
19846
19847     /**
19848      * Abstract method called after a drag/drop object is clicked
19849      * and the drag or mousedown time thresholds have beeen met.
19850      * @method startDrag
19851      * @param {int} X click location
19852      * @param {int} Y click location
19853      */
19854     startDrag: function(x, y) { /* override this */ },
19855
19856     /**
19857      * Code that executes immediately before the onDrag event
19858      * @method b4Drag
19859      * @private
19860      */
19861     b4Drag: function(e) { },
19862
19863     /**
19864      * Abstract method called during the onMouseMove event while dragging an
19865      * object.
19866      * @method onDrag
19867      * @param {Event} e the mousemove event
19868      */
19869     onDrag: function(e) { /* override this */ },
19870
19871     /**
19872      * Abstract method called when this element fist begins hovering over
19873      * another DragDrop obj
19874      * @method onDragEnter
19875      * @param {Event} e the mousemove event
19876      * @param {String|DragDrop[]} id In POINT mode, the element
19877      * id this is hovering over.  In INTERSECT mode, an array of one or more
19878      * dragdrop items being hovered over.
19879      */
19880     onDragEnter: function(e, id) { /* override this */ },
19881
19882     /**
19883      * Code that executes immediately before the onDragOver event
19884      * @method b4DragOver
19885      * @private
19886      */
19887     b4DragOver: function(e) { },
19888
19889     /**
19890      * Abstract method called when this element is hovering over another
19891      * DragDrop obj
19892      * @method onDragOver
19893      * @param {Event} e the mousemove event
19894      * @param {String|DragDrop[]} id In POINT mode, the element
19895      * id this is hovering over.  In INTERSECT mode, an array of dd items
19896      * being hovered over.
19897      */
19898     onDragOver: function(e, id) { /* override this */ },
19899
19900     /**
19901      * Code that executes immediately before the onDragOut event
19902      * @method b4DragOut
19903      * @private
19904      */
19905     b4DragOut: function(e) { },
19906
19907     /**
19908      * Abstract method called when we are no longer hovering over an element
19909      * @method onDragOut
19910      * @param {Event} e the mousemove event
19911      * @param {String|DragDrop[]} id In POINT mode, the element
19912      * id this was hovering over.  In INTERSECT mode, an array of dd items
19913      * that the mouse is no longer over.
19914      */
19915     onDragOut: function(e, id) { /* override this */ },
19916
19917     /**
19918      * Code that executes immediately before the onDragDrop event
19919      * @method b4DragDrop
19920      * @private
19921      */
19922     b4DragDrop: function(e) { },
19923
19924     /**
19925      * Abstract method called when this item is dropped on another DragDrop
19926      * obj
19927      * @method onDragDrop
19928      * @param {Event} e the mouseup event
19929      * @param {String|DragDrop[]} id In POINT mode, the element
19930      * id this was dropped on.  In INTERSECT mode, an array of dd items this
19931      * was dropped on.
19932      */
19933     onDragDrop: function(e, id) { /* override this */ },
19934
19935     /**
19936      * Abstract method called when this item is dropped on an area with no
19937      * drop target
19938      * @method onInvalidDrop
19939      * @param {Event} e the mouseup event
19940      */
19941     onInvalidDrop: function(e) { /* override this */ },
19942
19943     /**
19944      * Code that executes immediately before the endDrag event
19945      * @method b4EndDrag
19946      * @private
19947      */
19948     b4EndDrag: function(e) { },
19949
19950     /**
19951      * Fired when we are done dragging the object
19952      * @method endDrag
19953      * @param {Event} e the mouseup event
19954      */
19955     endDrag: function(e) { /* override this */ },
19956
19957     /**
19958      * Code executed immediately before the onMouseDown event
19959      * @method b4MouseDown
19960      * @param {Event} e the mousedown event
19961      * @private
19962      */
19963     b4MouseDown: function(e) {  },
19964
19965     /**
19966      * Event handler that fires when a drag/drop obj gets a mousedown
19967      * @method onMouseDown
19968      * @param {Event} e the mousedown event
19969      */
19970     onMouseDown: function(e) { /* override this */ },
19971
19972     /**
19973      * Event handler that fires when a drag/drop obj gets a mouseup
19974      * @method onMouseUp
19975      * @param {Event} e the mouseup event
19976      */
19977     onMouseUp: function(e) { /* override this */ },
19978
19979     /**
19980      * Override the onAvailable method to do what is needed after the initial
19981      * position was determined.
19982      * @method onAvailable
19983      */
19984     onAvailable: function () {
19985     },
19986
19987     /*
19988      * Provides default constraint padding to "constrainTo" elements (defaults to {left: 0, right:0, top:0, bottom:0}).
19989      * @type Object
19990      */
19991     defaultPadding : {left:0, right:0, top:0, bottom:0},
19992
19993     /*
19994      * Initializes the drag drop object's constraints to restrict movement to a certain element.
19995  *
19996  * Usage:
19997  <pre><code>
19998  var dd = new Roo.dd.DDProxy("dragDiv1", "proxytest",
19999                 { dragElId: "existingProxyDiv" });
20000  dd.startDrag = function(){
20001      this.constrainTo("parent-id");
20002  };
20003  </code></pre>
20004  * Or you can initalize it using the {@link Roo.Element} object:
20005  <pre><code>
20006  Roo.get("dragDiv1").initDDProxy("proxytest", {dragElId: "existingProxyDiv"}, {
20007      startDrag : function(){
20008          this.constrainTo("parent-id");
20009      }
20010  });
20011  </code></pre>
20012      * @param {String/HTMLElement/Element} constrainTo The element to constrain to.
20013      * @param {Object/Number} pad (optional) Pad provides a way to specify "padding" of the constraints,
20014      * and can be either a number for symmetrical padding (4 would be equal to {left:4, right:4, top:4, bottom:4}) or
20015      * an object containing the sides to pad. For example: {right:10, bottom:10}
20016      * @param {Boolean} inContent (optional) Constrain the draggable in the content box of the element (inside padding and borders)
20017      */
20018     constrainTo : function(constrainTo, pad, inContent){
20019         if(typeof pad == "number"){
20020             pad = {left: pad, right:pad, top:pad, bottom:pad};
20021         }
20022         pad = pad || this.defaultPadding;
20023         var b = Roo.get(this.getEl()).getBox();
20024         var ce = Roo.get(constrainTo);
20025         var s = ce.getScroll();
20026         var c, cd = ce.dom;
20027         if(cd == document.body){
20028             c = { x: s.left, y: s.top, width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
20029         }else{
20030             xy = ce.getXY();
20031             c = {x : xy[0]+s.left, y: xy[1]+s.top, width: cd.clientWidth, height: cd.clientHeight};
20032         }
20033
20034
20035         var topSpace = b.y - c.y;
20036         var leftSpace = b.x - c.x;
20037
20038         this.resetConstraints();
20039         this.setXConstraint(leftSpace - (pad.left||0), // left
20040                 c.width - leftSpace - b.width - (pad.right||0) //right
20041         );
20042         this.setYConstraint(topSpace - (pad.top||0), //top
20043                 c.height - topSpace - b.height - (pad.bottom||0) //bottom
20044         );
20045     },
20046
20047     /**
20048      * Returns a reference to the linked element
20049      * @method getEl
20050      * @return {HTMLElement} the html element
20051      */
20052     getEl: function() {
20053         if (!this._domRef) {
20054             this._domRef = Roo.getDom(this.id);
20055         }
20056
20057         return this._domRef;
20058     },
20059
20060     /**
20061      * Returns a reference to the actual element to drag.  By default this is
20062      * the same as the html element, but it can be assigned to another
20063      * element. An example of this can be found in Roo.dd.DDProxy
20064      * @method getDragEl
20065      * @return {HTMLElement} the html element
20066      */
20067     getDragEl: function() {
20068         return Roo.getDom(this.dragElId);
20069     },
20070
20071     /**
20072      * Sets up the DragDrop object.  Must be called in the constructor of any
20073      * Roo.dd.DragDrop subclass
20074      * @method init
20075      * @param id the id of the linked element
20076      * @param {String} sGroup the group of related items
20077      * @param {object} config configuration attributes
20078      */
20079     init: function(id, sGroup, config) {
20080         this.initTarget(id, sGroup, config);
20081         if (!Roo.isTouch) {
20082             Event.on(this.id, "mousedown", this.handleMouseDown, this);
20083         }
20084         Event.on(this.id, "touchstart", this.handleMouseDown, this);
20085         // Event.on(this.id, "selectstart", Event.preventDefault);
20086     },
20087
20088     /**
20089      * Initializes Targeting functionality only... the object does not
20090      * get a mousedown handler.
20091      * @method initTarget
20092      * @param id the id of the linked element
20093      * @param {String} sGroup the group of related items
20094      * @param {object} config configuration attributes
20095      */
20096     initTarget: function(id, sGroup, config) {
20097
20098         // configuration attributes
20099         this.config = config || {};
20100
20101         // create a local reference to the drag and drop manager
20102         this.DDM = Roo.dd.DDM;
20103         // initialize the groups array
20104         this.groups = {};
20105
20106         // assume that we have an element reference instead of an id if the
20107         // parameter is not a string
20108         if (typeof id !== "string") {
20109             id = Roo.id(id);
20110         }
20111
20112         // set the id
20113         this.id = id;
20114
20115         // add to an interaction group
20116         this.addToGroup((sGroup) ? sGroup : "default");
20117
20118         // We don't want to register this as the handle with the manager
20119         // so we just set the id rather than calling the setter.
20120         this.handleElId = id;
20121
20122         // the linked element is the element that gets dragged by default
20123         this.setDragElId(id);
20124
20125         // by default, clicked anchors will not start drag operations.
20126         this.invalidHandleTypes = { A: "A" };
20127         this.invalidHandleIds = {};
20128         this.invalidHandleClasses = [];
20129
20130         this.applyConfig();
20131
20132         this.handleOnAvailable();
20133     },
20134
20135     /**
20136      * Applies the configuration parameters that were passed into the constructor.
20137      * This is supposed to happen at each level through the inheritance chain.  So
20138      * a DDProxy implentation will execute apply config on DDProxy, DD, and
20139      * DragDrop in order to get all of the parameters that are available in
20140      * each object.
20141      * @method applyConfig
20142      */
20143     applyConfig: function() {
20144
20145         // configurable properties:
20146         //    padding, isTarget, maintainOffset, primaryButtonOnly
20147         this.padding           = this.config.padding || [0, 0, 0, 0];
20148         this.isTarget          = (this.config.isTarget !== false);
20149         this.maintainOffset    = (this.config.maintainOffset);
20150         this.primaryButtonOnly = (this.config.primaryButtonOnly !== false);
20151
20152     },
20153
20154     /**
20155      * Executed when the linked element is available
20156      * @method handleOnAvailable
20157      * @private
20158      */
20159     handleOnAvailable: function() {
20160         this.available = true;
20161         this.resetConstraints();
20162         this.onAvailable();
20163     },
20164
20165      /**
20166      * Configures the padding for the target zone in px.  Effectively expands
20167      * (or reduces) the virtual object size for targeting calculations.
20168      * Supports css-style shorthand; if only one parameter is passed, all sides
20169      * will have that padding, and if only two are passed, the top and bottom
20170      * will have the first param, the left and right the second.
20171      * @method setPadding
20172      * @param {int} iTop    Top pad
20173      * @param {int} iRight  Right pad
20174      * @param {int} iBot    Bot pad
20175      * @param {int} iLeft   Left pad
20176      */
20177     setPadding: function(iTop, iRight, iBot, iLeft) {
20178         // this.padding = [iLeft, iRight, iTop, iBot];
20179         if (!iRight && 0 !== iRight) {
20180             this.padding = [iTop, iTop, iTop, iTop];
20181         } else if (!iBot && 0 !== iBot) {
20182             this.padding = [iTop, iRight, iTop, iRight];
20183         } else {
20184             this.padding = [iTop, iRight, iBot, iLeft];
20185         }
20186     },
20187
20188     /**
20189      * Stores the initial placement of the linked element.
20190      * @method setInitialPosition
20191      * @param {int} diffX   the X offset, default 0
20192      * @param {int} diffY   the Y offset, default 0
20193      */
20194     setInitPosition: function(diffX, diffY) {
20195         var el = this.getEl();
20196
20197         if (!this.DDM.verifyEl(el)) {
20198             return;
20199         }
20200
20201         var dx = diffX || 0;
20202         var dy = diffY || 0;
20203
20204         var p = Dom.getXY( el );
20205
20206         this.initPageX = p[0] - dx;
20207         this.initPageY = p[1] - dy;
20208
20209         this.lastPageX = p[0];
20210         this.lastPageY = p[1];
20211
20212
20213         this.setStartPosition(p);
20214     },
20215
20216     /**
20217      * Sets the start position of the element.  This is set when the obj
20218      * is initialized, the reset when a drag is started.
20219      * @method setStartPosition
20220      * @param pos current position (from previous lookup)
20221      * @private
20222      */
20223     setStartPosition: function(pos) {
20224         var p = pos || Dom.getXY( this.getEl() );
20225         this.deltaSetXY = null;
20226
20227         this.startPageX = p[0];
20228         this.startPageY = p[1];
20229     },
20230
20231     /**
20232      * Add this instance to a group of related drag/drop objects.  All
20233      * instances belong to at least one group, and can belong to as many
20234      * groups as needed.
20235      * @method addToGroup
20236      * @param sGroup {string} the name of the group
20237      */
20238     addToGroup: function(sGroup) {
20239         this.groups[sGroup] = true;
20240         this.DDM.regDragDrop(this, sGroup);
20241     },
20242
20243     /**
20244      * Remove's this instance from the supplied interaction group
20245      * @method removeFromGroup
20246      * @param {string}  sGroup  The group to drop
20247      */
20248     removeFromGroup: function(sGroup) {
20249         if (this.groups[sGroup]) {
20250             delete this.groups[sGroup];
20251         }
20252
20253         this.DDM.removeDDFromGroup(this, sGroup);
20254     },
20255
20256     /**
20257      * Allows you to specify that an element other than the linked element
20258      * will be moved with the cursor during a drag
20259      * @method setDragElId
20260      * @param id {string} the id of the element that will be used to initiate the drag
20261      */
20262     setDragElId: function(id) {
20263         this.dragElId = id;
20264     },
20265
20266     /**
20267      * Allows you to specify a child of the linked element that should be
20268      * used to initiate the drag operation.  An example of this would be if
20269      * you have a content div with text and links.  Clicking anywhere in the
20270      * content area would normally start the drag operation.  Use this method
20271      * to specify that an element inside of the content div is the element
20272      * that starts the drag operation.
20273      * @method setHandleElId
20274      * @param id {string} the id of the element that will be used to
20275      * initiate the drag.
20276      */
20277     setHandleElId: function(id) {
20278         if (typeof id !== "string") {
20279             id = Roo.id(id);
20280         }
20281         this.handleElId = id;
20282         this.DDM.regHandle(this.id, id);
20283     },
20284
20285     /**
20286      * Allows you to set an element outside of the linked element as a drag
20287      * handle
20288      * @method setOuterHandleElId
20289      * @param id the id of the element that will be used to initiate the drag
20290      */
20291     setOuterHandleElId: function(id) {
20292         if (typeof id !== "string") {
20293             id = Roo.id(id);
20294         }
20295         Event.on(id, "mousedown",
20296                 this.handleMouseDown, this);
20297         this.setHandleElId(id);
20298
20299         this.hasOuterHandles = true;
20300     },
20301
20302     /**
20303      * Remove all drag and drop hooks for this element
20304      * @method unreg
20305      */
20306     unreg: function() {
20307         Event.un(this.id, "mousedown",
20308                 this.handleMouseDown);
20309         Event.un(this.id, "touchstart",
20310                 this.handleMouseDown);
20311         this._domRef = null;
20312         this.DDM._remove(this);
20313     },
20314
20315     destroy : function(){
20316         this.unreg();
20317     },
20318
20319     /**
20320      * Returns true if this instance is locked, or the drag drop mgr is locked
20321      * (meaning that all drag/drop is disabled on the page.)
20322      * @method isLocked
20323      * @return {boolean} true if this obj or all drag/drop is locked, else
20324      * false
20325      */
20326     isLocked: function() {
20327         return (this.DDM.isLocked() || this.locked);
20328     },
20329
20330     /**
20331      * Fired when this object is clicked
20332      * @method handleMouseDown
20333      * @param {Event} e
20334      * @param {Roo.dd.DragDrop} oDD the clicked dd object (this dd obj)
20335      * @private
20336      */
20337     handleMouseDown: function(e, oDD){
20338      
20339         if (!Roo.isTouch && this.primaryButtonOnly && e.button != 0) {
20340             //Roo.log('not touch/ button !=0');
20341             return;
20342         }
20343         if (e.browserEvent.touches && e.browserEvent.touches.length != 1) {
20344             return; // double touch..
20345         }
20346         
20347
20348         if (this.isLocked()) {
20349             //Roo.log('locked');
20350             return;
20351         }
20352
20353         this.DDM.refreshCache(this.groups);
20354 //        Roo.log([Roo.lib.Event.getPageX(e), Roo.lib.Event.getPageY(e)]);
20355         var pt = new Roo.lib.Point(Roo.lib.Event.getPageX(e), Roo.lib.Event.getPageY(e));
20356         if (!this.hasOuterHandles && !this.DDM.isOverTarget(pt, this) )  {
20357             //Roo.log('no outer handes or not over target');
20358                 // do nothing.
20359         } else {
20360 //            Roo.log('check validator');
20361             if (this.clickValidator(e)) {
20362 //                Roo.log('validate success');
20363                 // set the initial element position
20364                 this.setStartPosition();
20365
20366
20367                 this.b4MouseDown(e);
20368                 this.onMouseDown(e);
20369
20370                 this.DDM.handleMouseDown(e, this);
20371
20372                 this.DDM.stopEvent(e);
20373             } else {
20374
20375
20376             }
20377         }
20378     },
20379
20380     clickValidator: function(e) {
20381         var target = e.getTarget();
20382         return ( this.isValidHandleChild(target) &&
20383                     (this.id == this.handleElId ||
20384                         this.DDM.handleWasClicked(target, this.id)) );
20385     },
20386
20387     /**
20388      * Allows you to specify a tag name that should not start a drag operation
20389      * when clicked.  This is designed to facilitate embedding links within a
20390      * drag handle that do something other than start the drag.
20391      * @method addInvalidHandleType
20392      * @param {string} tagName the type of element to exclude
20393      */
20394     addInvalidHandleType: function(tagName) {
20395         var type = tagName.toUpperCase();
20396         this.invalidHandleTypes[type] = type;
20397     },
20398
20399     /**
20400      * Lets you to specify an element id for a child of a drag handle
20401      * that should not initiate a drag
20402      * @method addInvalidHandleId
20403      * @param {string} id the element id of the element you wish to ignore
20404      */
20405     addInvalidHandleId: function(id) {
20406         if (typeof id !== "string") {
20407             id = Roo.id(id);
20408         }
20409         this.invalidHandleIds[id] = id;
20410     },
20411
20412     /**
20413      * Lets you specify a css class of elements that will not initiate a drag
20414      * @method addInvalidHandleClass
20415      * @param {string} cssClass the class of the elements you wish to ignore
20416      */
20417     addInvalidHandleClass: function(cssClass) {
20418         this.invalidHandleClasses.push(cssClass);
20419     },
20420
20421     /**
20422      * Unsets an excluded tag name set by addInvalidHandleType
20423      * @method removeInvalidHandleType
20424      * @param {string} tagName the type of element to unexclude
20425      */
20426     removeInvalidHandleType: function(tagName) {
20427         var type = tagName.toUpperCase();
20428         // this.invalidHandleTypes[type] = null;
20429         delete this.invalidHandleTypes[type];
20430     },
20431
20432     /**
20433      * Unsets an invalid handle id
20434      * @method removeInvalidHandleId
20435      * @param {string} id the id of the element to re-enable
20436      */
20437     removeInvalidHandleId: function(id) {
20438         if (typeof id !== "string") {
20439             id = Roo.id(id);
20440         }
20441         delete this.invalidHandleIds[id];
20442     },
20443
20444     /**
20445      * Unsets an invalid css class
20446      * @method removeInvalidHandleClass
20447      * @param {string} cssClass the class of the element(s) you wish to
20448      * re-enable
20449      */
20450     removeInvalidHandleClass: function(cssClass) {
20451         for (var i=0, len=this.invalidHandleClasses.length; i<len; ++i) {
20452             if (this.invalidHandleClasses[i] == cssClass) {
20453                 delete this.invalidHandleClasses[i];
20454             }
20455         }
20456     },
20457
20458     /**
20459      * Checks the tag exclusion list to see if this click should be ignored
20460      * @method isValidHandleChild
20461      * @param {HTMLElement} node the HTMLElement to evaluate
20462      * @return {boolean} true if this is a valid tag type, false if not
20463      */
20464     isValidHandleChild: function(node) {
20465
20466         var valid = true;
20467         // var n = (node.nodeName == "#text") ? node.parentNode : node;
20468         var nodeName;
20469         try {
20470             nodeName = node.nodeName.toUpperCase();
20471         } catch(e) {
20472             nodeName = node.nodeName;
20473         }
20474         valid = valid && !this.invalidHandleTypes[nodeName];
20475         valid = valid && !this.invalidHandleIds[node.id];
20476
20477         for (var i=0, len=this.invalidHandleClasses.length; valid && i<len; ++i) {
20478             valid = !Dom.hasClass(node, this.invalidHandleClasses[i]);
20479         }
20480
20481
20482         return valid;
20483
20484     },
20485
20486     /**
20487      * Create the array of horizontal tick marks if an interval was specified
20488      * in setXConstraint().
20489      * @method setXTicks
20490      * @private
20491      */
20492     setXTicks: function(iStartX, iTickSize) {
20493         this.xTicks = [];
20494         this.xTickSize = iTickSize;
20495
20496         var tickMap = {};
20497
20498         for (var i = this.initPageX; i >= this.minX; i = i - iTickSize) {
20499             if (!tickMap[i]) {
20500                 this.xTicks[this.xTicks.length] = i;
20501                 tickMap[i] = true;
20502             }
20503         }
20504
20505         for (i = this.initPageX; i <= this.maxX; i = i + iTickSize) {
20506             if (!tickMap[i]) {
20507                 this.xTicks[this.xTicks.length] = i;
20508                 tickMap[i] = true;
20509             }
20510         }
20511
20512         this.xTicks.sort(this.DDM.numericSort) ;
20513     },
20514
20515     /**
20516      * Create the array of vertical tick marks if an interval was specified in
20517      * setYConstraint().
20518      * @method setYTicks
20519      * @private
20520      */
20521     setYTicks: function(iStartY, iTickSize) {
20522         this.yTicks = [];
20523         this.yTickSize = iTickSize;
20524
20525         var tickMap = {};
20526
20527         for (var i = this.initPageY; i >= this.minY; i = i - iTickSize) {
20528             if (!tickMap[i]) {
20529                 this.yTicks[this.yTicks.length] = i;
20530                 tickMap[i] = true;
20531             }
20532         }
20533
20534         for (i = this.initPageY; i <= this.maxY; i = i + iTickSize) {
20535             if (!tickMap[i]) {
20536                 this.yTicks[this.yTicks.length] = i;
20537                 tickMap[i] = true;
20538             }
20539         }
20540
20541         this.yTicks.sort(this.DDM.numericSort) ;
20542     },
20543
20544     /**
20545      * By default, the element can be dragged any place on the screen.  Use
20546      * this method to limit the horizontal travel of the element.  Pass in
20547      * 0,0 for the parameters if you want to lock the drag to the y axis.
20548      * @method setXConstraint
20549      * @param {int} iLeft the number of pixels the element can move to the left
20550      * @param {int} iRight the number of pixels the element can move to the
20551      * right
20552      * @param {int} iTickSize optional parameter for specifying that the
20553      * element
20554      * should move iTickSize pixels at a time.
20555      */
20556     setXConstraint: function(iLeft, iRight, iTickSize) {
20557         this.leftConstraint = iLeft;
20558         this.rightConstraint = iRight;
20559
20560         this.minX = this.initPageX - iLeft;
20561         this.maxX = this.initPageX + iRight;
20562         if (iTickSize) { this.setXTicks(this.initPageX, iTickSize); }
20563
20564         this.constrainX = true;
20565     },
20566
20567     /**
20568      * Clears any constraints applied to this instance.  Also clears ticks
20569      * since they can't exist independent of a constraint at this time.
20570      * @method clearConstraints
20571      */
20572     clearConstraints: function() {
20573         this.constrainX = false;
20574         this.constrainY = false;
20575         this.clearTicks();
20576     },
20577
20578     /**
20579      * Clears any tick interval defined for this instance
20580      * @method clearTicks
20581      */
20582     clearTicks: function() {
20583         this.xTicks = null;
20584         this.yTicks = null;
20585         this.xTickSize = 0;
20586         this.yTickSize = 0;
20587     },
20588
20589     /**
20590      * By default, the element can be dragged any place on the screen.  Set
20591      * this to limit the vertical travel of the element.  Pass in 0,0 for the
20592      * parameters if you want to lock the drag to the x axis.
20593      * @method setYConstraint
20594      * @param {int} iUp the number of pixels the element can move up
20595      * @param {int} iDown the number of pixels the element can move down
20596      * @param {int} iTickSize optional parameter for specifying that the
20597      * element should move iTickSize pixels at a time.
20598      */
20599     setYConstraint: function(iUp, iDown, iTickSize) {
20600         this.topConstraint = iUp;
20601         this.bottomConstraint = iDown;
20602
20603         this.minY = this.initPageY - iUp;
20604         this.maxY = this.initPageY + iDown;
20605         if (iTickSize) { this.setYTicks(this.initPageY, iTickSize); }
20606
20607         this.constrainY = true;
20608
20609     },
20610
20611     /**
20612      * resetConstraints must be called if you manually reposition a dd element.
20613      * @method resetConstraints
20614      * @param {boolean} maintainOffset
20615      */
20616     resetConstraints: function() {
20617
20618
20619         // Maintain offsets if necessary
20620         if (this.initPageX || this.initPageX === 0) {
20621             // figure out how much this thing has moved
20622             var dx = (this.maintainOffset) ? this.lastPageX - this.initPageX : 0;
20623             var dy = (this.maintainOffset) ? this.lastPageY - this.initPageY : 0;
20624
20625             this.setInitPosition(dx, dy);
20626
20627         // This is the first time we have detected the element's position
20628         } else {
20629             this.setInitPosition();
20630         }
20631
20632         if (this.constrainX) {
20633             this.setXConstraint( this.leftConstraint,
20634                                  this.rightConstraint,
20635                                  this.xTickSize        );
20636         }
20637
20638         if (this.constrainY) {
20639             this.setYConstraint( this.topConstraint,
20640                                  this.bottomConstraint,
20641                                  this.yTickSize         );
20642         }
20643     },
20644
20645     /**
20646      * Normally the drag element is moved pixel by pixel, but we can specify
20647      * that it move a number of pixels at a time.  This method resolves the
20648      * location when we have it set up like this.
20649      * @method getTick
20650      * @param {int} val where we want to place the object
20651      * @param {int[]} tickArray sorted array of valid points
20652      * @return {int} the closest tick
20653      * @private
20654      */
20655     getTick: function(val, tickArray) {
20656
20657         if (!tickArray) {
20658             // If tick interval is not defined, it is effectively 1 pixel,
20659             // so we return the value passed to us.
20660             return val;
20661         } else if (tickArray[0] >= val) {
20662             // The value is lower than the first tick, so we return the first
20663             // tick.
20664             return tickArray[0];
20665         } else {
20666             for (var i=0, len=tickArray.length; i<len; ++i) {
20667                 var next = i + 1;
20668                 if (tickArray[next] && tickArray[next] >= val) {
20669                     var diff1 = val - tickArray[i];
20670                     var diff2 = tickArray[next] - val;
20671                     return (diff2 > diff1) ? tickArray[i] : tickArray[next];
20672                 }
20673             }
20674
20675             // The value is larger than the last tick, so we return the last
20676             // tick.
20677             return tickArray[tickArray.length - 1];
20678         }
20679     },
20680
20681     /**
20682      * toString method
20683      * @method toString
20684      * @return {string} string representation of the dd obj
20685      */
20686     toString: function() {
20687         return ("DragDrop " + this.id);
20688     }
20689
20690 });
20691
20692 })();
20693 /*
20694  * Based on:
20695  * Ext JS Library 1.1.1
20696  * Copyright(c) 2006-2007, Ext JS, LLC.
20697  *
20698  * Originally Released Under LGPL - original licence link has changed is not relivant.
20699  *
20700  * Fork - LGPL
20701  * <script type="text/javascript">
20702  */
20703
20704
20705 /**
20706  * The drag and drop utility provides a framework for building drag and drop
20707  * applications.  In addition to enabling drag and drop for specific elements,
20708  * the drag and drop elements are tracked by the manager class, and the
20709  * interactions between the various elements are tracked during the drag and
20710  * the implementing code is notified about these important moments.
20711  */
20712
20713 // Only load the library once.  Rewriting the manager class would orphan
20714 // existing drag and drop instances.
20715 if (!Roo.dd.DragDropMgr) {
20716
20717 /**
20718  * @class Roo.dd.DragDropMgr
20719  * DragDropMgr is a singleton that tracks the element interaction for
20720  * all DragDrop items in the window.  Generally, you will not call
20721  * this class directly, but it does have helper methods that could
20722  * be useful in your DragDrop implementations.
20723  * @static
20724  */
20725 Roo.dd.DragDropMgr = function() {
20726
20727     var Event = Roo.EventManager;
20728
20729     return {
20730
20731         /**
20732          * Two dimensional Array of registered DragDrop objects.  The first
20733          * dimension is the DragDrop item group, the second the DragDrop
20734          * object.
20735          * @property ids
20736          * @type {string: string}
20737          * @private
20738          * @static
20739          */
20740         ids: {},
20741
20742         /**
20743          * Array of element ids defined as drag handles.  Used to determine
20744          * if the element that generated the mousedown event is actually the
20745          * handle and not the html element itself.
20746          * @property handleIds
20747          * @type {string: string}
20748          * @private
20749          * @static
20750          */
20751         handleIds: {},
20752
20753         /**
20754          * the DragDrop object that is currently being dragged
20755          * @property dragCurrent
20756          * @type DragDrop
20757          * @private
20758          * @static
20759          **/
20760         dragCurrent: null,
20761
20762         /**
20763          * the DragDrop object(s) that are being hovered over
20764          * @property dragOvers
20765          * @type Array
20766          * @private
20767          * @static
20768          */
20769         dragOvers: {},
20770
20771         /**
20772          * the X distance between the cursor and the object being dragged
20773          * @property deltaX
20774          * @type int
20775          * @private
20776          * @static
20777          */
20778         deltaX: 0,
20779
20780         /**
20781          * the Y distance between the cursor and the object being dragged
20782          * @property deltaY
20783          * @type int
20784          * @private
20785          * @static
20786          */
20787         deltaY: 0,
20788
20789         /**
20790          * Flag to determine if we should prevent the default behavior of the
20791          * events we define. By default this is true, but this can be set to
20792          * false if you need the default behavior (not recommended)
20793          * @property preventDefault
20794          * @type boolean
20795          * @static
20796          */
20797         preventDefault: true,
20798
20799         /**
20800          * Flag to determine if we should stop the propagation of the events
20801          * we generate. This is true by default but you may want to set it to
20802          * false if the html element contains other features that require the
20803          * mouse click.
20804          * @property stopPropagation
20805          * @type boolean
20806          * @static
20807          */
20808         stopPropagation: true,
20809
20810         /**
20811          * Internal flag that is set to true when drag and drop has been
20812          * intialized
20813          * @property initialized
20814          * @private
20815          * @static
20816          */
20817         initalized: false,
20818
20819         /**
20820          * All drag and drop can be disabled.
20821          * @property locked
20822          * @private
20823          * @static
20824          */
20825         locked: false,
20826
20827         /**
20828          * Called the first time an element is registered.
20829          * @method init
20830          * @private
20831          * @static
20832          */
20833         init: function() {
20834             this.initialized = true;
20835         },
20836
20837         /**
20838          * In point mode, drag and drop interaction is defined by the
20839          * location of the cursor during the drag/drop
20840          * @property POINT
20841          * @type int
20842          * @static
20843          */
20844         POINT: 0,
20845
20846         /**
20847          * In intersect mode, drag and drop interactio nis defined by the
20848          * overlap of two or more drag and drop objects.
20849          * @property INTERSECT
20850          * @type int
20851          * @static
20852          */
20853         INTERSECT: 1,
20854
20855         /**
20856          * The current drag and drop mode.  Default: POINT
20857          * @property mode
20858          * @type int
20859          * @static
20860          */
20861         mode: 0,
20862
20863         /**
20864          * Runs method on all drag and drop objects
20865          * @method _execOnAll
20866          * @private
20867          * @static
20868          */
20869         _execOnAll: function(sMethod, args) {
20870             for (var i in this.ids) {
20871                 for (var j in this.ids[i]) {
20872                     var oDD = this.ids[i][j];
20873                     if (! this.isTypeOfDD(oDD)) {
20874                         continue;
20875                     }
20876                     oDD[sMethod].apply(oDD, args);
20877                 }
20878             }
20879         },
20880
20881         /**
20882          * Drag and drop initialization.  Sets up the global event handlers
20883          * @method _onLoad
20884          * @private
20885          * @static
20886          */
20887         _onLoad: function() {
20888
20889             this.init();
20890
20891             if (!Roo.isTouch) {
20892                 Event.on(document, "mouseup",   this.handleMouseUp, this, true);
20893                 Event.on(document, "mousemove", this.handleMouseMove, this, true);
20894             }
20895             Event.on(document, "touchend",   this.handleMouseUp, this, true);
20896             Event.on(document, "touchmove", this.handleMouseMove, this, true);
20897             
20898             Event.on(window,   "unload",    this._onUnload, this, true);
20899             Event.on(window,   "resize",    this._onResize, this, true);
20900             // Event.on(window,   "mouseout",    this._test);
20901
20902         },
20903
20904         /**
20905          * Reset constraints on all drag and drop objs
20906          * @method _onResize
20907          * @private
20908          * @static
20909          */
20910         _onResize: function(e) {
20911             this._execOnAll("resetConstraints", []);
20912         },
20913
20914         /**
20915          * Lock all drag and drop functionality
20916          * @method lock
20917          * @static
20918          */
20919         lock: function() { this.locked = true; },
20920
20921         /**
20922          * Unlock all drag and drop functionality
20923          * @method unlock
20924          * @static
20925          */
20926         unlock: function() { this.locked = false; },
20927
20928         /**
20929          * Is drag and drop locked?
20930          * @method isLocked
20931          * @return {boolean} True if drag and drop is locked, false otherwise.
20932          * @static
20933          */
20934         isLocked: function() { return this.locked; },
20935
20936         /**
20937          * Location cache that is set for all drag drop objects when a drag is
20938          * initiated, cleared when the drag is finished.
20939          * @property locationCache
20940          * @private
20941          * @static
20942          */
20943         locationCache: {},
20944
20945         /**
20946          * Set useCache to false if you want to force object the lookup of each
20947          * drag and drop linked element constantly during a drag.
20948          * @property useCache
20949          * @type boolean
20950          * @static
20951          */
20952         useCache: true,
20953
20954         /**
20955          * The number of pixels that the mouse needs to move after the
20956          * mousedown before the drag is initiated.  Default=3;
20957          * @property clickPixelThresh
20958          * @type int
20959          * @static
20960          */
20961         clickPixelThresh: 3,
20962
20963         /**
20964          * The number of milliseconds after the mousedown event to initiate the
20965          * drag if we don't get a mouseup event. Default=1000
20966          * @property clickTimeThresh
20967          * @type int
20968          * @static
20969          */
20970         clickTimeThresh: 350,
20971
20972         /**
20973          * Flag that indicates that either the drag pixel threshold or the
20974          * mousdown time threshold has been met
20975          * @property dragThreshMet
20976          * @type boolean
20977          * @private
20978          * @static
20979          */
20980         dragThreshMet: false,
20981
20982         /**
20983          * Timeout used for the click time threshold
20984          * @property clickTimeout
20985          * @type Object
20986          * @private
20987          * @static
20988          */
20989         clickTimeout: null,
20990
20991         /**
20992          * The X position of the mousedown event stored for later use when a
20993          * drag threshold is met.
20994          * @property startX
20995          * @type int
20996          * @private
20997          * @static
20998          */
20999         startX: 0,
21000
21001         /**
21002          * The Y position of the mousedown event stored for later use when a
21003          * drag threshold is met.
21004          * @property startY
21005          * @type int
21006          * @private
21007          * @static
21008          */
21009         startY: 0,
21010
21011         /**
21012          * Each DragDrop instance must be registered with the DragDropMgr.
21013          * This is executed in DragDrop.init()
21014          * @method regDragDrop
21015          * @param {DragDrop} oDD the DragDrop object to register
21016          * @param {String} sGroup the name of the group this element belongs to
21017          * @static
21018          */
21019         regDragDrop: function(oDD, sGroup) {
21020             if (!this.initialized) { this.init(); }
21021
21022             if (!this.ids[sGroup]) {
21023                 this.ids[sGroup] = {};
21024             }
21025             this.ids[sGroup][oDD.id] = oDD;
21026         },
21027
21028         /**
21029          * Removes the supplied dd instance from the supplied group. Executed
21030          * by DragDrop.removeFromGroup, so don't call this function directly.
21031          * @method removeDDFromGroup
21032          * @private
21033          * @static
21034          */
21035         removeDDFromGroup: function(oDD, sGroup) {
21036             if (!this.ids[sGroup]) {
21037                 this.ids[sGroup] = {};
21038             }
21039
21040             var obj = this.ids[sGroup];
21041             if (obj && obj[oDD.id]) {
21042                 delete obj[oDD.id];
21043             }
21044         },
21045
21046         /**
21047          * Unregisters a drag and drop item.  This is executed in
21048          * DragDrop.unreg, use that method instead of calling this directly.
21049          * @method _remove
21050          * @private
21051          * @static
21052          */
21053         _remove: function(oDD) {
21054             for (var g in oDD.groups) {
21055                 if (g && this.ids[g][oDD.id]) {
21056                     delete this.ids[g][oDD.id];
21057                 }
21058             }
21059             delete this.handleIds[oDD.id];
21060         },
21061
21062         /**
21063          * Each DragDrop handle element must be registered.  This is done
21064          * automatically when executing DragDrop.setHandleElId()
21065          * @method regHandle
21066          * @param {String} sDDId the DragDrop id this element is a handle for
21067          * @param {String} sHandleId the id of the element that is the drag
21068          * handle
21069          * @static
21070          */
21071         regHandle: function(sDDId, sHandleId) {
21072             if (!this.handleIds[sDDId]) {
21073                 this.handleIds[sDDId] = {};
21074             }
21075             this.handleIds[sDDId][sHandleId] = sHandleId;
21076         },
21077
21078         /**
21079          * Utility function to determine if a given element has been
21080          * registered as a drag drop item.
21081          * @method isDragDrop
21082          * @param {String} id the element id to check
21083          * @return {boolean} true if this element is a DragDrop item,
21084          * false otherwise
21085          * @static
21086          */
21087         isDragDrop: function(id) {
21088             return ( this.getDDById(id) ) ? true : false;
21089         },
21090
21091         /**
21092          * Returns the drag and drop instances that are in all groups the
21093          * passed in instance belongs to.
21094          * @method getRelated
21095          * @param {DragDrop} p_oDD the obj to get related data for
21096          * @param {boolean} bTargetsOnly if true, only return targetable objs
21097          * @return {DragDrop[]} the related instances
21098          * @static
21099          */
21100         getRelated: function(p_oDD, bTargetsOnly) {
21101             var oDDs = [];
21102             for (var i in p_oDD.groups) {
21103                 for (j in this.ids[i]) {
21104                     var dd = this.ids[i][j];
21105                     if (! this.isTypeOfDD(dd)) {
21106                         continue;
21107                     }
21108                     if (!bTargetsOnly || dd.isTarget) {
21109                         oDDs[oDDs.length] = dd;
21110                     }
21111                 }
21112             }
21113
21114             return oDDs;
21115         },
21116
21117         /**
21118          * Returns true if the specified dd target is a legal target for
21119          * the specifice drag obj
21120          * @method isLegalTarget
21121          * @param {DragDrop} the drag obj
21122          * @param {DragDrop} the target
21123          * @return {boolean} true if the target is a legal target for the
21124          * dd obj
21125          * @static
21126          */
21127         isLegalTarget: function (oDD, oTargetDD) {
21128             var targets = this.getRelated(oDD, true);
21129             for (var i=0, len=targets.length;i<len;++i) {
21130                 if (targets[i].id == oTargetDD.id) {
21131                     return true;
21132                 }
21133             }
21134
21135             return false;
21136         },
21137
21138         /**
21139          * My goal is to be able to transparently determine if an object is
21140          * typeof DragDrop, and the exact subclass of DragDrop.  typeof
21141          * returns "object", oDD.constructor.toString() always returns
21142          * "DragDrop" and not the name of the subclass.  So for now it just
21143          * evaluates a well-known variable in DragDrop.
21144          * @method isTypeOfDD
21145          * @param {Object} the object to evaluate
21146          * @return {boolean} true if typeof oDD = DragDrop
21147          * @static
21148          */
21149         isTypeOfDD: function (oDD) {
21150             return (oDD && oDD.__ygDragDrop);
21151         },
21152
21153         /**
21154          * Utility function to determine if a given element has been
21155          * registered as a drag drop handle for the given Drag Drop object.
21156          * @method isHandle
21157          * @param {String} id the element id to check
21158          * @return {boolean} true if this element is a DragDrop handle, false
21159          * otherwise
21160          * @static
21161          */
21162         isHandle: function(sDDId, sHandleId) {
21163             return ( this.handleIds[sDDId] &&
21164                             this.handleIds[sDDId][sHandleId] );
21165         },
21166
21167         /**
21168          * Returns the DragDrop instance for a given id
21169          * @method getDDById
21170          * @param {String} id the id of the DragDrop object
21171          * @return {DragDrop} the drag drop object, null if it is not found
21172          * @static
21173          */
21174         getDDById: function(id) {
21175             for (var i in this.ids) {
21176                 if (this.ids[i][id]) {
21177                     return this.ids[i][id];
21178                 }
21179             }
21180             return null;
21181         },
21182
21183         /**
21184          * Fired after a registered DragDrop object gets the mousedown event.
21185          * Sets up the events required to track the object being dragged
21186          * @method handleMouseDown
21187          * @param {Event} e the event
21188          * @param oDD the DragDrop object being dragged
21189          * @private
21190          * @static
21191          */
21192         handleMouseDown: function(e, oDD) {
21193             if(Roo.QuickTips){
21194                 Roo.QuickTips.disable();
21195             }
21196             this.currentTarget = e.getTarget();
21197
21198             this.dragCurrent = oDD;
21199
21200             var el = oDD.getEl();
21201
21202             // track start position
21203             this.startX = e.getPageX();
21204             this.startY = e.getPageY();
21205
21206             this.deltaX = this.startX - el.offsetLeft;
21207             this.deltaY = this.startY - el.offsetTop;
21208
21209             this.dragThreshMet = false;
21210
21211             this.clickTimeout = setTimeout(
21212                     function() {
21213                         var DDM = Roo.dd.DDM;
21214                         DDM.startDrag(DDM.startX, DDM.startY);
21215                     },
21216                     this.clickTimeThresh );
21217         },
21218
21219         /**
21220          * Fired when either the drag pixel threshol or the mousedown hold
21221          * time threshold has been met.
21222          * @method startDrag
21223          * @param x {int} the X position of the original mousedown
21224          * @param y {int} the Y position of the original mousedown
21225          * @static
21226          */
21227         startDrag: function(x, y) {
21228             clearTimeout(this.clickTimeout);
21229             if (this.dragCurrent) {
21230                 this.dragCurrent.b4StartDrag(x, y);
21231                 this.dragCurrent.startDrag(x, y);
21232             }
21233             this.dragThreshMet = true;
21234         },
21235
21236         /**
21237          * Internal function to handle the mouseup event.  Will be invoked
21238          * from the context of the document.
21239          * @method handleMouseUp
21240          * @param {Event} e the event
21241          * @private
21242          * @static
21243          */
21244         handleMouseUp: function(e) {
21245
21246             if(Roo.QuickTips){
21247                 Roo.QuickTips.enable();
21248             }
21249             if (! this.dragCurrent) {
21250                 return;
21251             }
21252
21253             clearTimeout(this.clickTimeout);
21254
21255             if (this.dragThreshMet) {
21256                 this.fireEvents(e, true);
21257             } else {
21258             }
21259
21260             this.stopDrag(e);
21261
21262             this.stopEvent(e);
21263         },
21264
21265         /**
21266          * Utility to stop event propagation and event default, if these
21267          * features are turned on.
21268          * @method stopEvent
21269          * @param {Event} e the event as returned by this.getEvent()
21270          * @static
21271          */
21272         stopEvent: function(e){
21273             if(this.stopPropagation) {
21274                 e.stopPropagation();
21275             }
21276
21277             if (this.preventDefault) {
21278                 e.preventDefault();
21279             }
21280         },
21281
21282         /**
21283          * Internal function to clean up event handlers after the drag
21284          * operation is complete
21285          * @method stopDrag
21286          * @param {Event} e the event
21287          * @private
21288          * @static
21289          */
21290         stopDrag: function(e) {
21291             // Fire the drag end event for the item that was dragged
21292             if (this.dragCurrent) {
21293                 if (this.dragThreshMet) {
21294                     this.dragCurrent.b4EndDrag(e);
21295                     this.dragCurrent.endDrag(e);
21296                 }
21297
21298                 this.dragCurrent.onMouseUp(e);
21299             }
21300
21301             this.dragCurrent = null;
21302             this.dragOvers = {};
21303         },
21304
21305         /**
21306          * Internal function to handle the mousemove event.  Will be invoked
21307          * from the context of the html element.
21308          *
21309          * @TODO figure out what we can do about mouse events lost when the
21310          * user drags objects beyond the window boundary.  Currently we can
21311          * detect this in internet explorer by verifying that the mouse is
21312          * down during the mousemove event.  Firefox doesn't give us the
21313          * button state on the mousemove event.
21314          * @method handleMouseMove
21315          * @param {Event} e the event
21316          * @private
21317          * @static
21318          */
21319         handleMouseMove: function(e) {
21320             if (! this.dragCurrent) {
21321                 return true;
21322             }
21323
21324             // var button = e.which || e.button;
21325
21326             // check for IE mouseup outside of page boundary
21327             if (Roo.isIE && (e.button !== 0 && e.button !== 1 && e.button !== 2)) {
21328                 this.stopEvent(e);
21329                 return this.handleMouseUp(e);
21330             }
21331
21332             if (!this.dragThreshMet) {
21333                 var diffX = Math.abs(this.startX - e.getPageX());
21334                 var diffY = Math.abs(this.startY - e.getPageY());
21335                 if (diffX > this.clickPixelThresh ||
21336                             diffY > this.clickPixelThresh) {
21337                     this.startDrag(this.startX, this.startY);
21338                 }
21339             }
21340
21341             if (this.dragThreshMet) {
21342                 this.dragCurrent.b4Drag(e);
21343                 this.dragCurrent.onDrag(e);
21344                 if(!this.dragCurrent.moveOnly){
21345                     this.fireEvents(e, false);
21346                 }
21347             }
21348
21349             this.stopEvent(e);
21350
21351             return true;
21352         },
21353
21354         /**
21355          * Iterates over all of the DragDrop elements to find ones we are
21356          * hovering over or dropping on
21357          * @method fireEvents
21358          * @param {Event} e the event
21359          * @param {boolean} isDrop is this a drop op or a mouseover op?
21360          * @private
21361          * @static
21362          */
21363         fireEvents: function(e, isDrop) {
21364             var dc = this.dragCurrent;
21365
21366             // If the user did the mouse up outside of the window, we could
21367             // get here even though we have ended the drag.
21368             if (!dc || dc.isLocked()) {
21369                 return;
21370             }
21371
21372             var pt = e.getPoint();
21373
21374             // cache the previous dragOver array
21375             var oldOvers = [];
21376
21377             var outEvts   = [];
21378             var overEvts  = [];
21379             var dropEvts  = [];
21380             var enterEvts = [];
21381
21382             // Check to see if the object(s) we were hovering over is no longer
21383             // being hovered over so we can fire the onDragOut event
21384             for (var i in this.dragOvers) {
21385
21386                 var ddo = this.dragOvers[i];
21387
21388                 if (! this.isTypeOfDD(ddo)) {
21389                     continue;
21390                 }
21391
21392                 if (! this.isOverTarget(pt, ddo, this.mode)) {
21393                     outEvts.push( ddo );
21394                 }
21395
21396                 oldOvers[i] = true;
21397                 delete this.dragOvers[i];
21398             }
21399
21400             for (var sGroup in dc.groups) {
21401
21402                 if ("string" != typeof sGroup) {
21403                     continue;
21404                 }
21405
21406                 for (i in this.ids[sGroup]) {
21407                     var oDD = this.ids[sGroup][i];
21408                     if (! this.isTypeOfDD(oDD)) {
21409                         continue;
21410                     }
21411
21412                     if (oDD.isTarget && !oDD.isLocked() && oDD != dc) {
21413                         if (this.isOverTarget(pt, oDD, this.mode)) {
21414                             // look for drop interactions
21415                             if (isDrop) {
21416                                 dropEvts.push( oDD );
21417                             // look for drag enter and drag over interactions
21418                             } else {
21419
21420                                 // initial drag over: dragEnter fires
21421                                 if (!oldOvers[oDD.id]) {
21422                                     enterEvts.push( oDD );
21423                                 // subsequent drag overs: dragOver fires
21424                                 } else {
21425                                     overEvts.push( oDD );
21426                                 }
21427
21428                                 this.dragOvers[oDD.id] = oDD;
21429                             }
21430                         }
21431                     }
21432                 }
21433             }
21434
21435             if (this.mode) {
21436                 if (outEvts.length) {
21437                     dc.b4DragOut(e, outEvts);
21438                     dc.onDragOut(e, outEvts);
21439                 }
21440
21441                 if (enterEvts.length) {
21442                     dc.onDragEnter(e, enterEvts);
21443                 }
21444
21445                 if (overEvts.length) {
21446                     dc.b4DragOver(e, overEvts);
21447                     dc.onDragOver(e, overEvts);
21448                 }
21449
21450                 if (dropEvts.length) {
21451                     dc.b4DragDrop(e, dropEvts);
21452                     dc.onDragDrop(e, dropEvts);
21453                 }
21454
21455             } else {
21456                 // fire dragout events
21457                 var len = 0;
21458                 for (i=0, len=outEvts.length; i<len; ++i) {
21459                     dc.b4DragOut(e, outEvts[i].id);
21460                     dc.onDragOut(e, outEvts[i].id);
21461                 }
21462
21463                 // fire enter events
21464                 for (i=0,len=enterEvts.length; i<len; ++i) {
21465                     // dc.b4DragEnter(e, oDD.id);
21466                     dc.onDragEnter(e, enterEvts[i].id);
21467                 }
21468
21469                 // fire over events
21470                 for (i=0,len=overEvts.length; i<len; ++i) {
21471                     dc.b4DragOver(e, overEvts[i].id);
21472                     dc.onDragOver(e, overEvts[i].id);
21473                 }
21474
21475                 // fire drop events
21476                 for (i=0, len=dropEvts.length; i<len; ++i) {
21477                     dc.b4DragDrop(e, dropEvts[i].id);
21478                     dc.onDragDrop(e, dropEvts[i].id);
21479                 }
21480
21481             }
21482
21483             // notify about a drop that did not find a target
21484             if (isDrop && !dropEvts.length) {
21485                 dc.onInvalidDrop(e);
21486             }
21487
21488         },
21489
21490         /**
21491          * Helper function for getting the best match from the list of drag
21492          * and drop objects returned by the drag and drop events when we are
21493          * in INTERSECT mode.  It returns either the first object that the
21494          * cursor is over, or the object that has the greatest overlap with
21495          * the dragged element.
21496          * @method getBestMatch
21497          * @param  {DragDrop[]} dds The array of drag and drop objects
21498          * targeted
21499          * @return {DragDrop}       The best single match
21500          * @static
21501          */
21502         getBestMatch: function(dds) {
21503             var winner = null;
21504             // Return null if the input is not what we expect
21505             //if (!dds || !dds.length || dds.length == 0) {
21506                // winner = null;
21507             // If there is only one item, it wins
21508             //} else if (dds.length == 1) {
21509
21510             var len = dds.length;
21511
21512             if (len == 1) {
21513                 winner = dds[0];
21514             } else {
21515                 // Loop through the targeted items
21516                 for (var i=0; i<len; ++i) {
21517                     var dd = dds[i];
21518                     // If the cursor is over the object, it wins.  If the
21519                     // cursor is over multiple matches, the first one we come
21520                     // to wins.
21521                     if (dd.cursorIsOver) {
21522                         winner = dd;
21523                         break;
21524                     // Otherwise the object with the most overlap wins
21525                     } else {
21526                         if (!winner ||
21527                             winner.overlap.getArea() < dd.overlap.getArea()) {
21528                             winner = dd;
21529                         }
21530                     }
21531                 }
21532             }
21533
21534             return winner;
21535         },
21536
21537         /**
21538          * Refreshes the cache of the top-left and bottom-right points of the
21539          * drag and drop objects in the specified group(s).  This is in the
21540          * format that is stored in the drag and drop instance, so typical
21541          * usage is:
21542          * <code>
21543          * Roo.dd.DragDropMgr.refreshCache(ddinstance.groups);
21544          * </code>
21545          * Alternatively:
21546          * <code>
21547          * Roo.dd.DragDropMgr.refreshCache({group1:true, group2:true});
21548          * </code>
21549          * @TODO this really should be an indexed array.  Alternatively this
21550          * method could accept both.
21551          * @method refreshCache
21552          * @param {Object} groups an associative array of groups to refresh
21553          * @static
21554          */
21555         refreshCache: function(groups) {
21556             for (var sGroup in groups) {
21557                 if ("string" != typeof sGroup) {
21558                     continue;
21559                 }
21560                 for (var i in this.ids[sGroup]) {
21561                     var oDD = this.ids[sGroup][i];
21562
21563                     if (this.isTypeOfDD(oDD)) {
21564                     // if (this.isTypeOfDD(oDD) && oDD.isTarget) {
21565                         var loc = this.getLocation(oDD);
21566                         if (loc) {
21567                             this.locationCache[oDD.id] = loc;
21568                         } else {
21569                             delete this.locationCache[oDD.id];
21570                             // this will unregister the drag and drop object if
21571                             // the element is not in a usable state
21572                             // oDD.unreg();
21573                         }
21574                     }
21575                 }
21576             }
21577         },
21578
21579         /**
21580          * This checks to make sure an element exists and is in the DOM.  The
21581          * main purpose is to handle cases where innerHTML is used to remove
21582          * drag and drop objects from the DOM.  IE provides an 'unspecified
21583          * error' when trying to access the offsetParent of such an element
21584          * @method verifyEl
21585          * @param {HTMLElement} el the element to check
21586          * @return {boolean} true if the element looks usable
21587          * @static
21588          */
21589         verifyEl: function(el) {
21590             if (el) {
21591                 var parent;
21592                 if(Roo.isIE){
21593                     try{
21594                         parent = el.offsetParent;
21595                     }catch(e){}
21596                 }else{
21597                     parent = el.offsetParent;
21598                 }
21599                 if (parent) {
21600                     return true;
21601                 }
21602             }
21603
21604             return false;
21605         },
21606
21607         /**
21608          * Returns a Region object containing the drag and drop element's position
21609          * and size, including the padding configured for it
21610          * @method getLocation
21611          * @param {DragDrop} oDD the drag and drop object to get the
21612          *                       location for
21613          * @return {Roo.lib.Region} a Region object representing the total area
21614          *                             the element occupies, including any padding
21615          *                             the instance is configured for.
21616          * @static
21617          */
21618         getLocation: function(oDD) {
21619             if (! this.isTypeOfDD(oDD)) {
21620                 return null;
21621             }
21622
21623             var el = oDD.getEl(), pos, x1, x2, y1, y2, t, r, b, l;
21624
21625             try {
21626                 pos= Roo.lib.Dom.getXY(el);
21627             } catch (e) { }
21628
21629             if (!pos) {
21630                 return null;
21631             }
21632
21633             x1 = pos[0];
21634             x2 = x1 + el.offsetWidth;
21635             y1 = pos[1];
21636             y2 = y1 + el.offsetHeight;
21637
21638             t = y1 - oDD.padding[0];
21639             r = x2 + oDD.padding[1];
21640             b = y2 + oDD.padding[2];
21641             l = x1 - oDD.padding[3];
21642
21643             return new Roo.lib.Region( t, r, b, l );
21644         },
21645
21646         /**
21647          * Checks the cursor location to see if it over the target
21648          * @method isOverTarget
21649          * @param {Roo.lib.Point} pt The point to evaluate
21650          * @param {DragDrop} oTarget the DragDrop object we are inspecting
21651          * @return {boolean} true if the mouse is over the target
21652          * @private
21653          * @static
21654          */
21655         isOverTarget: function(pt, oTarget, intersect) {
21656             // use cache if available
21657             var loc = this.locationCache[oTarget.id];
21658             if (!loc || !this.useCache) {
21659                 loc = this.getLocation(oTarget);
21660                 this.locationCache[oTarget.id] = loc;
21661
21662             }
21663
21664             if (!loc) {
21665                 return false;
21666             }
21667
21668             oTarget.cursorIsOver = loc.contains( pt );
21669
21670             // DragDrop is using this as a sanity check for the initial mousedown
21671             // in this case we are done.  In POINT mode, if the drag obj has no
21672             // contraints, we are also done. Otherwise we need to evaluate the
21673             // location of the target as related to the actual location of the
21674             // dragged element.
21675             var dc = this.dragCurrent;
21676             if (!dc || !dc.getTargetCoord ||
21677                     (!intersect && !dc.constrainX && !dc.constrainY)) {
21678                 return oTarget.cursorIsOver;
21679             }
21680
21681             oTarget.overlap = null;
21682
21683             // Get the current location of the drag element, this is the
21684             // location of the mouse event less the delta that represents
21685             // where the original mousedown happened on the element.  We
21686             // need to consider constraints and ticks as well.
21687             var pos = dc.getTargetCoord(pt.x, pt.y);
21688
21689             var el = dc.getDragEl();
21690             var curRegion = new Roo.lib.Region( pos.y,
21691                                                    pos.x + el.offsetWidth,
21692                                                    pos.y + el.offsetHeight,
21693                                                    pos.x );
21694
21695             var overlap = curRegion.intersect(loc);
21696
21697             if (overlap) {
21698                 oTarget.overlap = overlap;
21699                 return (intersect) ? true : oTarget.cursorIsOver;
21700             } else {
21701                 return false;
21702             }
21703         },
21704
21705         /**
21706          * unload event handler
21707          * @method _onUnload
21708          * @private
21709          * @static
21710          */
21711         _onUnload: function(e, me) {
21712             Roo.dd.DragDropMgr.unregAll();
21713         },
21714
21715         /**
21716          * Cleans up the drag and drop events and objects.
21717          * @method unregAll
21718          * @private
21719          * @static
21720          */
21721         unregAll: function() {
21722
21723             if (this.dragCurrent) {
21724                 this.stopDrag();
21725                 this.dragCurrent = null;
21726             }
21727
21728             this._execOnAll("unreg", []);
21729
21730             for (i in this.elementCache) {
21731                 delete this.elementCache[i];
21732             }
21733
21734             this.elementCache = {};
21735             this.ids = {};
21736         },
21737
21738         /**
21739          * A cache of DOM elements
21740          * @property elementCache
21741          * @private
21742          * @static
21743          */
21744         elementCache: {},
21745
21746         /**
21747          * Get the wrapper for the DOM element specified
21748          * @method getElWrapper
21749          * @param {String} id the id of the element to get
21750          * @return {Roo.dd.DDM.ElementWrapper} the wrapped element
21751          * @private
21752          * @deprecated This wrapper isn't that useful
21753          * @static
21754          */
21755         getElWrapper: function(id) {
21756             var oWrapper = this.elementCache[id];
21757             if (!oWrapper || !oWrapper.el) {
21758                 oWrapper = this.elementCache[id] =
21759                     new this.ElementWrapper(Roo.getDom(id));
21760             }
21761             return oWrapper;
21762         },
21763
21764         /**
21765          * Returns the actual DOM element
21766          * @method getElement
21767          * @param {String} id the id of the elment to get
21768          * @return {Object} The element
21769          * @deprecated use Roo.getDom instead
21770          * @static
21771          */
21772         getElement: function(id) {
21773             return Roo.getDom(id);
21774         },
21775
21776         /**
21777          * Returns the style property for the DOM element (i.e.,
21778          * document.getElById(id).style)
21779          * @method getCss
21780          * @param {String} id the id of the elment to get
21781          * @return {Object} The style property of the element
21782          * @deprecated use Roo.getDom instead
21783          * @static
21784          */
21785         getCss: function(id) {
21786             var el = Roo.getDom(id);
21787             return (el) ? el.style : null;
21788         },
21789
21790         /**
21791          * Inner class for cached elements
21792          * @class DragDropMgr.ElementWrapper
21793          * @for DragDropMgr
21794          * @private
21795          * @deprecated
21796          */
21797         ElementWrapper: function(el) {
21798                 /**
21799                  * The element
21800                  * @property el
21801                  */
21802                 this.el = el || null;
21803                 /**
21804                  * The element id
21805                  * @property id
21806                  */
21807                 this.id = this.el && el.id;
21808                 /**
21809                  * A reference to the style property
21810                  * @property css
21811                  */
21812                 this.css = this.el && el.style;
21813             },
21814
21815         /**
21816          * Returns the X position of an html element
21817          * @method getPosX
21818          * @param el the element for which to get the position
21819          * @return {int} the X coordinate
21820          * @for DragDropMgr
21821          * @deprecated use Roo.lib.Dom.getX instead
21822          * @static
21823          */
21824         getPosX: function(el) {
21825             return Roo.lib.Dom.getX(el);
21826         },
21827
21828         /**
21829          * Returns the Y position of an html element
21830          * @method getPosY
21831          * @param el the element for which to get the position
21832          * @return {int} the Y coordinate
21833          * @deprecated use Roo.lib.Dom.getY instead
21834          * @static
21835          */
21836         getPosY: function(el) {
21837             return Roo.lib.Dom.getY(el);
21838         },
21839
21840         /**
21841          * Swap two nodes.  In IE, we use the native method, for others we
21842          * emulate the IE behavior
21843          * @method swapNode
21844          * @param n1 the first node to swap
21845          * @param n2 the other node to swap
21846          * @static
21847          */
21848         swapNode: function(n1, n2) {
21849             if (n1.swapNode) {
21850                 n1.swapNode(n2);
21851             } else {
21852                 var p = n2.parentNode;
21853                 var s = n2.nextSibling;
21854
21855                 if (s == n1) {
21856                     p.insertBefore(n1, n2);
21857                 } else if (n2 == n1.nextSibling) {
21858                     p.insertBefore(n2, n1);
21859                 } else {
21860                     n1.parentNode.replaceChild(n2, n1);
21861                     p.insertBefore(n1, s);
21862                 }
21863             }
21864         },
21865
21866         /**
21867          * Returns the current scroll position
21868          * @method getScroll
21869          * @private
21870          * @static
21871          */
21872         getScroll: function () {
21873             var t, l, dde=document.documentElement, db=document.body;
21874             if (dde && (dde.scrollTop || dde.scrollLeft)) {
21875                 t = dde.scrollTop;
21876                 l = dde.scrollLeft;
21877             } else if (db) {
21878                 t = db.scrollTop;
21879                 l = db.scrollLeft;
21880             } else {
21881
21882             }
21883             return { top: t, left: l };
21884         },
21885
21886         /**
21887          * Returns the specified element style property
21888          * @method getStyle
21889          * @param {HTMLElement} el          the element
21890          * @param {string}      styleProp   the style property
21891          * @return {string} The value of the style property
21892          * @deprecated use Roo.lib.Dom.getStyle
21893          * @static
21894          */
21895         getStyle: function(el, styleProp) {
21896             return Roo.fly(el).getStyle(styleProp);
21897         },
21898
21899         /**
21900          * Gets the scrollTop
21901          * @method getScrollTop
21902          * @return {int} the document's scrollTop
21903          * @static
21904          */
21905         getScrollTop: function () { return this.getScroll().top; },
21906
21907         /**
21908          * Gets the scrollLeft
21909          * @method getScrollLeft
21910          * @return {int} the document's scrollTop
21911          * @static
21912          */
21913         getScrollLeft: function () { return this.getScroll().left; },
21914
21915         /**
21916          * Sets the x/y position of an element to the location of the
21917          * target element.
21918          * @method moveToEl
21919          * @param {HTMLElement} moveEl      The element to move
21920          * @param {HTMLElement} targetEl    The position reference element
21921          * @static
21922          */
21923         moveToEl: function (moveEl, targetEl) {
21924             var aCoord = Roo.lib.Dom.getXY(targetEl);
21925             Roo.lib.Dom.setXY(moveEl, aCoord);
21926         },
21927
21928         /**
21929          * Numeric array sort function
21930          * @method numericSort
21931          * @static
21932          */
21933         numericSort: function(a, b) { return (a - b); },
21934
21935         /**
21936          * Internal counter
21937          * @property _timeoutCount
21938          * @private
21939          * @static
21940          */
21941         _timeoutCount: 0,
21942
21943         /**
21944          * Trying to make the load order less important.  Without this we get
21945          * an error if this file is loaded before the Event Utility.
21946          * @method _addListeners
21947          * @private
21948          * @static
21949          */
21950         _addListeners: function() {
21951             var DDM = Roo.dd.DDM;
21952             if ( Roo.lib.Event && document ) {
21953                 DDM._onLoad();
21954             } else {
21955                 if (DDM._timeoutCount > 2000) {
21956                 } else {
21957                     setTimeout(DDM._addListeners, 10);
21958                     if (document && document.body) {
21959                         DDM._timeoutCount += 1;
21960                     }
21961                 }
21962             }
21963         },
21964
21965         /**
21966          * Recursively searches the immediate parent and all child nodes for
21967          * the handle element in order to determine wheter or not it was
21968          * clicked.
21969          * @method handleWasClicked
21970          * @param node the html element to inspect
21971          * @static
21972          */
21973         handleWasClicked: function(node, id) {
21974             if (this.isHandle(id, node.id)) {
21975                 return true;
21976             } else {
21977                 // check to see if this is a text node child of the one we want
21978                 var p = node.parentNode;
21979
21980                 while (p) {
21981                     if (this.isHandle(id, p.id)) {
21982                         return true;
21983                     } else {
21984                         p = p.parentNode;
21985                     }
21986                 }
21987             }
21988
21989             return false;
21990         }
21991
21992     };
21993
21994 }();
21995
21996 // shorter alias, save a few bytes
21997 Roo.dd.DDM = Roo.dd.DragDropMgr;
21998 Roo.dd.DDM._addListeners();
21999
22000 }/*
22001  * Based on:
22002  * Ext JS Library 1.1.1
22003  * Copyright(c) 2006-2007, Ext JS, LLC.
22004  *
22005  * Originally Released Under LGPL - original licence link has changed is not relivant.
22006  *
22007  * Fork - LGPL
22008  * <script type="text/javascript">
22009  */
22010
22011 /**
22012  * @class Roo.dd.DD
22013  * A DragDrop implementation where the linked element follows the
22014  * mouse cursor during a drag.
22015  * @extends Roo.dd.DragDrop
22016  * @constructor
22017  * @param {String} id the id of the linked element
22018  * @param {String} sGroup the group of related DragDrop items
22019  * @param {object} config an object containing configurable attributes
22020  *                Valid properties for DD:
22021  *                    scroll
22022  */
22023 Roo.dd.DD = function(id, sGroup, config) {
22024     if (id) {
22025         this.init(id, sGroup, config);
22026     }
22027 };
22028
22029 Roo.extend(Roo.dd.DD, Roo.dd.DragDrop, {
22030
22031     /**
22032      * When set to true, the utility automatically tries to scroll the browser
22033      * window wehn a drag and drop element is dragged near the viewport boundary.
22034      * Defaults to true.
22035      * @property scroll
22036      * @type boolean
22037      */
22038     scroll: true,
22039
22040     /**
22041      * Sets the pointer offset to the distance between the linked element's top
22042      * left corner and the location the element was clicked
22043      * @method autoOffset
22044      * @param {int} iPageX the X coordinate of the click
22045      * @param {int} iPageY the Y coordinate of the click
22046      */
22047     autoOffset: function(iPageX, iPageY) {
22048         var x = iPageX - this.startPageX;
22049         var y = iPageY - this.startPageY;
22050         this.setDelta(x, y);
22051     },
22052
22053     /**
22054      * Sets the pointer offset.  You can call this directly to force the
22055      * offset to be in a particular location (e.g., pass in 0,0 to set it
22056      * to the center of the object)
22057      * @method setDelta
22058      * @param {int} iDeltaX the distance from the left
22059      * @param {int} iDeltaY the distance from the top
22060      */
22061     setDelta: function(iDeltaX, iDeltaY) {
22062         this.deltaX = iDeltaX;
22063         this.deltaY = iDeltaY;
22064     },
22065
22066     /**
22067      * Sets the drag element to the location of the mousedown or click event,
22068      * maintaining the cursor location relative to the location on the element
22069      * that was clicked.  Override this if you want to place the element in a
22070      * location other than where the cursor is.
22071      * @method setDragElPos
22072      * @param {int} iPageX the X coordinate of the mousedown or drag event
22073      * @param {int} iPageY the Y coordinate of the mousedown or drag event
22074      */
22075     setDragElPos: function(iPageX, iPageY) {
22076         // the first time we do this, we are going to check to make sure
22077         // the element has css positioning
22078
22079         var el = this.getDragEl();
22080         this.alignElWithMouse(el, iPageX, iPageY);
22081     },
22082
22083     /**
22084      * Sets the element to the location of the mousedown or click event,
22085      * maintaining the cursor location relative to the location on the element
22086      * that was clicked.  Override this if you want to place the element in a
22087      * location other than where the cursor is.
22088      * @method alignElWithMouse
22089      * @param {HTMLElement} el the element to move
22090      * @param {int} iPageX the X coordinate of the mousedown or drag event
22091      * @param {int} iPageY the Y coordinate of the mousedown or drag event
22092      */
22093     alignElWithMouse: function(el, iPageX, iPageY) {
22094         var oCoord = this.getTargetCoord(iPageX, iPageY);
22095         var fly = el.dom ? el : Roo.fly(el);
22096         if (!this.deltaSetXY) {
22097             var aCoord = [oCoord.x, oCoord.y];
22098             fly.setXY(aCoord);
22099             var newLeft = fly.getLeft(true);
22100             var newTop  = fly.getTop(true);
22101             this.deltaSetXY = [ newLeft - oCoord.x, newTop - oCoord.y ];
22102         } else {
22103             fly.setLeftTop(oCoord.x + this.deltaSetXY[0], oCoord.y + this.deltaSetXY[1]);
22104         }
22105
22106         this.cachePosition(oCoord.x, oCoord.y);
22107         this.autoScroll(oCoord.x, oCoord.y, el.offsetHeight, el.offsetWidth);
22108         return oCoord;
22109     },
22110
22111     /**
22112      * Saves the most recent position so that we can reset the constraints and
22113      * tick marks on-demand.  We need to know this so that we can calculate the
22114      * number of pixels the element is offset from its original position.
22115      * @method cachePosition
22116      * @param iPageX the current x position (optional, this just makes it so we
22117      * don't have to look it up again)
22118      * @param iPageY the current y position (optional, this just makes it so we
22119      * don't have to look it up again)
22120      */
22121     cachePosition: function(iPageX, iPageY) {
22122         if (iPageX) {
22123             this.lastPageX = iPageX;
22124             this.lastPageY = iPageY;
22125         } else {
22126             var aCoord = Roo.lib.Dom.getXY(this.getEl());
22127             this.lastPageX = aCoord[0];
22128             this.lastPageY = aCoord[1];
22129         }
22130     },
22131
22132     /**
22133      * Auto-scroll the window if the dragged object has been moved beyond the
22134      * visible window boundary.
22135      * @method autoScroll
22136      * @param {int} x the drag element's x position
22137      * @param {int} y the drag element's y position
22138      * @param {int} h the height of the drag element
22139      * @param {int} w the width of the drag element
22140      * @private
22141      */
22142     autoScroll: function(x, y, h, w) {
22143
22144         if (this.scroll) {
22145             // The client height
22146             var clientH = Roo.lib.Dom.getViewWidth();
22147
22148             // The client width
22149             var clientW = Roo.lib.Dom.getViewHeight();
22150
22151             // The amt scrolled down
22152             var st = this.DDM.getScrollTop();
22153
22154             // The amt scrolled right
22155             var sl = this.DDM.getScrollLeft();
22156
22157             // Location of the bottom of the element
22158             var bot = h + y;
22159
22160             // Location of the right of the element
22161             var right = w + x;
22162
22163             // The distance from the cursor to the bottom of the visible area,
22164             // adjusted so that we don't scroll if the cursor is beyond the
22165             // element drag constraints
22166             var toBot = (clientH + st - y - this.deltaY);
22167
22168             // The distance from the cursor to the right of the visible area
22169             var toRight = (clientW + sl - x - this.deltaX);
22170
22171
22172             // How close to the edge the cursor must be before we scroll
22173             // var thresh = (document.all) ? 100 : 40;
22174             var thresh = 40;
22175
22176             // How many pixels to scroll per autoscroll op.  This helps to reduce
22177             // clunky scrolling. IE is more sensitive about this ... it needs this
22178             // value to be higher.
22179             var scrAmt = (document.all) ? 80 : 30;
22180
22181             // Scroll down if we are near the bottom of the visible page and the
22182             // obj extends below the crease
22183             if ( bot > clientH && toBot < thresh ) {
22184                 window.scrollTo(sl, st + scrAmt);
22185             }
22186
22187             // Scroll up if the window is scrolled down and the top of the object
22188             // goes above the top border
22189             if ( y < st && st > 0 && y - st < thresh ) {
22190                 window.scrollTo(sl, st - scrAmt);
22191             }
22192
22193             // Scroll right if the obj is beyond the right border and the cursor is
22194             // near the border.
22195             if ( right > clientW && toRight < thresh ) {
22196                 window.scrollTo(sl + scrAmt, st);
22197             }
22198
22199             // Scroll left if the window has been scrolled to the right and the obj
22200             // extends past the left border
22201             if ( x < sl && sl > 0 && x - sl < thresh ) {
22202                 window.scrollTo(sl - scrAmt, st);
22203             }
22204         }
22205     },
22206
22207     /**
22208      * Finds the location the element should be placed if we want to move
22209      * it to where the mouse location less the click offset would place us.
22210      * @method getTargetCoord
22211      * @param {int} iPageX the X coordinate of the click
22212      * @param {int} iPageY the Y coordinate of the click
22213      * @return an object that contains the coordinates (Object.x and Object.y)
22214      * @private
22215      */
22216     getTargetCoord: function(iPageX, iPageY) {
22217
22218
22219         var x = iPageX - this.deltaX;
22220         var y = iPageY - this.deltaY;
22221
22222         if (this.constrainX) {
22223             if (x < this.minX) { x = this.minX; }
22224             if (x > this.maxX) { x = this.maxX; }
22225         }
22226
22227         if (this.constrainY) {
22228             if (y < this.minY) { y = this.minY; }
22229             if (y > this.maxY) { y = this.maxY; }
22230         }
22231
22232         x = this.getTick(x, this.xTicks);
22233         y = this.getTick(y, this.yTicks);
22234
22235
22236         return {x:x, y:y};
22237     },
22238
22239     /*
22240      * Sets up config options specific to this class. Overrides
22241      * Roo.dd.DragDrop, but all versions of this method through the
22242      * inheritance chain are called
22243      */
22244     applyConfig: function() {
22245         Roo.dd.DD.superclass.applyConfig.call(this);
22246         this.scroll = (this.config.scroll !== false);
22247     },
22248
22249     /*
22250      * Event that fires prior to the onMouseDown event.  Overrides
22251      * Roo.dd.DragDrop.
22252      */
22253     b4MouseDown: function(e) {
22254         // this.resetConstraints();
22255         this.autoOffset(e.getPageX(),
22256                             e.getPageY());
22257     },
22258
22259     /*
22260      * Event that fires prior to the onDrag event.  Overrides
22261      * Roo.dd.DragDrop.
22262      */
22263     b4Drag: function(e) {
22264         this.setDragElPos(e.getPageX(),
22265                             e.getPageY());
22266     },
22267
22268     toString: function() {
22269         return ("DD " + this.id);
22270     }
22271
22272     //////////////////////////////////////////////////////////////////////////
22273     // Debugging ygDragDrop events that can be overridden
22274     //////////////////////////////////////////////////////////////////////////
22275     /*
22276     startDrag: function(x, y) {
22277     },
22278
22279     onDrag: function(e) {
22280     },
22281
22282     onDragEnter: function(e, id) {
22283     },
22284
22285     onDragOver: function(e, id) {
22286     },
22287
22288     onDragOut: function(e, id) {
22289     },
22290
22291     onDragDrop: function(e, id) {
22292     },
22293
22294     endDrag: function(e) {
22295     }
22296
22297     */
22298
22299 });/*
22300  * Based on:
22301  * Ext JS Library 1.1.1
22302  * Copyright(c) 2006-2007, Ext JS, LLC.
22303  *
22304  * Originally Released Under LGPL - original licence link has changed is not relivant.
22305  *
22306  * Fork - LGPL
22307  * <script type="text/javascript">
22308  */
22309
22310 /**
22311  * @class Roo.dd.DDProxy
22312  * A DragDrop implementation that inserts an empty, bordered div into
22313  * the document that follows the cursor during drag operations.  At the time of
22314  * the click, the frame div is resized to the dimensions of the linked html
22315  * element, and moved to the exact location of the linked element.
22316  *
22317  * References to the "frame" element refer to the single proxy element that
22318  * was created to be dragged in place of all DDProxy elements on the
22319  * page.
22320  *
22321  * @extends Roo.dd.DD
22322  * @constructor
22323  * @param {String} id the id of the linked html element
22324  * @param {String} sGroup the group of related DragDrop objects
22325  * @param {object} config an object containing configurable attributes
22326  *                Valid properties for DDProxy in addition to those in DragDrop:
22327  *                   resizeFrame, centerFrame, dragElId
22328  */
22329 Roo.dd.DDProxy = function(id, sGroup, config) {
22330     if (id) {
22331         this.init(id, sGroup, config);
22332         this.initFrame();
22333     }
22334 };
22335
22336 /**
22337  * The default drag frame div id
22338  * @property Roo.dd.DDProxy.dragElId
22339  * @type String
22340  * @static
22341  */
22342 Roo.dd.DDProxy.dragElId = "ygddfdiv";
22343
22344 Roo.extend(Roo.dd.DDProxy, Roo.dd.DD, {
22345
22346     /**
22347      * By default we resize the drag frame to be the same size as the element
22348      * we want to drag (this is to get the frame effect).  We can turn it off
22349      * if we want a different behavior.
22350      * @property resizeFrame
22351      * @type boolean
22352      */
22353     resizeFrame: true,
22354
22355     /**
22356      * By default the frame is positioned exactly where the drag element is, so
22357      * we use the cursor offset provided by Roo.dd.DD.  Another option that works only if
22358      * you do not have constraints on the obj is to have the drag frame centered
22359      * around the cursor.  Set centerFrame to true for this effect.
22360      * @property centerFrame
22361      * @type boolean
22362      */
22363     centerFrame: false,
22364
22365     /**
22366      * Creates the proxy element if it does not yet exist
22367      * @method createFrame
22368      */
22369     createFrame: function() {
22370         var self = this;
22371         var body = document.body;
22372
22373         if (!body || !body.firstChild) {
22374             setTimeout( function() { self.createFrame(); }, 50 );
22375             return;
22376         }
22377
22378         var div = this.getDragEl();
22379
22380         if (!div) {
22381             div    = document.createElement("div");
22382             div.id = this.dragElId;
22383             var s  = div.style;
22384
22385             s.position   = "absolute";
22386             s.visibility = "hidden";
22387             s.cursor     = "move";
22388             s.border     = "2px solid #aaa";
22389             s.zIndex     = 999;
22390
22391             // appendChild can blow up IE if invoked prior to the window load event
22392             // while rendering a table.  It is possible there are other scenarios
22393             // that would cause this to happen as well.
22394             body.insertBefore(div, body.firstChild);
22395         }
22396     },
22397
22398     /**
22399      * Initialization for the drag frame element.  Must be called in the
22400      * constructor of all subclasses
22401      * @method initFrame
22402      */
22403     initFrame: function() {
22404         this.createFrame();
22405     },
22406
22407     applyConfig: function() {
22408         Roo.dd.DDProxy.superclass.applyConfig.call(this);
22409
22410         this.resizeFrame = (this.config.resizeFrame !== false);
22411         this.centerFrame = (this.config.centerFrame);
22412         this.setDragElId(this.config.dragElId || Roo.dd.DDProxy.dragElId);
22413     },
22414
22415     /**
22416      * Resizes the drag frame to the dimensions of the clicked object, positions
22417      * it over the object, and finally displays it
22418      * @method showFrame
22419      * @param {int} iPageX X click position
22420      * @param {int} iPageY Y click position
22421      * @private
22422      */
22423     showFrame: function(iPageX, iPageY) {
22424         var el = this.getEl();
22425         var dragEl = this.getDragEl();
22426         var s = dragEl.style;
22427
22428         this._resizeProxy();
22429
22430         if (this.centerFrame) {
22431             this.setDelta( Math.round(parseInt(s.width,  10)/2),
22432                            Math.round(parseInt(s.height, 10)/2) );
22433         }
22434
22435         this.setDragElPos(iPageX, iPageY);
22436
22437         Roo.fly(dragEl).show();
22438     },
22439
22440     /**
22441      * The proxy is automatically resized to the dimensions of the linked
22442      * element when a drag is initiated, unless resizeFrame is set to false
22443      * @method _resizeProxy
22444      * @private
22445      */
22446     _resizeProxy: function() {
22447         if (this.resizeFrame) {
22448             var el = this.getEl();
22449             Roo.fly(this.getDragEl()).setSize(el.offsetWidth, el.offsetHeight);
22450         }
22451     },
22452
22453     // overrides Roo.dd.DragDrop
22454     b4MouseDown: function(e) {
22455         var x = e.getPageX();
22456         var y = e.getPageY();
22457         this.autoOffset(x, y);
22458         this.setDragElPos(x, y);
22459     },
22460
22461     // overrides Roo.dd.DragDrop
22462     b4StartDrag: function(x, y) {
22463         // show the drag frame
22464         this.showFrame(x, y);
22465     },
22466
22467     // overrides Roo.dd.DragDrop
22468     b4EndDrag: function(e) {
22469         Roo.fly(this.getDragEl()).hide();
22470     },
22471
22472     // overrides Roo.dd.DragDrop
22473     // By default we try to move the element to the last location of the frame.
22474     // This is so that the default behavior mirrors that of Roo.dd.DD.
22475     endDrag: function(e) {
22476
22477         var lel = this.getEl();
22478         var del = this.getDragEl();
22479
22480         // Show the drag frame briefly so we can get its position
22481         del.style.visibility = "";
22482
22483         this.beforeMove();
22484         // Hide the linked element before the move to get around a Safari
22485         // rendering bug.
22486         lel.style.visibility = "hidden";
22487         Roo.dd.DDM.moveToEl(lel, del);
22488         del.style.visibility = "hidden";
22489         lel.style.visibility = "";
22490
22491         this.afterDrag();
22492     },
22493
22494     beforeMove : function(){
22495
22496     },
22497
22498     afterDrag : function(){
22499
22500     },
22501
22502     toString: function() {
22503         return ("DDProxy " + this.id);
22504     }
22505
22506 });
22507 /*
22508  * Based on:
22509  * Ext JS Library 1.1.1
22510  * Copyright(c) 2006-2007, Ext JS, LLC.
22511  *
22512  * Originally Released Under LGPL - original licence link has changed is not relivant.
22513  *
22514  * Fork - LGPL
22515  * <script type="text/javascript">
22516  */
22517
22518  /**
22519  * @class Roo.dd.DDTarget
22520  * A DragDrop implementation that does not move, but can be a drop
22521  * target.  You would get the same result by simply omitting implementation
22522  * for the event callbacks, but this way we reduce the processing cost of the
22523  * event listener and the callbacks.
22524  * @extends Roo.dd.DragDrop
22525  * @constructor
22526  * @param {String} id the id of the element that is a drop target
22527  * @param {String} sGroup the group of related DragDrop objects
22528  * @param {object} config an object containing configurable attributes
22529  *                 Valid properties for DDTarget in addition to those in
22530  *                 DragDrop:
22531  *                    none
22532  */
22533 Roo.dd.DDTarget = function(id, sGroup, config) {
22534     if (id) {
22535         this.initTarget(id, sGroup, config);
22536     }
22537     if (config && (config.listeners || config.events)) { 
22538         Roo.dd.DragDrop.superclass.constructor.call(this,  { 
22539             listeners : config.listeners || {}, 
22540             events : config.events || {} 
22541         });    
22542     }
22543 };
22544
22545 // Roo.dd.DDTarget.prototype = new Roo.dd.DragDrop();
22546 Roo.extend(Roo.dd.DDTarget, Roo.dd.DragDrop, {
22547     toString: function() {
22548         return ("DDTarget " + this.id);
22549     }
22550 });
22551 /*
22552  * Based on:
22553  * Ext JS Library 1.1.1
22554  * Copyright(c) 2006-2007, Ext JS, LLC.
22555  *
22556  * Originally Released Under LGPL - original licence link has changed is not relivant.
22557  *
22558  * Fork - LGPL
22559  * <script type="text/javascript">
22560  */
22561  
22562
22563 /**
22564  * @class Roo.dd.ScrollManager
22565  * Provides automatic scrolling of overflow regions in the page during drag operations.<br><br>
22566  * <b>Note: This class uses "Point Mode" and is untested in "Intersect Mode".</b>
22567  * @static
22568  */
22569 Roo.dd.ScrollManager = function(){
22570     var ddm = Roo.dd.DragDropMgr;
22571     var els = {};
22572     var dragEl = null;
22573     var proc = {};
22574     
22575     
22576     
22577     var onStop = function(e){
22578         dragEl = null;
22579         clearProc();
22580     };
22581     
22582     var triggerRefresh = function(){
22583         if(ddm.dragCurrent){
22584              ddm.refreshCache(ddm.dragCurrent.groups);
22585         }
22586     };
22587     
22588     var doScroll = function(){
22589         if(ddm.dragCurrent){
22590             var dds = Roo.dd.ScrollManager;
22591             if(!dds.animate){
22592                 if(proc.el.scroll(proc.dir, dds.increment)){
22593                     triggerRefresh();
22594                 }
22595             }else{
22596                 proc.el.scroll(proc.dir, dds.increment, true, dds.animDuration, triggerRefresh);
22597             }
22598         }
22599     };
22600     
22601     var clearProc = function(){
22602         if(proc.id){
22603             clearInterval(proc.id);
22604         }
22605         proc.id = 0;
22606         proc.el = null;
22607         proc.dir = "";
22608     };
22609     
22610     var startProc = function(el, dir){
22611          Roo.log('scroll startproc');
22612         clearProc();
22613         proc.el = el;
22614         proc.dir = dir;
22615         proc.id = setInterval(doScroll, Roo.dd.ScrollManager.frequency);
22616     };
22617     
22618     var onFire = function(e, isDrop){
22619        
22620         if(isDrop || !ddm.dragCurrent){ return; }
22621         var dds = Roo.dd.ScrollManager;
22622         if(!dragEl || dragEl != ddm.dragCurrent){
22623             dragEl = ddm.dragCurrent;
22624             // refresh regions on drag start
22625             dds.refreshCache();
22626         }
22627         
22628         var xy = Roo.lib.Event.getXY(e);
22629         var pt = new Roo.lib.Point(xy[0], xy[1]);
22630         for(var id in els){
22631             var el = els[id], r = el._region;
22632             if(r && r.contains(pt) && el.isScrollable()){
22633                 if(r.bottom - pt.y <= dds.thresh){
22634                     if(proc.el != el){
22635                         startProc(el, "down");
22636                     }
22637                     return;
22638                 }else if(r.right - pt.x <= dds.thresh){
22639                     if(proc.el != el){
22640                         startProc(el, "left");
22641                     }
22642                     return;
22643                 }else if(pt.y - r.top <= dds.thresh){
22644                     if(proc.el != el){
22645                         startProc(el, "up");
22646                     }
22647                     return;
22648                 }else if(pt.x - r.left <= dds.thresh){
22649                     if(proc.el != el){
22650                         startProc(el, "right");
22651                     }
22652                     return;
22653                 }
22654             }
22655         }
22656         clearProc();
22657     };
22658     
22659     ddm.fireEvents = ddm.fireEvents.createSequence(onFire, ddm);
22660     ddm.stopDrag = ddm.stopDrag.createSequence(onStop, ddm);
22661     
22662     return {
22663         /**
22664          * Registers new overflow element(s) to auto scroll
22665          * @param {String/HTMLElement/Element/Array} el The id of or the element to be scrolled or an array of either
22666          */
22667         register : function(el){
22668             if(el instanceof Array){
22669                 for(var i = 0, len = el.length; i < len; i++) {
22670                         this.register(el[i]);
22671                 }
22672             }else{
22673                 el = Roo.get(el);
22674                 els[el.id] = el;
22675             }
22676             Roo.dd.ScrollManager.els = els;
22677         },
22678         
22679         /**
22680          * Unregisters overflow element(s) so they are no longer scrolled
22681          * @param {String/HTMLElement/Element/Array} el The id of or the element to be removed or an array of either
22682          */
22683         unregister : function(el){
22684             if(el instanceof Array){
22685                 for(var i = 0, len = el.length; i < len; i++) {
22686                         this.unregister(el[i]);
22687                 }
22688             }else{
22689                 el = Roo.get(el);
22690                 delete els[el.id];
22691             }
22692         },
22693         
22694         /**
22695          * The number of pixels from the edge of a container the pointer needs to be to 
22696          * trigger scrolling (defaults to 25)
22697          * @type Number
22698          */
22699         thresh : 25,
22700         
22701         /**
22702          * The number of pixels to scroll in each scroll increment (defaults to 50)
22703          * @type Number
22704          */
22705         increment : 100,
22706         
22707         /**
22708          * The frequency of scrolls in milliseconds (defaults to 500)
22709          * @type Number
22710          */
22711         frequency : 500,
22712         
22713         /**
22714          * True to animate the scroll (defaults to true)
22715          * @type Boolean
22716          */
22717         animate: true,
22718         
22719         /**
22720          * The animation duration in seconds - 
22721          * MUST BE less than Roo.dd.ScrollManager.frequency! (defaults to .4)
22722          * @type Number
22723          */
22724         animDuration: .4,
22725         
22726         /**
22727          * Manually trigger a cache refresh.
22728          */
22729         refreshCache : function(){
22730             for(var id in els){
22731                 if(typeof els[id] == 'object'){ // for people extending the object prototype
22732                     els[id]._region = els[id].getRegion();
22733                 }
22734             }
22735         }
22736     };
22737 }();/*
22738  * Based on:
22739  * Ext JS Library 1.1.1
22740  * Copyright(c) 2006-2007, Ext JS, LLC.
22741  *
22742  * Originally Released Under LGPL - original licence link has changed is not relivant.
22743  *
22744  * Fork - LGPL
22745  * <script type="text/javascript">
22746  */
22747  
22748
22749 /**
22750  * @class Roo.dd.Registry
22751  * Provides easy access to all drag drop components that are registered on a page.  Items can be retrieved either
22752  * directly by DOM node id, or by passing in the drag drop event that occurred and looking up the event target.
22753  * @static
22754  */
22755 Roo.dd.Registry = function(){
22756     var elements = {}; 
22757     var handles = {}; 
22758     var autoIdSeed = 0;
22759
22760     var getId = function(el, autogen){
22761         if(typeof el == "string"){
22762             return el;
22763         }
22764         var id = el.id;
22765         if(!id && autogen !== false){
22766             id = "roodd-" + (++autoIdSeed);
22767             el.id = id;
22768         }
22769         return id;
22770     };
22771     
22772     return {
22773     /**
22774      * Register a drag drop element
22775      * @param {String|HTMLElement} element The id or DOM node to register
22776      * @param {Object} data (optional) A custom data object that will be passed between the elements that are involved
22777      * in drag drop operations.  You can populate this object with any arbitrary properties that your own code
22778      * knows how to interpret, plus there are some specific properties known to the Registry that should be
22779      * populated in the data object (if applicable):
22780      * <pre>
22781 Value      Description<br />
22782 ---------  ------------------------------------------<br />
22783 handles    Array of DOM nodes that trigger dragging<br />
22784            for the element being registered<br />
22785 isHandle   True if the element passed in triggers<br />
22786            dragging itself, else false
22787 </pre>
22788      */
22789         register : function(el, data){
22790             data = data || {};
22791             if(typeof el == "string"){
22792                 el = document.getElementById(el);
22793             }
22794             data.ddel = el;
22795             elements[getId(el)] = data;
22796             if(data.isHandle !== false){
22797                 handles[data.ddel.id] = data;
22798             }
22799             if(data.handles){
22800                 var hs = data.handles;
22801                 for(var i = 0, len = hs.length; i < len; i++){
22802                         handles[getId(hs[i])] = data;
22803                 }
22804             }
22805         },
22806
22807     /**
22808      * Unregister a drag drop element
22809      * @param {String|HTMLElement}  element The id or DOM node to unregister
22810      */
22811         unregister : function(el){
22812             var id = getId(el, false);
22813             var data = elements[id];
22814             if(data){
22815                 delete elements[id];
22816                 if(data.handles){
22817                     var hs = data.handles;
22818                     for(var i = 0, len = hs.length; i < len; i++){
22819                         delete handles[getId(hs[i], false)];
22820                     }
22821                 }
22822             }
22823         },
22824
22825     /**
22826      * Returns the handle registered for a DOM Node by id
22827      * @param {String|HTMLElement} id The DOM node or id to look up
22828      * @return {Object} handle The custom handle data
22829      */
22830         getHandle : function(id){
22831             if(typeof id != "string"){ // must be element?
22832                 id = id.id;
22833             }
22834             return handles[id];
22835         },
22836
22837     /**
22838      * Returns the handle that is registered for the DOM node that is the target of the event
22839      * @param {Event} e The event
22840      * @return {Object} handle The custom handle data
22841      */
22842         getHandleFromEvent : function(e){
22843             var t = Roo.lib.Event.getTarget(e);
22844             return t ? handles[t.id] : null;
22845         },
22846
22847     /**
22848      * Returns a custom data object that is registered for a DOM node by id
22849      * @param {String|HTMLElement} id The DOM node or id to look up
22850      * @return {Object} data The custom data
22851      */
22852         getTarget : function(id){
22853             if(typeof id != "string"){ // must be element?
22854                 id = id.id;
22855             }
22856             return elements[id];
22857         },
22858
22859     /**
22860      * Returns a custom data object that is registered for the DOM node that is the target of the event
22861      * @param {Event} e The event
22862      * @return {Object} data The custom data
22863      */
22864         getTargetFromEvent : function(e){
22865             var t = Roo.lib.Event.getTarget(e);
22866             return t ? elements[t.id] || handles[t.id] : null;
22867         }
22868     };
22869 }();/*
22870  * Based on:
22871  * Ext JS Library 1.1.1
22872  * Copyright(c) 2006-2007, Ext JS, LLC.
22873  *
22874  * Originally Released Under LGPL - original licence link has changed is not relivant.
22875  *
22876  * Fork - LGPL
22877  * <script type="text/javascript">
22878  */
22879  
22880
22881 /**
22882  * @class Roo.dd.StatusProxy
22883  * A specialized drag proxy that supports a drop status icon, {@link Roo.Layer} styles and auto-repair.  This is the
22884  * default drag proxy used by all Roo.dd components.
22885  * @constructor
22886  * @param {Object} config
22887  */
22888 Roo.dd.StatusProxy = function(config){
22889     Roo.apply(this, config);
22890     this.id = this.id || Roo.id();
22891     this.el = new Roo.Layer({
22892         dh: {
22893             id: this.id, tag: "div", cls: "x-dd-drag-proxy "+this.dropNotAllowed, children: [
22894                 {tag: "div", cls: "x-dd-drop-icon"},
22895                 {tag: "div", cls: "x-dd-drag-ghost"}
22896             ]
22897         }, 
22898         shadow: !config || config.shadow !== false
22899     });
22900     this.ghost = Roo.get(this.el.dom.childNodes[1]);
22901     this.dropStatus = this.dropNotAllowed;
22902 };
22903
22904 Roo.dd.StatusProxy.prototype = {
22905     /**
22906      * @cfg {String} dropAllowed
22907      * The CSS class to apply to the status element when drop is allowed (defaults to "x-dd-drop-ok").
22908      */
22909     dropAllowed : "x-dd-drop-ok",
22910     /**
22911      * @cfg {String} dropNotAllowed
22912      * The CSS class to apply to the status element when drop is not allowed (defaults to "x-dd-drop-nodrop").
22913      */
22914     dropNotAllowed : "x-dd-drop-nodrop",
22915
22916     /**
22917      * Updates the proxy's visual element to indicate the status of whether or not drop is allowed
22918      * over the current target element.
22919      * @param {String} cssClass The css class for the new drop status indicator image
22920      */
22921     setStatus : function(cssClass){
22922         cssClass = cssClass || this.dropNotAllowed;
22923         if(this.dropStatus != cssClass){
22924             this.el.replaceClass(this.dropStatus, cssClass);
22925             this.dropStatus = cssClass;
22926         }
22927     },
22928
22929     /**
22930      * Resets the status indicator to the default dropNotAllowed value
22931      * @param {Boolean} clearGhost True to also remove all content from the ghost, false to preserve it
22932      */
22933     reset : function(clearGhost){
22934         this.el.dom.className = "x-dd-drag-proxy " + this.dropNotAllowed;
22935         this.dropStatus = this.dropNotAllowed;
22936         if(clearGhost){
22937             this.ghost.update("");
22938         }
22939     },
22940
22941     /**
22942      * Updates the contents of the ghost element
22943      * @param {String} html The html that will replace the current innerHTML of the ghost element
22944      */
22945     update : function(html){
22946         if(typeof html == "string"){
22947             this.ghost.update(html);
22948         }else{
22949             this.ghost.update("");
22950             html.style.margin = "0";
22951             this.ghost.dom.appendChild(html);
22952         }
22953         // ensure float = none set?? cant remember why though.
22954         var el = this.ghost.dom.firstChild;
22955                 if(el){
22956                         Roo.fly(el).setStyle('float', 'none');
22957                 }
22958     },
22959     
22960     /**
22961      * Returns the underlying proxy {@link Roo.Layer}
22962      * @return {Roo.Layer} el
22963     */
22964     getEl : function(){
22965         return this.el;
22966     },
22967
22968     /**
22969      * Returns the ghost element
22970      * @return {Roo.Element} el
22971      */
22972     getGhost : function(){
22973         return this.ghost;
22974     },
22975
22976     /**
22977      * Hides the proxy
22978      * @param {Boolean} clear True to reset the status and clear the ghost contents, false to preserve them
22979      */
22980     hide : function(clear){
22981         this.el.hide();
22982         if(clear){
22983             this.reset(true);
22984         }
22985     },
22986
22987     /**
22988      * Stops the repair animation if it's currently running
22989      */
22990     stop : function(){
22991         if(this.anim && this.anim.isAnimated && this.anim.isAnimated()){
22992             this.anim.stop();
22993         }
22994     },
22995
22996     /**
22997      * Displays this proxy
22998      */
22999     show : function(){
23000         this.el.show();
23001     },
23002
23003     /**
23004      * Force the Layer to sync its shadow and shim positions to the element
23005      */
23006     sync : function(){
23007         this.el.sync();
23008     },
23009
23010     /**
23011      * Causes the proxy to return to its position of origin via an animation.  Should be called after an
23012      * invalid drop operation by the item being dragged.
23013      * @param {Array} xy The XY position of the element ([x, y])
23014      * @param {Function} callback The function to call after the repair is complete
23015      * @param {Object} scope The scope in which to execute the callback
23016      */
23017     repair : function(xy, callback, scope){
23018         this.callback = callback;
23019         this.scope = scope;
23020         if(xy && this.animRepair !== false){
23021             this.el.addClass("x-dd-drag-repair");
23022             this.el.hideUnders(true);
23023             this.anim = this.el.shift({
23024                 duration: this.repairDuration || .5,
23025                 easing: 'easeOut',
23026                 xy: xy,
23027                 stopFx: true,
23028                 callback: this.afterRepair,
23029                 scope: this
23030             });
23031         }else{
23032             this.afterRepair();
23033         }
23034     },
23035
23036     // private
23037     afterRepair : function(){
23038         this.hide(true);
23039         if(typeof this.callback == "function"){
23040             this.callback.call(this.scope || this);
23041         }
23042         this.callback = null;
23043         this.scope = null;
23044     }
23045 };/*
23046  * Based on:
23047  * Ext JS Library 1.1.1
23048  * Copyright(c) 2006-2007, Ext JS, LLC.
23049  *
23050  * Originally Released Under LGPL - original licence link has changed is not relivant.
23051  *
23052  * Fork - LGPL
23053  * <script type="text/javascript">
23054  */
23055
23056 /**
23057  * @class Roo.dd.DragSource
23058  * @extends Roo.dd.DDProxy
23059  * A simple class that provides the basic implementation needed to make any element draggable.
23060  * @constructor
23061  * @param {String/HTMLElement/Element} el The container element
23062  * @param {Object} config
23063  */
23064 Roo.dd.DragSource = function(el, config){
23065     this.el = Roo.get(el);
23066     this.dragData = {};
23067     
23068     Roo.apply(this, config);
23069     
23070     if(!this.proxy){
23071         this.proxy = new Roo.dd.StatusProxy();
23072     }
23073
23074     Roo.dd.DragSource.superclass.constructor.call(this, this.el.dom, this.ddGroup || this.group,
23075           {dragElId : this.proxy.id, resizeFrame: false, isTarget: false, scroll: this.scroll === true});
23076     
23077     this.dragging = false;
23078 };
23079
23080 Roo.extend(Roo.dd.DragSource, Roo.dd.DDProxy, {
23081     /**
23082      * @cfg {String} dropAllowed
23083      * The CSS class returned to the drag source when drop is allowed (defaults to "x-dd-drop-ok").
23084      */
23085     dropAllowed : "x-dd-drop-ok",
23086     /**
23087      * @cfg {String} dropNotAllowed
23088      * The CSS class returned to the drag source when drop is not allowed (defaults to "x-dd-drop-nodrop").
23089      */
23090     dropNotAllowed : "x-dd-drop-nodrop",
23091
23092     /**
23093      * Returns the data object associated with this drag source
23094      * @return {Object} data An object containing arbitrary data
23095      */
23096     getDragData : function(e){
23097         return this.dragData;
23098     },
23099
23100     // private
23101     onDragEnter : function(e, id){
23102         var target = Roo.dd.DragDropMgr.getDDById(id);
23103         this.cachedTarget = target;
23104         if(this.beforeDragEnter(target, e, id) !== false){
23105             if(target.isNotifyTarget){
23106                 var status = target.notifyEnter(this, e, this.dragData);
23107                 this.proxy.setStatus(status);
23108             }else{
23109                 this.proxy.setStatus(this.dropAllowed);
23110             }
23111             
23112             if(this.afterDragEnter){
23113                 /**
23114                  * An empty function by default, but provided so that you can perform a custom action
23115                  * when the dragged item enters the drop target by providing an implementation.
23116                  * @param {Roo.dd.DragDrop} target The drop target
23117                  * @param {Event} e The event object
23118                  * @param {String} id The id of the dragged element
23119                  * @method afterDragEnter
23120                  */
23121                 this.afterDragEnter(target, e, id);
23122             }
23123         }
23124     },
23125
23126     /**
23127      * An empty function by default, but provided so that you can perform a custom action
23128      * before the dragged item enters the drop target and optionally cancel the onDragEnter.
23129      * @param {Roo.dd.DragDrop} target The drop target
23130      * @param {Event} e The event object
23131      * @param {String} id The id of the dragged element
23132      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
23133      */
23134     beforeDragEnter : function(target, e, id){
23135         return true;
23136     },
23137
23138     // private
23139     alignElWithMouse: function() {
23140         Roo.dd.DragSource.superclass.alignElWithMouse.apply(this, arguments);
23141         this.proxy.sync();
23142     },
23143
23144     // private
23145     onDragOver : function(e, id){
23146         var target = this.cachedTarget || Roo.dd.DragDropMgr.getDDById(id);
23147         if(this.beforeDragOver(target, e, id) !== false){
23148             if(target.isNotifyTarget){
23149                 var status = target.notifyOver(this, e, this.dragData);
23150                 this.proxy.setStatus(status);
23151             }
23152
23153             if(this.afterDragOver){
23154                 /**
23155                  * An empty function by default, but provided so that you can perform a custom action
23156                  * while the dragged item is over the drop target by providing an implementation.
23157                  * @param {Roo.dd.DragDrop} target The drop target
23158                  * @param {Event} e The event object
23159                  * @param {String} id The id of the dragged element
23160                  * @method afterDragOver
23161                  */
23162                 this.afterDragOver(target, e, id);
23163             }
23164         }
23165     },
23166
23167     /**
23168      * An empty function by default, but provided so that you can perform a custom action
23169      * while the dragged item is over the drop target and optionally cancel the onDragOver.
23170      * @param {Roo.dd.DragDrop} target The drop target
23171      * @param {Event} e The event object
23172      * @param {String} id The id of the dragged element
23173      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
23174      */
23175     beforeDragOver : function(target, e, id){
23176         return true;
23177     },
23178
23179     // private
23180     onDragOut : function(e, id){
23181         var target = this.cachedTarget || Roo.dd.DragDropMgr.getDDById(id);
23182         if(this.beforeDragOut(target, e, id) !== false){
23183             if(target.isNotifyTarget){
23184                 target.notifyOut(this, e, this.dragData);
23185             }
23186             this.proxy.reset();
23187             if(this.afterDragOut){
23188                 /**
23189                  * An empty function by default, but provided so that you can perform a custom action
23190                  * after the dragged item is dragged out of the target without dropping.
23191                  * @param {Roo.dd.DragDrop} target The drop target
23192                  * @param {Event} e The event object
23193                  * @param {String} id The id of the dragged element
23194                  * @method afterDragOut
23195                  */
23196                 this.afterDragOut(target, e, id);
23197             }
23198         }
23199         this.cachedTarget = null;
23200     },
23201
23202     /**
23203      * An empty function by default, but provided so that you can perform a custom action before the dragged
23204      * item is dragged out of the target without dropping, and optionally cancel the onDragOut.
23205      * @param {Roo.dd.DragDrop} target The drop target
23206      * @param {Event} e The event object
23207      * @param {String} id The id of the dragged element
23208      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
23209      */
23210     beforeDragOut : function(target, e, id){
23211         return true;
23212     },
23213     
23214     // private
23215     onDragDrop : function(e, id){
23216         var target = this.cachedTarget || Roo.dd.DragDropMgr.getDDById(id);
23217         if(this.beforeDragDrop(target, e, id) !== false){
23218             if(target.isNotifyTarget){
23219                 if(target.notifyDrop(this, e, this.dragData)){ // valid drop?
23220                     this.onValidDrop(target, e, id);
23221                 }else{
23222                     this.onInvalidDrop(target, e, id);
23223                 }
23224             }else{
23225                 this.onValidDrop(target, e, id);
23226             }
23227             
23228             if(this.afterDragDrop){
23229                 /**
23230                  * An empty function by default, but provided so that you can perform a custom action
23231                  * after a valid drag drop has occurred by providing an implementation.
23232                  * @param {Roo.dd.DragDrop} target The drop target
23233                  * @param {Event} e The event object
23234                  * @param {String} id The id of the dropped element
23235                  * @method afterDragDrop
23236                  */
23237                 this.afterDragDrop(target, e, id);
23238             }
23239         }
23240         delete this.cachedTarget;
23241     },
23242
23243     /**
23244      * An empty function by default, but provided so that you can perform a custom action before the dragged
23245      * item is dropped onto the target and optionally cancel the onDragDrop.
23246      * @param {Roo.dd.DragDrop} target The drop target
23247      * @param {Event} e The event object
23248      * @param {String} id The id of the dragged element
23249      * @return {Boolean} isValid True if the drag drop event is valid, else false to cancel
23250      */
23251     beforeDragDrop : function(target, e, id){
23252         return true;
23253     },
23254
23255     // private
23256     onValidDrop : function(target, e, id){
23257         this.hideProxy();
23258         if(this.afterValidDrop){
23259             /**
23260              * An empty function by default, but provided so that you can perform a custom action
23261              * after a valid drop has occurred by providing an implementation.
23262              * @param {Object} target The target DD 
23263              * @param {Event} e The event object
23264              * @param {String} id The id of the dropped element
23265              * @method afterInvalidDrop
23266              */
23267             this.afterValidDrop(target, e, id);
23268         }
23269     },
23270
23271     // private
23272     getRepairXY : function(e, data){
23273         return this.el.getXY();  
23274     },
23275
23276     // private
23277     onInvalidDrop : function(target, e, id){
23278         this.beforeInvalidDrop(target, e, id);
23279         if(this.cachedTarget){
23280             if(this.cachedTarget.isNotifyTarget){
23281                 this.cachedTarget.notifyOut(this, e, this.dragData);
23282             }
23283             this.cacheTarget = null;
23284         }
23285         this.proxy.repair(this.getRepairXY(e, this.dragData), this.afterRepair, this);
23286
23287         if(this.afterInvalidDrop){
23288             /**
23289              * An empty function by default, but provided so that you can perform a custom action
23290              * after an invalid drop has occurred by providing an implementation.
23291              * @param {Event} e The event object
23292              * @param {String} id The id of the dropped element
23293              * @method afterInvalidDrop
23294              */
23295             this.afterInvalidDrop(e, id);
23296         }
23297     },
23298
23299     // private
23300     afterRepair : function(){
23301         if(Roo.enableFx){
23302             this.el.highlight(this.hlColor || "c3daf9");
23303         }
23304         this.dragging = false;
23305     },
23306
23307     /**
23308      * An empty function by default, but provided so that you can perform a custom action after an invalid
23309      * drop has occurred.
23310      * @param {Roo.dd.DragDrop} target The drop target
23311      * @param {Event} e The event object
23312      * @param {String} id The id of the dragged element
23313      * @return {Boolean} isValid True if the invalid drop should proceed, else false to cancel
23314      */
23315     beforeInvalidDrop : function(target, e, id){
23316         return true;
23317     },
23318
23319     // private
23320     handleMouseDown : function(e){
23321         if(this.dragging) {
23322             return;
23323         }
23324         var data = this.getDragData(e);
23325         if(data && this.onBeforeDrag(data, e) !== false){
23326             this.dragData = data;
23327             this.proxy.stop();
23328             Roo.dd.DragSource.superclass.handleMouseDown.apply(this, arguments);
23329         } 
23330     },
23331
23332     /**
23333      * An empty function by default, but provided so that you can perform a custom action before the initial
23334      * drag event begins and optionally cancel it.
23335      * @param {Object} data An object containing arbitrary data to be shared with drop targets
23336      * @param {Event} e The event object
23337      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
23338      */
23339     onBeforeDrag : function(data, e){
23340         return true;
23341     },
23342
23343     /**
23344      * An empty function by default, but provided so that you can perform a custom action once the initial
23345      * drag event has begun.  The drag cannot be canceled from this function.
23346      * @param {Number} x The x position of the click on the dragged object
23347      * @param {Number} y The y position of the click on the dragged object
23348      */
23349     onStartDrag : Roo.emptyFn,
23350
23351     // private - YUI override
23352     startDrag : function(x, y){
23353         this.proxy.reset();
23354         this.dragging = true;
23355         this.proxy.update("");
23356         this.onInitDrag(x, y);
23357         this.proxy.show();
23358     },
23359
23360     // private
23361     onInitDrag : function(x, y){
23362         var clone = this.el.dom.cloneNode(true);
23363         clone.id = Roo.id(); // prevent duplicate ids
23364         this.proxy.update(clone);
23365         this.onStartDrag(x, y);
23366         return true;
23367     },
23368
23369     /**
23370      * Returns the drag source's underlying {@link Roo.dd.StatusProxy}
23371      * @return {Roo.dd.StatusProxy} proxy The StatusProxy
23372      */
23373     getProxy : function(){
23374         return this.proxy;  
23375     },
23376
23377     /**
23378      * Hides the drag source's {@link Roo.dd.StatusProxy}
23379      */
23380     hideProxy : function(){
23381         this.proxy.hide();  
23382         this.proxy.reset(true);
23383         this.dragging = false;
23384     },
23385
23386     // private
23387     triggerCacheRefresh : function(){
23388         Roo.dd.DDM.refreshCache(this.groups);
23389     },
23390
23391     // private - override to prevent hiding
23392     b4EndDrag: function(e) {
23393     },
23394
23395     // private - override to prevent moving
23396     endDrag : function(e){
23397         this.onEndDrag(this.dragData, e);
23398     },
23399
23400     // private
23401     onEndDrag : function(data, e){
23402     },
23403     
23404     // private - pin to cursor
23405     autoOffset : function(x, y) {
23406         this.setDelta(-12, -20);
23407     }    
23408 });/*
23409  * Based on:
23410  * Ext JS Library 1.1.1
23411  * Copyright(c) 2006-2007, Ext JS, LLC.
23412  *
23413  * Originally Released Under LGPL - original licence link has changed is not relivant.
23414  *
23415  * Fork - LGPL
23416  * <script type="text/javascript">
23417  */
23418
23419
23420 /**
23421  * @class Roo.dd.DropTarget
23422  * @extends Roo.dd.DDTarget
23423  * A simple class that provides the basic implementation needed to make any element a drop target that can have
23424  * draggable items dropped onto it.  The drop has no effect until an implementation of notifyDrop is provided.
23425  * @constructor
23426  * @param {String/HTMLElement/Element} el The container element
23427  * @param {Object} config
23428  */
23429 Roo.dd.DropTarget = function(el, config){
23430     this.el = Roo.get(el);
23431     
23432     var listeners = false; ;
23433     if (config && config.listeners) {
23434         listeners= config.listeners;
23435         delete config.listeners;
23436     }
23437     Roo.apply(this, config);
23438     
23439     if(this.containerScroll){
23440         Roo.dd.ScrollManager.register(this.el);
23441     }
23442     this.addEvents( {
23443          /**
23444          * @scope Roo.dd.DropTarget
23445          */
23446          
23447          /**
23448          * @event enter
23449          * The function a {@link Roo.dd.DragSource} calls once to notify this drop target that the source is now over the
23450          * target.  This default implementation adds the CSS class specified by overClass (if any) to the drop element
23451          * and returns the dropAllowed config value.  This method should be overridden if drop validation is required.
23452          * 
23453          * IMPORTANT : it should set  this.valid to true|false
23454          * 
23455          * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
23456          * @param {Event} e The event
23457          * @param {Object} data An object containing arbitrary data supplied by the drag source
23458          */
23459         "enter" : true,
23460         
23461          /**
23462          * @event over
23463          * The function a {@link Roo.dd.DragSource} calls continuously while it is being dragged over the target.
23464          * This method will be called on every mouse movement while the drag source is over the drop target.
23465          * This default implementation simply returns the dropAllowed config value.
23466          * 
23467          * IMPORTANT : it should set  this.valid to true|false
23468          * 
23469          * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
23470          * @param {Event} e The event
23471          * @param {Object} data An object containing arbitrary data supplied by the drag source
23472          
23473          */
23474         "over" : true,
23475         /**
23476          * @event out
23477          * The function a {@link Roo.dd.DragSource} calls once to notify this drop target that the source has been dragged
23478          * out of the target without dropping.  This default implementation simply removes the CSS class specified by
23479          * overClass (if any) from the drop element.
23480          * 
23481          * 
23482          * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
23483          * @param {Event} e The event
23484          * @param {Object} data An object containing arbitrary data supplied by the drag source
23485          */
23486          "out" : true,
23487          
23488         /**
23489          * @event drop
23490          * The function a {@link Roo.dd.DragSource} calls once to notify this drop target that the dragged item has
23491          * been dropped on it.  This method has no default implementation and returns false, so you must provide an
23492          * implementation that does something to process the drop event and returns true so that the drag source's
23493          * repair action does not run.
23494          * 
23495          * IMPORTANT : it should set this.success
23496          * 
23497          * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
23498          * @param {Event} e The event
23499          * @param {Object} data An object containing arbitrary data supplied by the drag source
23500         */
23501          "drop" : true
23502     });
23503             
23504      
23505     Roo.dd.DropTarget.superclass.constructor.call(  this, 
23506         this.el.dom, 
23507         this.ddGroup || this.group,
23508         {
23509             isTarget: true,
23510             listeners : listeners || {} 
23511            
23512         
23513         }
23514     );
23515
23516 };
23517
23518 Roo.extend(Roo.dd.DropTarget, Roo.dd.DDTarget, {
23519     /**
23520      * @cfg {String} overClass
23521      * The CSS class applied to the drop target element while the drag source is over it (defaults to "").
23522      */
23523      /**
23524      * @cfg {String} ddGroup
23525      * The drag drop group to handle drop events for
23526      */
23527      
23528     /**
23529      * @cfg {String} dropAllowed
23530      * The CSS class returned to the drag source when drop is allowed (defaults to "x-dd-drop-ok").
23531      */
23532     dropAllowed : "x-dd-drop-ok",
23533     /**
23534      * @cfg {String} dropNotAllowed
23535      * The CSS class returned to the drag source when drop is not allowed (defaults to "x-dd-drop-nodrop").
23536      */
23537     dropNotAllowed : "x-dd-drop-nodrop",
23538     /**
23539      * @cfg {boolean} success
23540      * set this after drop listener.. 
23541      */
23542     success : false,
23543     /**
23544      * @cfg {boolean|String} valid true/false or string (ok-add/ok-sub/ok/nodrop)
23545      * if the drop point is valid for over/enter..
23546      */
23547     valid : false,
23548     // private
23549     isTarget : true,
23550
23551     // private
23552     isNotifyTarget : true,
23553     
23554     /**
23555      * @hide
23556      */
23557     notifyEnter : function(dd, e, data)
23558     {
23559         this.valid = true;
23560         this.fireEvent('enter', dd, e, data);
23561         if(this.overClass){
23562             this.el.addClass(this.overClass);
23563         }
23564         return typeof(this.valid) == 'string' ? 'x-dd-drop-' + this.valid : (
23565             this.valid ? this.dropAllowed : this.dropNotAllowed
23566         );
23567     },
23568
23569     /**
23570      * @hide
23571      */
23572     notifyOver : function(dd, e, data)
23573     {
23574         this.valid = true;
23575         this.fireEvent('over', dd, e, data);
23576         return typeof(this.valid) == 'string' ? 'x-dd-drop-' + this.valid : (
23577             this.valid ? this.dropAllowed : this.dropNotAllowed
23578         );
23579     },
23580
23581     /**
23582      * @hide
23583      */
23584     notifyOut : function(dd, e, data)
23585     {
23586         this.fireEvent('out', dd, e, data);
23587         if(this.overClass){
23588             this.el.removeClass(this.overClass);
23589         }
23590     },
23591
23592     /**
23593      * @hide
23594      */
23595     notifyDrop : function(dd, e, data)
23596     {
23597         this.success = false;
23598         this.fireEvent('drop', dd, e, data);
23599         return this.success;
23600     }
23601 });/*
23602  * Based on:
23603  * Ext JS Library 1.1.1
23604  * Copyright(c) 2006-2007, Ext JS, LLC.
23605  *
23606  * Originally Released Under LGPL - original licence link has changed is not relivant.
23607  *
23608  * Fork - LGPL
23609  * <script type="text/javascript">
23610  */
23611
23612
23613 /**
23614  * @class Roo.dd.DragZone
23615  * @extends Roo.dd.DragSource
23616  * This class provides a container DD instance that proxies for multiple child node sources.<br />
23617  * By default, this class requires that draggable child nodes are registered with {@link Roo.dd.Registry}.
23618  * @constructor
23619  * @param {String/HTMLElement/Element} el The container element
23620  * @param {Object} config
23621  */
23622 Roo.dd.DragZone = function(el, config){
23623     Roo.dd.DragZone.superclass.constructor.call(this, el, config);
23624     if(this.containerScroll){
23625         Roo.dd.ScrollManager.register(this.el);
23626     }
23627 };
23628
23629 Roo.extend(Roo.dd.DragZone, Roo.dd.DragSource, {
23630     /**
23631      * @cfg {Boolean} containerScroll True to register this container with the Scrollmanager
23632      * for auto scrolling during drag operations.
23633      */
23634     /**
23635      * @cfg {String} hlColor The color to use when visually highlighting the drag source in the afterRepair
23636      * method after a failed drop (defaults to "c3daf9" - light blue)
23637      */
23638
23639     /**
23640      * Called when a mousedown occurs in this container. Looks in {@link Roo.dd.Registry}
23641      * for a valid target to drag based on the mouse down. Override this method
23642      * to provide your own lookup logic (e.g. finding a child by class name). Make sure your returned
23643      * object has a "ddel" attribute (with an HTML Element) for other functions to work.
23644      * @param {EventObject} e The mouse down event
23645      * @return {Object} The dragData
23646      */
23647     getDragData : function(e){
23648         return Roo.dd.Registry.getHandleFromEvent(e);
23649     },
23650     
23651     /**
23652      * Called once drag threshold has been reached to initialize the proxy element. By default, it clones the
23653      * this.dragData.ddel
23654      * @param {Number} x The x position of the click on the dragged object
23655      * @param {Number} y The y position of the click on the dragged object
23656      * @return {Boolean} true to continue the drag, false to cancel
23657      */
23658     onInitDrag : function(x, y){
23659         this.proxy.update(this.dragData.ddel.cloneNode(true));
23660         this.onStartDrag(x, y);
23661         return true;
23662     },
23663     
23664     /**
23665      * Called after a repair of an invalid drop. By default, highlights this.dragData.ddel 
23666      */
23667     afterRepair : function(){
23668         if(Roo.enableFx){
23669             Roo.Element.fly(this.dragData.ddel).highlight(this.hlColor || "c3daf9");
23670         }
23671         this.dragging = false;
23672     },
23673
23674     /**
23675      * Called before a repair of an invalid drop to get the XY to animate to. By default returns
23676      * the XY of this.dragData.ddel
23677      * @param {EventObject} e The mouse up event
23678      * @return {Array} The xy location (e.g. [100, 200])
23679      */
23680     getRepairXY : function(e){
23681         return Roo.Element.fly(this.dragData.ddel).getXY();  
23682     }
23683 });/*
23684  * Based on:
23685  * Ext JS Library 1.1.1
23686  * Copyright(c) 2006-2007, Ext JS, LLC.
23687  *
23688  * Originally Released Under LGPL - original licence link has changed is not relivant.
23689  *
23690  * Fork - LGPL
23691  * <script type="text/javascript">
23692  */
23693 /**
23694  * @class Roo.dd.DropZone
23695  * @extends Roo.dd.DropTarget
23696  * This class provides a container DD instance that proxies for multiple child node targets.<br />
23697  * By default, this class requires that child nodes accepting drop are registered with {@link Roo.dd.Registry}.
23698  * @constructor
23699  * @param {String/HTMLElement/Element} el The container element
23700  * @param {Object} config
23701  */
23702 Roo.dd.DropZone = function(el, config){
23703     Roo.dd.DropZone.superclass.constructor.call(this, el, config);
23704 };
23705
23706 Roo.extend(Roo.dd.DropZone, Roo.dd.DropTarget, {
23707     /**
23708      * Returns a custom data object associated with the DOM node that is the target of the event.  By default
23709      * this looks up the event target in the {@link Roo.dd.Registry}, although you can override this method to
23710      * provide your own custom lookup.
23711      * @param {Event} e The event
23712      * @return {Object} data The custom data
23713      */
23714     getTargetFromEvent : function(e){
23715         return Roo.dd.Registry.getTargetFromEvent(e);
23716     },
23717
23718     /**
23719      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has entered a drop node
23720      * that it has registered.  This method has no default implementation and should be overridden to provide
23721      * node-specific processing if necessary.
23722      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from 
23723      * {@link #getTargetFromEvent} for this node)
23724      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
23725      * @param {Event} e The event
23726      * @param {Object} data An object containing arbitrary data supplied by the drag source
23727      */
23728     onNodeEnter : function(n, dd, e, data){
23729         
23730     },
23731
23732     /**
23733      * Called internally while the DropZone determines that a {@link Roo.dd.DragSource} is over a drop node
23734      * that it has registered.  The default implementation returns this.dropNotAllowed, so it should be
23735      * overridden to provide the proper feedback.
23736      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from
23737      * {@link #getTargetFromEvent} for this node)
23738      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
23739      * @param {Event} e The event
23740      * @param {Object} data An object containing arbitrary data supplied by the drag source
23741      * @return {String} status The CSS class that communicates the drop status back to the source so that the
23742      * underlying {@link Roo.dd.StatusProxy} can be updated
23743      */
23744     onNodeOver : function(n, dd, e, data){
23745         return this.dropAllowed;
23746     },
23747
23748     /**
23749      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has been dragged out of
23750      * the drop node without dropping.  This method has no default implementation and should be overridden to provide
23751      * node-specific processing if necessary.
23752      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from
23753      * {@link #getTargetFromEvent} for this node)
23754      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
23755      * @param {Event} e The event
23756      * @param {Object} data An object containing arbitrary data supplied by the drag source
23757      */
23758     onNodeOut : function(n, dd, e, data){
23759         
23760     },
23761
23762     /**
23763      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has been dropped onto
23764      * the drop node.  The default implementation returns false, so it should be overridden to provide the
23765      * appropriate processing of the drop event and return true so that the drag source's repair action does not run.
23766      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from
23767      * {@link #getTargetFromEvent} for this node)
23768      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
23769      * @param {Event} e The event
23770      * @param {Object} data An object containing arbitrary data supplied by the drag source
23771      * @return {Boolean} True if the drop was valid, else false
23772      */
23773     onNodeDrop : function(n, dd, e, data){
23774         return false;
23775     },
23776
23777     /**
23778      * Called internally while the DropZone determines that a {@link Roo.dd.DragSource} is being dragged over it,
23779      * but not over any of its registered drop nodes.  The default implementation returns this.dropNotAllowed, so
23780      * it should be overridden to provide the proper feedback if necessary.
23781      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
23782      * @param {Event} e The event
23783      * @param {Object} data An object containing arbitrary data supplied by the drag source
23784      * @return {String} status The CSS class that communicates the drop status back to the source so that the
23785      * underlying {@link Roo.dd.StatusProxy} can be updated
23786      */
23787     onContainerOver : function(dd, e, data){
23788         return this.dropNotAllowed;
23789     },
23790
23791     /**
23792      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has been dropped on it,
23793      * but not on any of its registered drop nodes.  The default implementation returns false, so it should be
23794      * overridden to provide the appropriate processing of the drop event if you need the drop zone itself to
23795      * be able to accept drops.  It should return true when valid so that the drag source's repair action does not run.
23796      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
23797      * @param {Event} e The event
23798      * @param {Object} data An object containing arbitrary data supplied by the drag source
23799      * @return {Boolean} True if the drop was valid, else false
23800      */
23801     onContainerDrop : function(dd, e, data){
23802         return false;
23803     },
23804
23805     /**
23806      * The function a {@link Roo.dd.DragSource} calls once to notify this drop zone that the source is now over
23807      * the zone.  The default implementation returns this.dropNotAllowed and expects that only registered drop
23808      * nodes can process drag drop operations, so if you need the drop zone itself to be able to process drops
23809      * you should override this method and provide a custom implementation.
23810      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
23811      * @param {Event} e The event
23812      * @param {Object} data An object containing arbitrary data supplied by the drag source
23813      * @return {String} status The CSS class that communicates the drop status back to the source so that the
23814      * underlying {@link Roo.dd.StatusProxy} can be updated
23815      */
23816     notifyEnter : function(dd, e, data){
23817         return this.dropNotAllowed;
23818     },
23819
23820     /**
23821      * The function a {@link Roo.dd.DragSource} calls continuously while it is being dragged over the drop zone.
23822      * This method will be called on every mouse movement while the drag source is over the drop zone.
23823      * It will call {@link #onNodeOver} while the drag source is over a registered node, and will also automatically
23824      * delegate to the appropriate node-specific methods as necessary when the drag source enters and exits
23825      * registered nodes ({@link #onNodeEnter}, {@link #onNodeOut}). If the drag source is not currently over a
23826      * registered node, it will call {@link #onContainerOver}.
23827      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
23828      * @param {Event} e The event
23829      * @param {Object} data An object containing arbitrary data supplied by the drag source
23830      * @return {String} status The CSS class that communicates the drop status back to the source so that the
23831      * underlying {@link Roo.dd.StatusProxy} can be updated
23832      */
23833     notifyOver : function(dd, e, data){
23834         var n = this.getTargetFromEvent(e);
23835         if(!n){ // not over valid drop target
23836             if(this.lastOverNode){
23837                 this.onNodeOut(this.lastOverNode, dd, e, data);
23838                 this.lastOverNode = null;
23839             }
23840             return this.onContainerOver(dd, e, data);
23841         }
23842         if(this.lastOverNode != n){
23843             if(this.lastOverNode){
23844                 this.onNodeOut(this.lastOverNode, dd, e, data);
23845             }
23846             this.onNodeEnter(n, dd, e, data);
23847             this.lastOverNode = n;
23848         }
23849         return this.onNodeOver(n, dd, e, data);
23850     },
23851
23852     /**
23853      * The function a {@link Roo.dd.DragSource} calls once to notify this drop zone that the source has been dragged
23854      * out of the zone without dropping.  If the drag source is currently over a registered node, the notification
23855      * will be delegated to {@link #onNodeOut} for node-specific handling, otherwise it will be ignored.
23856      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
23857      * @param {Event} e The event
23858      * @param {Object} data An object containing arbitrary data supplied by the drag zone
23859      */
23860     notifyOut : function(dd, e, data){
23861         if(this.lastOverNode){
23862             this.onNodeOut(this.lastOverNode, dd, e, data);
23863             this.lastOverNode = null;
23864         }
23865     },
23866
23867     /**
23868      * The function a {@link Roo.dd.DragSource} calls once to notify this drop zone that the dragged item has
23869      * been dropped on it.  The drag zone will look up the target node based on the event passed in, and if there
23870      * is a node registered for that event, it will delegate to {@link #onNodeDrop} for node-specific handling,
23871      * otherwise it will call {@link #onContainerDrop}.
23872      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
23873      * @param {Event} e The event
23874      * @param {Object} data An object containing arbitrary data supplied by the drag source
23875      * @return {Boolean} True if the drop was valid, else false
23876      */
23877     notifyDrop : function(dd, e, data){
23878         if(this.lastOverNode){
23879             this.onNodeOut(this.lastOverNode, dd, e, data);
23880             this.lastOverNode = null;
23881         }
23882         var n = this.getTargetFromEvent(e);
23883         return n ?
23884             this.onNodeDrop(n, dd, e, data) :
23885             this.onContainerDrop(dd, e, data);
23886     },
23887
23888     // private
23889     triggerCacheRefresh : function(){
23890         Roo.dd.DDM.refreshCache(this.groups);
23891     }  
23892 });/*
23893  * Based on:
23894  * Ext JS Library 1.1.1
23895  * Copyright(c) 2006-2007, Ext JS, LLC.
23896  *
23897  * Originally Released Under LGPL - original licence link has changed is not relivant.
23898  *
23899  * Fork - LGPL
23900  * <script type="text/javascript">
23901  */
23902
23903
23904 /**
23905  * @class Roo.data.SortTypes
23906  * @static
23907  * Defines the default sorting (casting?) comparison functions used when sorting data.
23908  */
23909 Roo.data.SortTypes = {
23910     /**
23911      * Default sort that does nothing
23912      * @param {Mixed} s The value being converted
23913      * @return {Mixed} The comparison value
23914      */
23915     none : function(s){
23916         return s;
23917     },
23918     
23919     /**
23920      * The regular expression used to strip tags
23921      * @type {RegExp}
23922      * @property
23923      */
23924     stripTagsRE : /<\/?[^>]+>/gi,
23925     
23926     /**
23927      * Strips all HTML tags to sort on text only
23928      * @param {Mixed} s The value being converted
23929      * @return {String} The comparison value
23930      */
23931     asText : function(s){
23932         return String(s).replace(this.stripTagsRE, "");
23933     },
23934     
23935     /**
23936      * Strips all HTML tags to sort on text only - Case insensitive
23937      * @param {Mixed} s The value being converted
23938      * @return {String} The comparison value
23939      */
23940     asUCText : function(s){
23941         return String(s).toUpperCase().replace(this.stripTagsRE, "");
23942     },
23943     
23944     /**
23945      * Case insensitive string
23946      * @param {Mixed} s The value being converted
23947      * @return {String} The comparison value
23948      */
23949     asUCString : function(s) {
23950         return String(s).toUpperCase();
23951     },
23952     
23953     /**
23954      * Date sorting
23955      * @param {Mixed} s The value being converted
23956      * @return {Number} The comparison value
23957      */
23958     asDate : function(s) {
23959         if(!s){
23960             return 0;
23961         }
23962         if(s instanceof Date){
23963             return s.getTime();
23964         }
23965         return Date.parse(String(s));
23966     },
23967     
23968     /**
23969      * Float sorting
23970      * @param {Mixed} s The value being converted
23971      * @return {Float} The comparison value
23972      */
23973     asFloat : function(s) {
23974         var val = parseFloat(String(s).replace(/,/g, ""));
23975         if(isNaN(val)) {
23976             val = 0;
23977         }
23978         return val;
23979     },
23980     
23981     /**
23982      * Integer sorting
23983      * @param {Mixed} s The value being converted
23984      * @return {Number} The comparison value
23985      */
23986     asInt : function(s) {
23987         var val = parseInt(String(s).replace(/,/g, ""));
23988         if(isNaN(val)) {
23989             val = 0;
23990         }
23991         return val;
23992     }
23993 };/*
23994  * Based on:
23995  * Ext JS Library 1.1.1
23996  * Copyright(c) 2006-2007, Ext JS, LLC.
23997  *
23998  * Originally Released Under LGPL - original licence link has changed is not relivant.
23999  *
24000  * Fork - LGPL
24001  * <script type="text/javascript">
24002  */
24003
24004 /**
24005 * @class Roo.data.Record
24006  * Instances of this class encapsulate both record <em>definition</em> information, and record
24007  * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
24008  * to access Records cached in an {@link Roo.data.Store} object.<br>
24009  * <p>
24010  * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
24011  * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
24012  * objects.<br>
24013  * <p>
24014  * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
24015  * @constructor
24016  * This constructor should not be used to create Record objects. Instead, use the constructor generated by
24017  * {@link #create}. The parameters are the same.
24018  * @param {Array} data An associative Array of data values keyed by the field name.
24019  * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
24020  * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
24021  * not specified an integer id is generated.
24022  */
24023 Roo.data.Record = function(data, id){
24024     this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
24025     this.data = data;
24026 };
24027
24028 /**
24029  * Generate a constructor for a specific record layout.
24030  * @param {Array} o An Array of field definition objects which specify field names, and optionally,
24031  * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
24032  * Each field definition object may contain the following properties: <ul>
24033  * <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,
24034  * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
24035  * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
24036  * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
24037  * is being used, then this is a string containing the javascript expression to reference the data relative to 
24038  * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
24039  * to the data item relative to the record element. If the mapping expression is the same as the field name,
24040  * this may be omitted.</p></li>
24041  * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
24042  * <ul><li>auto (Default, implies no conversion)</li>
24043  * <li>string</li>
24044  * <li>int</li>
24045  * <li>float</li>
24046  * <li>boolean</li>
24047  * <li>date</li></ul></p></li>
24048  * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
24049  * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
24050  * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
24051  * by the Reader into an object that will be stored in the Record. It is passed the
24052  * following parameters:<ul>
24053  * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
24054  * </ul></p></li>
24055  * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
24056  * </ul>
24057  * <br>usage:<br><pre><code>
24058 var TopicRecord = Roo.data.Record.create(
24059     {name: 'title', mapping: 'topic_title'},
24060     {name: 'author', mapping: 'username'},
24061     {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
24062     {name: 'lastPost', mapping: 'post_time', type: 'date'},
24063     {name: 'lastPoster', mapping: 'user2'},
24064     {name: 'excerpt', mapping: 'post_text'}
24065 );
24066
24067 var myNewRecord = new TopicRecord({
24068     title: 'Do my job please',
24069     author: 'noobie',
24070     totalPosts: 1,
24071     lastPost: new Date(),
24072     lastPoster: 'Animal',
24073     excerpt: 'No way dude!'
24074 });
24075 myStore.add(myNewRecord);
24076 </code></pre>
24077  * @method create
24078  * @static
24079  */
24080 Roo.data.Record.create = function(o){
24081     var f = function(){
24082         f.superclass.constructor.apply(this, arguments);
24083     };
24084     Roo.extend(f, Roo.data.Record);
24085     var p = f.prototype;
24086     p.fields = new Roo.util.MixedCollection(false, function(field){
24087         return field.name;
24088     });
24089     for(var i = 0, len = o.length; i < len; i++){
24090         p.fields.add(new Roo.data.Field(o[i]));
24091     }
24092     f.getField = function(name){
24093         return p.fields.get(name);  
24094     };
24095     return f;
24096 };
24097
24098 Roo.data.Record.AUTO_ID = 1000;
24099 Roo.data.Record.EDIT = 'edit';
24100 Roo.data.Record.REJECT = 'reject';
24101 Roo.data.Record.COMMIT = 'commit';
24102
24103 Roo.data.Record.prototype = {
24104     /**
24105      * Readonly flag - true if this record has been modified.
24106      * @type Boolean
24107      */
24108     dirty : false,
24109     editing : false,
24110     error: null,
24111     modified: null,
24112
24113     // private
24114     join : function(store){
24115         this.store = store;
24116     },
24117
24118     /**
24119      * Set the named field to the specified value.
24120      * @param {String} name The name of the field to set.
24121      * @param {Object} value The value to set the field to.
24122      */
24123     set : function(name, value){
24124         if(this.data[name] == value){
24125             return;
24126         }
24127         this.dirty = true;
24128         if(!this.modified){
24129             this.modified = {};
24130         }
24131         if(typeof this.modified[name] == 'undefined'){
24132             this.modified[name] = this.data[name];
24133         }
24134         this.data[name] = value;
24135         if(!this.editing && this.store){
24136             this.store.afterEdit(this);
24137         }       
24138     },
24139
24140     /**
24141      * Get the value of the named field.
24142      * @param {String} name The name of the field to get the value of.
24143      * @return {Object} The value of the field.
24144      */
24145     get : function(name){
24146         return this.data[name]; 
24147     },
24148
24149     // private
24150     beginEdit : function(){
24151         this.editing = true;
24152         this.modified = {}; 
24153     },
24154
24155     // private
24156     cancelEdit : function(){
24157         this.editing = false;
24158         delete this.modified;
24159     },
24160
24161     // private
24162     endEdit : function(){
24163         this.editing = false;
24164         if(this.dirty && this.store){
24165             this.store.afterEdit(this);
24166         }
24167     },
24168
24169     /**
24170      * Usually called by the {@link Roo.data.Store} which owns the Record.
24171      * Rejects all changes made to the Record since either creation, or the last commit operation.
24172      * Modified fields are reverted to their original values.
24173      * <p>
24174      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
24175      * of reject operations.
24176      */
24177     reject : function(){
24178         var m = this.modified;
24179         for(var n in m){
24180             if(typeof m[n] != "function"){
24181                 this.data[n] = m[n];
24182             }
24183         }
24184         this.dirty = false;
24185         delete this.modified;
24186         this.editing = false;
24187         if(this.store){
24188             this.store.afterReject(this);
24189         }
24190     },
24191
24192     /**
24193      * Usually called by the {@link Roo.data.Store} which owns the Record.
24194      * Commits all changes made to the Record since either creation, or the last commit operation.
24195      * <p>
24196      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
24197      * of commit operations.
24198      */
24199     commit : function(){
24200         this.dirty = false;
24201         delete this.modified;
24202         this.editing = false;
24203         if(this.store){
24204             this.store.afterCommit(this);
24205         }
24206     },
24207
24208     // private
24209     hasError : function(){
24210         return this.error != null;
24211     },
24212
24213     // private
24214     clearError : function(){
24215         this.error = null;
24216     },
24217
24218     /**
24219      * Creates a copy of this record.
24220      * @param {String} id (optional) A new record id if you don't want to use this record's id
24221      * @return {Record}
24222      */
24223     copy : function(newId) {
24224         return new this.constructor(Roo.apply({}, this.data), newId || this.id);
24225     }
24226 };/*
24227  * Based on:
24228  * Ext JS Library 1.1.1
24229  * Copyright(c) 2006-2007, Ext JS, LLC.
24230  *
24231  * Originally Released Under LGPL - original licence link has changed is not relivant.
24232  *
24233  * Fork - LGPL
24234  * <script type="text/javascript">
24235  */
24236
24237
24238
24239 /**
24240  * @class Roo.data.Store
24241  * @extends Roo.util.Observable
24242  * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
24243  * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
24244  * <p>
24245  * 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
24246  * has no knowledge of the format of the data returned by the Proxy.<br>
24247  * <p>
24248  * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
24249  * instances from the data object. These records are cached and made available through accessor functions.
24250  * @constructor
24251  * Creates a new Store.
24252  * @param {Object} config A config object containing the objects needed for the Store to access data,
24253  * and read the data into Records.
24254  */
24255 Roo.data.Store = function(config){
24256     this.data = new Roo.util.MixedCollection(false);
24257     this.data.getKey = function(o){
24258         return o.id;
24259     };
24260     this.baseParams = {};
24261     // private
24262     this.paramNames = {
24263         "start" : "start",
24264         "limit" : "limit",
24265         "sort" : "sort",
24266         "dir" : "dir",
24267         "multisort" : "_multisort"
24268     };
24269
24270     if(config && config.data){
24271         this.inlineData = config.data;
24272         delete config.data;
24273     }
24274
24275     Roo.apply(this, config);
24276     
24277     if(this.reader){ // reader passed
24278         this.reader = Roo.factory(this.reader, Roo.data);
24279         this.reader.xmodule = this.xmodule || false;
24280         if(!this.recordType){
24281             this.recordType = this.reader.recordType;
24282         }
24283         if(this.reader.onMetaChange){
24284             this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
24285         }
24286     }
24287
24288     if(this.recordType){
24289         this.fields = this.recordType.prototype.fields;
24290     }
24291     this.modified = [];
24292
24293     this.addEvents({
24294         /**
24295          * @event datachanged
24296          * Fires when the data cache has changed, and a widget which is using this Store
24297          * as a Record cache should refresh its view.
24298          * @param {Store} this
24299          */
24300         datachanged : true,
24301         /**
24302          * @event metachange
24303          * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
24304          * @param {Store} this
24305          * @param {Object} meta The JSON metadata
24306          */
24307         metachange : true,
24308         /**
24309          * @event add
24310          * Fires when Records have been added to the Store
24311          * @param {Store} this
24312          * @param {Roo.data.Record[]} records The array of Records added
24313          * @param {Number} index The index at which the record(s) were added
24314          */
24315         add : true,
24316         /**
24317          * @event remove
24318          * Fires when a Record has been removed from the Store
24319          * @param {Store} this
24320          * @param {Roo.data.Record} record The Record that was removed
24321          * @param {Number} index The index at which the record was removed
24322          */
24323         remove : true,
24324         /**
24325          * @event update
24326          * Fires when a Record has been updated
24327          * @param {Store} this
24328          * @param {Roo.data.Record} record The Record that was updated
24329          * @param {String} operation The update operation being performed.  Value may be one of:
24330          * <pre><code>
24331  Roo.data.Record.EDIT
24332  Roo.data.Record.REJECT
24333  Roo.data.Record.COMMIT
24334          * </code></pre>
24335          */
24336         update : true,
24337         /**
24338          * @event clear
24339          * Fires when the data cache has been cleared.
24340          * @param {Store} this
24341          */
24342         clear : true,
24343         /**
24344          * @event beforeload
24345          * Fires before a request is made for a new data object.  If the beforeload handler returns false
24346          * the load action will be canceled.
24347          * @param {Store} this
24348          * @param {Object} options The loading options that were specified (see {@link #load} for details)
24349          */
24350         beforeload : true,
24351         /**
24352          * @event beforeloadadd
24353          * Fires after a new set of Records has been loaded.
24354          * @param {Store} this
24355          * @param {Roo.data.Record[]} records The Records that were loaded
24356          * @param {Object} options The loading options that were specified (see {@link #load} for details)
24357          */
24358         beforeloadadd : true,
24359         /**
24360          * @event load
24361          * Fires after a new set of Records has been loaded, before they are added to the store.
24362          * @param {Store} this
24363          * @param {Roo.data.Record[]} records The Records that were loaded
24364          * @param {Object} options The loading options that were specified (see {@link #load} for details)
24365          * @params {Object} return from reader
24366          */
24367         load : true,
24368         /**
24369          * @event loadexception
24370          * Fires if an exception occurs in the Proxy during loading.
24371          * Called with the signature of the Proxy's "loadexception" event.
24372          * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
24373          * 
24374          * @param {Proxy} 
24375          * @param {Object} return from JsonData.reader() - success, totalRecords, records
24376          * @param {Object} load options 
24377          * @param {Object} jsonData from your request (normally this contains the Exception)
24378          */
24379         loadexception : true
24380     });
24381     
24382     if(this.proxy){
24383         this.proxy = Roo.factory(this.proxy, Roo.data);
24384         this.proxy.xmodule = this.xmodule || false;
24385         this.relayEvents(this.proxy,  ["loadexception"]);
24386     }
24387     this.sortToggle = {};
24388     this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
24389
24390     Roo.data.Store.superclass.constructor.call(this);
24391
24392     if(this.inlineData){
24393         this.loadData(this.inlineData);
24394         delete this.inlineData;
24395     }
24396 };
24397
24398 Roo.extend(Roo.data.Store, Roo.util.Observable, {
24399      /**
24400     * @cfg {boolean} isLocal   flag if data is locally available (and can be always looked up
24401     * without a remote query - used by combo/forms at present.
24402     */
24403     
24404     /**
24405     * @cfg {Roo.data.DataProxy} proxy [required] The Proxy object which provides access to a data object.
24406     */
24407     /**
24408     * @cfg {Array} data Inline data to be loaded when the store is initialized.
24409     */
24410     /**
24411     * @cfg {Roo.data.DataReader} reader [required]  The Reader object which processes the data object and returns
24412     * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
24413     */
24414     /**
24415     * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
24416     * on any HTTP request
24417     */
24418     /**
24419     * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
24420     */
24421     /**
24422     * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
24423     */
24424     multiSort: false,
24425     /**
24426     * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
24427     * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
24428     */
24429     remoteSort : false,
24430
24431     /**
24432     * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
24433      * loaded or when a record is removed. (defaults to false).
24434     */
24435     pruneModifiedRecords : false,
24436
24437     // private
24438     lastOptions : null,
24439
24440     /**
24441      * Add Records to the Store and fires the add event.
24442      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
24443      */
24444     add : function(records){
24445         records = [].concat(records);
24446         for(var i = 0, len = records.length; i < len; i++){
24447             records[i].join(this);
24448         }
24449         var index = this.data.length;
24450         this.data.addAll(records);
24451         this.fireEvent("add", this, records, index);
24452     },
24453
24454     /**
24455      * Remove a Record from the Store and fires the remove event.
24456      * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
24457      */
24458     remove : function(record){
24459         var index = this.data.indexOf(record);
24460         this.data.removeAt(index);
24461  
24462         if(this.pruneModifiedRecords){
24463             this.modified.remove(record);
24464         }
24465         this.fireEvent("remove", this, record, index);
24466     },
24467
24468     /**
24469      * Remove all Records from the Store and fires the clear event.
24470      */
24471     removeAll : function(){
24472         this.data.clear();
24473         if(this.pruneModifiedRecords){
24474             this.modified = [];
24475         }
24476         this.fireEvent("clear", this);
24477     },
24478
24479     /**
24480      * Inserts Records to the Store at the given index and fires the add event.
24481      * @param {Number} index The start index at which to insert the passed Records.
24482      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
24483      */
24484     insert : function(index, records){
24485         records = [].concat(records);
24486         for(var i = 0, len = records.length; i < len; i++){
24487             this.data.insert(index, records[i]);
24488             records[i].join(this);
24489         }
24490         this.fireEvent("add", this, records, index);
24491     },
24492
24493     /**
24494      * Get the index within the cache of the passed Record.
24495      * @param {Roo.data.Record} record The Roo.data.Record object to to find.
24496      * @return {Number} The index of the passed Record. Returns -1 if not found.
24497      */
24498     indexOf : function(record){
24499         return this.data.indexOf(record);
24500     },
24501
24502     /**
24503      * Get the index within the cache of the Record with the passed id.
24504      * @param {String} id The id of the Record to find.
24505      * @return {Number} The index of the Record. Returns -1 if not found.
24506      */
24507     indexOfId : function(id){
24508         return this.data.indexOfKey(id);
24509     },
24510
24511     /**
24512      * Get the Record with the specified id.
24513      * @param {String} id The id of the Record to find.
24514      * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
24515      */
24516     getById : function(id){
24517         return this.data.key(id);
24518     },
24519
24520     /**
24521      * Get the Record at the specified index.
24522      * @param {Number} index The index of the Record to find.
24523      * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
24524      */
24525     getAt : function(index){
24526         return this.data.itemAt(index);
24527     },
24528
24529     /**
24530      * Returns a range of Records between specified indices.
24531      * @param {Number} startIndex (optional) The starting index (defaults to 0)
24532      * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
24533      * @return {Roo.data.Record[]} An array of Records
24534      */
24535     getRange : function(start, end){
24536         return this.data.getRange(start, end);
24537     },
24538
24539     // private
24540     storeOptions : function(o){
24541         o = Roo.apply({}, o);
24542         delete o.callback;
24543         delete o.scope;
24544         this.lastOptions = o;
24545     },
24546
24547     /**
24548      * Loads the Record cache from the configured Proxy using the configured Reader.
24549      * <p>
24550      * If using remote paging, then the first load call must specify the <em>start</em>
24551      * and <em>limit</em> properties in the options.params property to establish the initial
24552      * position within the dataset, and the number of Records to cache on each read from the Proxy.
24553      * <p>
24554      * <strong>It is important to note that for remote data sources, loading is asynchronous,
24555      * and this call will return before the new data has been loaded. Perform any post-processing
24556      * in a callback function, or in a "load" event handler.</strong>
24557      * <p>
24558      * @param {Object} options An object containing properties which control loading options:<ul>
24559      * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
24560      * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
24561      * passed the following arguments:<ul>
24562      * <li>r : Roo.data.Record[]</li>
24563      * <li>options: Options object from the load call</li>
24564      * <li>success: Boolean success indicator</li></ul></li>
24565      * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
24566      * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
24567      * </ul>
24568      */
24569     load : function(options){
24570         options = options || {};
24571         if(this.fireEvent("beforeload", this, options) !== false){
24572             this.storeOptions(options);
24573             var p = Roo.apply(options.params || {}, this.baseParams);
24574             // if meta was not loaded from remote source.. try requesting it.
24575             if (!this.reader.metaFromRemote) {
24576                 p._requestMeta = 1;
24577             }
24578             if(this.sortInfo && this.remoteSort){
24579                 var pn = this.paramNames;
24580                 p[pn["sort"]] = this.sortInfo.field;
24581                 p[pn["dir"]] = this.sortInfo.direction;
24582             }
24583             if (this.multiSort) {
24584                 var pn = this.paramNames;
24585                 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
24586             }
24587             
24588             this.proxy.load(p, this.reader, this.loadRecords, this, options);
24589         }
24590     },
24591
24592     /**
24593      * Reloads the Record cache from the configured Proxy using the configured Reader and
24594      * the options from the last load operation performed.
24595      * @param {Object} options (optional) An object containing properties which may override the options
24596      * used in the last load operation. See {@link #load} for details (defaults to null, in which case
24597      * the most recently used options are reused).
24598      */
24599     reload : function(options){
24600         this.load(Roo.applyIf(options||{}, this.lastOptions));
24601     },
24602
24603     // private
24604     // Called as a callback by the Reader during a load operation.
24605     loadRecords : function(o, options, success){
24606          
24607         if(!o){
24608             if(success !== false){
24609                 this.fireEvent("load", this, [], options, o);
24610             }
24611             if(options.callback){
24612                 options.callback.call(options.scope || this, [], options, false);
24613             }
24614             return;
24615         }
24616         // if data returned failure - throw an exception.
24617         if (o.success === false) {
24618             // show a message if no listener is registered.
24619             if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
24620                     Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
24621             }
24622             // loadmask wil be hooked into this..
24623             this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
24624             return;
24625         }
24626         var r = o.records, t = o.totalRecords || r.length;
24627         
24628         this.fireEvent("beforeloadadd", this, r, options, o);
24629         
24630         if(!options || options.add !== true){
24631             if(this.pruneModifiedRecords){
24632                 this.modified = [];
24633             }
24634             for(var i = 0, len = r.length; i < len; i++){
24635                 r[i].join(this);
24636             }
24637             if(this.snapshot){
24638                 this.data = this.snapshot;
24639                 delete this.snapshot;
24640             }
24641             this.data.clear();
24642             this.data.addAll(r);
24643             this.totalLength = t;
24644             this.applySort();
24645             this.fireEvent("datachanged", this);
24646         }else{
24647             this.totalLength = Math.max(t, this.data.length+r.length);
24648             this.add(r);
24649         }
24650         
24651         if(this.parent && !Roo.isIOS && !this.useNativeIOS && this.parent.emptyTitle.length) {
24652                 
24653             var e = new Roo.data.Record({});
24654
24655             e.set(this.parent.displayField, this.parent.emptyTitle);
24656             e.set(this.parent.valueField, '');
24657
24658             this.insert(0, e);
24659         }
24660             
24661         this.fireEvent("load", this, r, options, o);
24662         if(options.callback){
24663             options.callback.call(options.scope || this, r, options, true);
24664         }
24665     },
24666
24667
24668     /**
24669      * Loads data from a passed data block. A Reader which understands the format of the data
24670      * must have been configured in the constructor.
24671      * @param {Object} data The data block from which to read the Records.  The format of the data expected
24672      * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
24673      * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
24674      */
24675     loadData : function(o, append){
24676         var r = this.reader.readRecords(o);
24677         this.loadRecords(r, {add: append}, true);
24678     },
24679     
24680      /**
24681      * using 'cn' the nested child reader read the child array into it's child stores.
24682      * @param {Object} rec The record with a 'children array
24683      */
24684     loadDataFromChildren : function(rec)
24685     {
24686         this.loadData(this.reader.toLoadData(rec));
24687     },
24688     
24689
24690     /**
24691      * Gets the number of cached records.
24692      * <p>
24693      * <em>If using paging, this may not be the total size of the dataset. If the data object
24694      * used by the Reader contains the dataset size, then the getTotalCount() function returns
24695      * the data set size</em>
24696      */
24697     getCount : function(){
24698         return this.data.length || 0;
24699     },
24700
24701     /**
24702      * Gets the total number of records in the dataset as returned by the server.
24703      * <p>
24704      * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
24705      * the dataset size</em>
24706      */
24707     getTotalCount : function(){
24708         return this.totalLength || 0;
24709     },
24710
24711     /**
24712      * Returns the sort state of the Store as an object with two properties:
24713      * <pre><code>
24714  field {String} The name of the field by which the Records are sorted
24715  direction {String} The sort order, "ASC" or "DESC"
24716      * </code></pre>
24717      */
24718     getSortState : function(){
24719         return this.sortInfo;
24720     },
24721
24722     // private
24723     applySort : function(){
24724         if(this.sortInfo && !this.remoteSort){
24725             var s = this.sortInfo, f = s.field;
24726             var st = this.fields.get(f).sortType;
24727             var fn = function(r1, r2){
24728                 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
24729                 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
24730             };
24731             this.data.sort(s.direction, fn);
24732             if(this.snapshot && this.snapshot != this.data){
24733                 this.snapshot.sort(s.direction, fn);
24734             }
24735         }
24736     },
24737
24738     /**
24739      * Sets the default sort column and order to be used by the next load operation.
24740      * @param {String} fieldName The name of the field to sort by.
24741      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
24742      */
24743     setDefaultSort : function(field, dir){
24744         this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
24745     },
24746
24747     /**
24748      * Sort the Records.
24749      * If remote sorting is used, the sort is performed on the server, and the cache is
24750      * reloaded. If local sorting is used, the cache is sorted internally.
24751      * @param {String} fieldName The name of the field to sort by.
24752      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
24753      */
24754     sort : function(fieldName, dir){
24755         var f = this.fields.get(fieldName);
24756         if(!dir){
24757             this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
24758             
24759             if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
24760                 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
24761             }else{
24762                 dir = f.sortDir;
24763             }
24764         }
24765         this.sortToggle[f.name] = dir;
24766         this.sortInfo = {field: f.name, direction: dir};
24767         if(!this.remoteSort){
24768             this.applySort();
24769             this.fireEvent("datachanged", this);
24770         }else{
24771             this.load(this.lastOptions);
24772         }
24773     },
24774
24775     /**
24776      * Calls the specified function for each of the Records in the cache.
24777      * @param {Function} fn The function to call. The Record is passed as the first parameter.
24778      * Returning <em>false</em> aborts and exits the iteration.
24779      * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
24780      */
24781     each : function(fn, scope){
24782         this.data.each(fn, scope);
24783     },
24784
24785     /**
24786      * Gets all records modified since the last commit.  Modified records are persisted across load operations
24787      * (e.g., during paging).
24788      * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
24789      */
24790     getModifiedRecords : function(){
24791         return this.modified;
24792     },
24793
24794     // private
24795     createFilterFn : function(property, value, anyMatch){
24796         if(!value.exec){ // not a regex
24797             value = String(value);
24798             if(value.length == 0){
24799                 return false;
24800             }
24801             value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
24802         }
24803         return function(r){
24804             return value.test(r.data[property]);
24805         };
24806     },
24807
24808     /**
24809      * Sums the value of <i>property</i> for each record between start and end and returns the result.
24810      * @param {String} property A field on your records
24811      * @param {Number} start The record index to start at (defaults to 0)
24812      * @param {Number} end The last record index to include (defaults to length - 1)
24813      * @return {Number} The sum
24814      */
24815     sum : function(property, start, end){
24816         var rs = this.data.items, v = 0;
24817         start = start || 0;
24818         end = (end || end === 0) ? end : rs.length-1;
24819
24820         for(var i = start; i <= end; i++){
24821             v += (rs[i].data[property] || 0);
24822         }
24823         return v;
24824     },
24825
24826     /**
24827      * Filter the records by a specified property.
24828      * @param {String} field A field on your records
24829      * @param {String/RegExp} value Either a string that the field
24830      * should start with or a RegExp to test against the field
24831      * @param {Boolean} anyMatch True to match any part not just the beginning
24832      */
24833     filter : function(property, value, anyMatch){
24834         var fn = this.createFilterFn(property, value, anyMatch);
24835         return fn ? this.filterBy(fn) : this.clearFilter();
24836     },
24837
24838     /**
24839      * Filter by a function. The specified function will be called with each
24840      * record in this data source. If the function returns true the record is included,
24841      * otherwise it is filtered.
24842      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
24843      * @param {Object} scope (optional) The scope of the function (defaults to this)
24844      */
24845     filterBy : function(fn, scope){
24846         this.snapshot = this.snapshot || this.data;
24847         this.data = this.queryBy(fn, scope||this);
24848         this.fireEvent("datachanged", this);
24849     },
24850
24851     /**
24852      * Query the records by a specified property.
24853      * @param {String} field A field on your records
24854      * @param {String/RegExp} value Either a string that the field
24855      * should start with or a RegExp to test against the field
24856      * @param {Boolean} anyMatch True to match any part not just the beginning
24857      * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
24858      */
24859     query : function(property, value, anyMatch){
24860         var fn = this.createFilterFn(property, value, anyMatch);
24861         return fn ? this.queryBy(fn) : this.data.clone();
24862     },
24863
24864     /**
24865      * Query by a function. The specified function will be called with each
24866      * record in this data source. If the function returns true the record is included
24867      * in the results.
24868      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
24869      * @param {Object} scope (optional) The scope of the function (defaults to this)
24870       @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
24871      **/
24872     queryBy : function(fn, scope){
24873         var data = this.snapshot || this.data;
24874         return data.filterBy(fn, scope||this);
24875     },
24876
24877     /**
24878      * Collects unique values for a particular dataIndex from this store.
24879      * @param {String} dataIndex The property to collect
24880      * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
24881      * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
24882      * @return {Array} An array of the unique values
24883      **/
24884     collect : function(dataIndex, allowNull, bypassFilter){
24885         var d = (bypassFilter === true && this.snapshot) ?
24886                 this.snapshot.items : this.data.items;
24887         var v, sv, r = [], l = {};
24888         for(var i = 0, len = d.length; i < len; i++){
24889             v = d[i].data[dataIndex];
24890             sv = String(v);
24891             if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
24892                 l[sv] = true;
24893                 r[r.length] = v;
24894             }
24895         }
24896         return r;
24897     },
24898
24899     /**
24900      * Revert to a view of the Record cache with no filtering applied.
24901      * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
24902      */
24903     clearFilter : function(suppressEvent){
24904         if(this.snapshot && this.snapshot != this.data){
24905             this.data = this.snapshot;
24906             delete this.snapshot;
24907             if(suppressEvent !== true){
24908                 this.fireEvent("datachanged", this);
24909             }
24910         }
24911     },
24912
24913     // private
24914     afterEdit : function(record){
24915         if(this.modified.indexOf(record) == -1){
24916             this.modified.push(record);
24917         }
24918         this.fireEvent("update", this, record, Roo.data.Record.EDIT);
24919     },
24920     
24921     // private
24922     afterReject : function(record){
24923         this.modified.remove(record);
24924         this.fireEvent("update", this, record, Roo.data.Record.REJECT);
24925     },
24926
24927     // private
24928     afterCommit : function(record){
24929         this.modified.remove(record);
24930         this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
24931     },
24932
24933     /**
24934      * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
24935      * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
24936      */
24937     commitChanges : function(){
24938         var m = this.modified.slice(0);
24939         this.modified = [];
24940         for(var i = 0, len = m.length; i < len; i++){
24941             m[i].commit();
24942         }
24943     },
24944
24945     /**
24946      * Cancel outstanding changes on all changed records.
24947      */
24948     rejectChanges : function(){
24949         var m = this.modified.slice(0);
24950         this.modified = [];
24951         for(var i = 0, len = m.length; i < len; i++){
24952             m[i].reject();
24953         }
24954     },
24955
24956     onMetaChange : function(meta, rtype, o){
24957         this.recordType = rtype;
24958         this.fields = rtype.prototype.fields;
24959         delete this.snapshot;
24960         this.sortInfo = meta.sortInfo || this.sortInfo;
24961         this.modified = [];
24962         this.fireEvent('metachange', this, this.reader.meta);
24963     },
24964     
24965     moveIndex : function(data, type)
24966     {
24967         var index = this.indexOf(data);
24968         
24969         var newIndex = index + type;
24970         
24971         this.remove(data);
24972         
24973         this.insert(newIndex, data);
24974         
24975     }
24976 });/*
24977  * Based on:
24978  * Ext JS Library 1.1.1
24979  * Copyright(c) 2006-2007, Ext JS, LLC.
24980  *
24981  * Originally Released Under LGPL - original licence link has changed is not relivant.
24982  *
24983  * Fork - LGPL
24984  * <script type="text/javascript">
24985  */
24986
24987 /**
24988  * @class Roo.data.SimpleStore
24989  * @extends Roo.data.Store
24990  * Small helper class to make creating Stores from Array data easier.
24991  * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
24992  * @cfg {Array} fields An array of field definition objects, or field name strings.
24993  * @cfg {Object} an existing reader (eg. copied from another store)
24994  * @cfg {Array} data The multi-dimensional array of data
24995  * @cfg {Roo.data.DataProxy} proxy [not-required]  
24996  * @cfg {Roo.data.Reader} reader  [not-required] 
24997  * @constructor
24998  * @param {Object} config
24999  */
25000 Roo.data.SimpleStore = function(config)
25001 {
25002     Roo.data.SimpleStore.superclass.constructor.call(this, {
25003         isLocal : true,
25004         reader: typeof(config.reader) != 'undefined' ? config.reader : new Roo.data.ArrayReader({
25005                 id: config.id
25006             },
25007             Roo.data.Record.create(config.fields)
25008         ),
25009         proxy : new Roo.data.MemoryProxy(config.data)
25010     });
25011     this.load();
25012 };
25013 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
25014  * Based on:
25015  * Ext JS Library 1.1.1
25016  * Copyright(c) 2006-2007, Ext JS, LLC.
25017  *
25018  * Originally Released Under LGPL - original licence link has changed is not relivant.
25019  *
25020  * Fork - LGPL
25021  * <script type="text/javascript">
25022  */
25023
25024 /**
25025 /**
25026  * @extends Roo.data.Store
25027  * @class Roo.data.JsonStore
25028  * Small helper class to make creating Stores for JSON data easier. <br/>
25029 <pre><code>
25030 var store = new Roo.data.JsonStore({
25031     url: 'get-images.php',
25032     root: 'images',
25033     fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
25034 });
25035 </code></pre>
25036  * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
25037  * JsonReader and HttpProxy (unless inline data is provided).</b>
25038  * @cfg {Array} fields An array of field definition objects, or field name strings.
25039  * @constructor
25040  * @param {Object} config
25041  */
25042 Roo.data.JsonStore = function(c){
25043     Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
25044         proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
25045         reader: new Roo.data.JsonReader(c, c.fields)
25046     }));
25047 };
25048 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
25049  * Based on:
25050  * Ext JS Library 1.1.1
25051  * Copyright(c) 2006-2007, Ext JS, LLC.
25052  *
25053  * Originally Released Under LGPL - original licence link has changed is not relivant.
25054  *
25055  * Fork - LGPL
25056  * <script type="text/javascript">
25057  */
25058
25059  
25060 Roo.data.Field = function(config){
25061     if(typeof config == "string"){
25062         config = {name: config};
25063     }
25064     Roo.apply(this, config);
25065     
25066     if(!this.type){
25067         this.type = "auto";
25068     }
25069     
25070     var st = Roo.data.SortTypes;
25071     // named sortTypes are supported, here we look them up
25072     if(typeof this.sortType == "string"){
25073         this.sortType = st[this.sortType];
25074     }
25075     
25076     // set default sortType for strings and dates
25077     if(!this.sortType){
25078         switch(this.type){
25079             case "string":
25080                 this.sortType = st.asUCString;
25081                 break;
25082             case "date":
25083                 this.sortType = st.asDate;
25084                 break;
25085             default:
25086                 this.sortType = st.none;
25087         }
25088     }
25089
25090     // define once
25091     var stripRe = /[\$,%]/g;
25092
25093     // prebuilt conversion function for this field, instead of
25094     // switching every time we're reading a value
25095     if(!this.convert){
25096         var cv, dateFormat = this.dateFormat;
25097         switch(this.type){
25098             case "":
25099             case "auto":
25100             case undefined:
25101                 cv = function(v){ return v; };
25102                 break;
25103             case "string":
25104                 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
25105                 break;
25106             case "int":
25107                 cv = function(v){
25108                     return v !== undefined && v !== null && v !== '' ?
25109                            parseInt(String(v).replace(stripRe, ""), 10) : '';
25110                     };
25111                 break;
25112             case "float":
25113                 cv = function(v){
25114                     return v !== undefined && v !== null && v !== '' ?
25115                            parseFloat(String(v).replace(stripRe, ""), 10) : ''; 
25116                     };
25117                 break;
25118             case "bool":
25119             case "boolean":
25120                 cv = function(v){ return v === true || v === "true" || v == 1; };
25121                 break;
25122             case "date":
25123                 cv = function(v){
25124                     if(!v){
25125                         return '';
25126                     }
25127                     if(v instanceof Date){
25128                         return v;
25129                     }
25130                     if(dateFormat){
25131                         if(dateFormat == "timestamp"){
25132                             return new Date(v*1000);
25133                         }
25134                         return Date.parseDate(v, dateFormat);
25135                     }
25136                     var parsed = Date.parse(v);
25137                     return parsed ? new Date(parsed) : null;
25138                 };
25139              break;
25140             
25141         }
25142         this.convert = cv;
25143     }
25144 };
25145
25146 Roo.data.Field.prototype = {
25147     dateFormat: null,
25148     defaultValue: "",
25149     mapping: null,
25150     sortType : null,
25151     sortDir : "ASC"
25152 };/*
25153  * Based on:
25154  * Ext JS Library 1.1.1
25155  * Copyright(c) 2006-2007, Ext JS, LLC.
25156  *
25157  * Originally Released Under LGPL - original licence link has changed is not relivant.
25158  *
25159  * Fork - LGPL
25160  * <script type="text/javascript">
25161  */
25162  
25163 // Base class for reading structured data from a data source.  This class is intended to be
25164 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
25165
25166 /**
25167  * @class Roo.data.DataReader
25168  * @abstract
25169  * Base class for reading structured data from a data source.  This class is intended to be
25170  * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
25171  */
25172
25173 Roo.data.DataReader = function(meta, recordType){
25174     
25175     this.meta = meta;
25176     
25177     this.recordType = recordType instanceof Array ? 
25178         Roo.data.Record.create(recordType) : recordType;
25179 };
25180
25181 Roo.data.DataReader.prototype = {
25182     
25183     
25184     readerType : 'Data',
25185      /**
25186      * Create an empty record
25187      * @param {Object} data (optional) - overlay some values
25188      * @return {Roo.data.Record} record created.
25189      */
25190     newRow :  function(d) {
25191         var da =  {};
25192         this.recordType.prototype.fields.each(function(c) {
25193             switch( c.type) {
25194                 case 'int' : da[c.name] = 0; break;
25195                 case 'date' : da[c.name] = new Date(); break;
25196                 case 'float' : da[c.name] = 0.0; break;
25197                 case 'boolean' : da[c.name] = false; break;
25198                 default : da[c.name] = ""; break;
25199             }
25200             
25201         });
25202         return new this.recordType(Roo.apply(da, d));
25203     }
25204     
25205     
25206 };/*
25207  * Based on:
25208  * Ext JS Library 1.1.1
25209  * Copyright(c) 2006-2007, Ext JS, LLC.
25210  *
25211  * Originally Released Under LGPL - original licence link has changed is not relivant.
25212  *
25213  * Fork - LGPL
25214  * <script type="text/javascript">
25215  */
25216
25217 /**
25218  * @class Roo.data.DataProxy
25219  * @extends Roo.util.Observable
25220  * @abstract
25221  * This class is an abstract base class for implementations which provide retrieval of
25222  * unformatted data objects.<br>
25223  * <p>
25224  * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
25225  * (of the appropriate type which knows how to parse the data object) to provide a block of
25226  * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
25227  * <p>
25228  * Custom implementations must implement the load method as described in
25229  * {@link Roo.data.HttpProxy#load}.
25230  */
25231 Roo.data.DataProxy = function(){
25232     this.addEvents({
25233         /**
25234          * @event beforeload
25235          * Fires before a network request is made to retrieve a data object.
25236          * @param {Object} This DataProxy object.
25237          * @param {Object} params The params parameter to the load function.
25238          */
25239         beforeload : true,
25240         /**
25241          * @event load
25242          * Fires before the load method's callback is called.
25243          * @param {Object} This DataProxy object.
25244          * @param {Object} o The data object.
25245          * @param {Object} arg The callback argument object passed to the load function.
25246          */
25247         load : true,
25248         /**
25249          * @event loadexception
25250          * Fires if an Exception occurs during data retrieval.
25251          * @param {Object} This DataProxy object.
25252          * @param {Object} o The data object.
25253          * @param {Object} arg The callback argument object passed to the load function.
25254          * @param {Object} e The Exception.
25255          */
25256         loadexception : true
25257     });
25258     Roo.data.DataProxy.superclass.constructor.call(this);
25259 };
25260
25261 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
25262
25263     /**
25264      * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
25265      */
25266 /*
25267  * Based on:
25268  * Ext JS Library 1.1.1
25269  * Copyright(c) 2006-2007, Ext JS, LLC.
25270  *
25271  * Originally Released Under LGPL - original licence link has changed is not relivant.
25272  *
25273  * Fork - LGPL
25274  * <script type="text/javascript">
25275  */
25276 /**
25277  * @class Roo.data.MemoryProxy
25278  * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
25279  * to the Reader when its load method is called.
25280  * @constructor
25281  * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
25282  */
25283 Roo.data.MemoryProxy = function(data){
25284     if (data.data) {
25285         data = data.data;
25286     }
25287     Roo.data.MemoryProxy.superclass.constructor.call(this);
25288     this.data = data;
25289 };
25290
25291 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
25292     
25293     /**
25294      * Load data from the requested source (in this case an in-memory
25295      * data object passed to the constructor), read the data object into
25296      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
25297      * process that block using the passed callback.
25298      * @param {Object} params This parameter is not used by the MemoryProxy class.
25299      * @param {Roo.data.DataReader} reader The Reader object which converts the data
25300      * object into a block of Roo.data.Records.
25301      * @param {Function} callback The function into which to pass the block of Roo.data.records.
25302      * The function must be passed <ul>
25303      * <li>The Record block object</li>
25304      * <li>The "arg" argument from the load function</li>
25305      * <li>A boolean success indicator</li>
25306      * </ul>
25307      * @param {Object} scope The scope in which to call the callback
25308      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
25309      */
25310     load : function(params, reader, callback, scope, arg){
25311         params = params || {};
25312         var result;
25313         try {
25314             result = reader.readRecords(params.data ? params.data :this.data);
25315         }catch(e){
25316             this.fireEvent("loadexception", this, arg, null, e);
25317             callback.call(scope, null, arg, false);
25318             return;
25319         }
25320         callback.call(scope, result, arg, true);
25321     },
25322     
25323     // private
25324     update : function(params, records){
25325         
25326     }
25327 });/*
25328  * Based on:
25329  * Ext JS Library 1.1.1
25330  * Copyright(c) 2006-2007, Ext JS, LLC.
25331  *
25332  * Originally Released Under LGPL - original licence link has changed is not relivant.
25333  *
25334  * Fork - LGPL
25335  * <script type="text/javascript">
25336  */
25337 /**
25338  * @class Roo.data.HttpProxy
25339  * @extends Roo.data.DataProxy
25340  * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
25341  * configured to reference a certain URL.<br><br>
25342  * <p>
25343  * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
25344  * from which the running page was served.<br><br>
25345  * <p>
25346  * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
25347  * <p>
25348  * Be aware that to enable the browser to parse an XML document, the server must set
25349  * the Content-Type header in the HTTP response to "text/xml".
25350  * @constructor
25351  * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
25352  * an {@link Roo.data.Connection} object.  If a Connection config is passed, the singleton {@link Roo.Ajax} object
25353  * will be used to make the request.
25354  */
25355 Roo.data.HttpProxy = function(conn){
25356     Roo.data.HttpProxy.superclass.constructor.call(this);
25357     // is conn a conn config or a real conn?
25358     this.conn = conn;
25359     this.useAjax = !conn || !conn.events;
25360   
25361 };
25362
25363 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
25364     // thse are take from connection...
25365     
25366     /**
25367      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
25368      */
25369     /**
25370      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
25371      * extra parameters to each request made by this object. (defaults to undefined)
25372      */
25373     /**
25374      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
25375      *  to each request made by this object. (defaults to undefined)
25376      */
25377     /**
25378      * @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)
25379      */
25380     /**
25381      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
25382      */
25383      /**
25384      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
25385      * @type Boolean
25386      */
25387   
25388
25389     /**
25390      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
25391      * @type Boolean
25392      */
25393     /**
25394      * Return the {@link Roo.data.Connection} object being used by this Proxy.
25395      * @return {Connection} The Connection object. This object may be used to subscribe to events on
25396      * a finer-grained basis than the DataProxy events.
25397      */
25398     getConnection : function(){
25399         return this.useAjax ? Roo.Ajax : this.conn;
25400     },
25401
25402     /**
25403      * Load data from the configured {@link Roo.data.Connection}, read the data object into
25404      * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
25405      * process that block using the passed callback.
25406      * @param {Object} params An object containing properties which are to be used as HTTP parameters
25407      * for the request to the remote server.
25408      * @param {Roo.data.DataReader} reader The Reader object which converts the data
25409      * object into a block of Roo.data.Records.
25410      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
25411      * The function must be passed <ul>
25412      * <li>The Record block object</li>
25413      * <li>The "arg" argument from the load function</li>
25414      * <li>A boolean success indicator</li>
25415      * </ul>
25416      * @param {Object} scope The scope in which to call the callback
25417      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
25418      */
25419     load : function(params, reader, callback, scope, arg){
25420         if(this.fireEvent("beforeload", this, params) !== false){
25421             var  o = {
25422                 params : params || {},
25423                 request: {
25424                     callback : callback,
25425                     scope : scope,
25426                     arg : arg
25427                 },
25428                 reader: reader,
25429                 callback : this.loadResponse,
25430                 scope: this
25431             };
25432             if(this.useAjax){
25433                 Roo.applyIf(o, this.conn);
25434                 if(this.activeRequest){
25435                     Roo.Ajax.abort(this.activeRequest);
25436                 }
25437                 this.activeRequest = Roo.Ajax.request(o);
25438             }else{
25439                 this.conn.request(o);
25440             }
25441         }else{
25442             callback.call(scope||this, null, arg, false);
25443         }
25444     },
25445
25446     // private
25447     loadResponse : function(o, success, response){
25448         delete this.activeRequest;
25449         if(!success){
25450             this.fireEvent("loadexception", this, o, response);
25451             o.request.callback.call(o.request.scope, null, o.request.arg, false);
25452             return;
25453         }
25454         var result;
25455         try {
25456             result = o.reader.read(response);
25457         }catch(e){
25458             o.success = false;
25459             o.raw = { errorMsg : response.responseText };
25460             this.fireEvent("loadexception", this, o, response, e);
25461             o.request.callback.call(o.request.scope, o, o.request.arg, false);
25462             return;
25463         }
25464         
25465         this.fireEvent("load", this, o, o.request.arg);
25466         o.request.callback.call(o.request.scope, result, o.request.arg, true);
25467     },
25468
25469     // private
25470     update : function(dataSet){
25471
25472     },
25473
25474     // private
25475     updateResponse : function(dataSet){
25476
25477     }
25478 });/*
25479  * Based on:
25480  * Ext JS Library 1.1.1
25481  * Copyright(c) 2006-2007, Ext JS, LLC.
25482  *
25483  * Originally Released Under LGPL - original licence link has changed is not relivant.
25484  *
25485  * Fork - LGPL
25486  * <script type="text/javascript">
25487  */
25488
25489 /**
25490  * @class Roo.data.ScriptTagProxy
25491  * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
25492  * other than the originating domain of the running page.<br><br>
25493  * <p>
25494  * <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
25495  * of the running page, you must use this class, rather than DataProxy.</em><br><br>
25496  * <p>
25497  * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
25498  * source code that is used as the source inside a &lt;script> tag.<br><br>
25499  * <p>
25500  * In order for the browser to process the returned data, the server must wrap the data object
25501  * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
25502  * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
25503  * depending on whether the callback name was passed:
25504  * <p>
25505  * <pre><code>
25506 boolean scriptTag = false;
25507 String cb = request.getParameter("callback");
25508 if (cb != null) {
25509     scriptTag = true;
25510     response.setContentType("text/javascript");
25511 } else {
25512     response.setContentType("application/x-json");
25513 }
25514 Writer out = response.getWriter();
25515 if (scriptTag) {
25516     out.write(cb + "(");
25517 }
25518 out.print(dataBlock.toJsonString());
25519 if (scriptTag) {
25520     out.write(");");
25521 }
25522 </pre></code>
25523  *
25524  * @constructor
25525  * @param {Object} config A configuration object.
25526  */
25527 Roo.data.ScriptTagProxy = function(config){
25528     Roo.data.ScriptTagProxy.superclass.constructor.call(this);
25529     Roo.apply(this, config);
25530     this.head = document.getElementsByTagName("head")[0];
25531 };
25532
25533 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
25534
25535 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
25536     /**
25537      * @cfg {String} url The URL from which to request the data object.
25538      */
25539     /**
25540      * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
25541      */
25542     timeout : 30000,
25543     /**
25544      * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
25545      * the server the name of the callback function set up by the load call to process the returned data object.
25546      * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
25547      * javascript output which calls this named function passing the data object as its only parameter.
25548      */
25549     callbackParam : "callback",
25550     /**
25551      *  @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
25552      * name to the request.
25553      */
25554     nocache : true,
25555
25556     /**
25557      * Load data from the configured URL, read the data object into
25558      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
25559      * process that block using the passed callback.
25560      * @param {Object} params An object containing properties which are to be used as HTTP parameters
25561      * for the request to the remote server.
25562      * @param {Roo.data.DataReader} reader The Reader object which converts the data
25563      * object into a block of Roo.data.Records.
25564      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
25565      * The function must be passed <ul>
25566      * <li>The Record block object</li>
25567      * <li>The "arg" argument from the load function</li>
25568      * <li>A boolean success indicator</li>
25569      * </ul>
25570      * @param {Object} scope The scope in which to call the callback
25571      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
25572      */
25573     load : function(params, reader, callback, scope, arg){
25574         if(this.fireEvent("beforeload", this, params) !== false){
25575
25576             var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
25577
25578             var url = this.url;
25579             url += (url.indexOf("?") != -1 ? "&" : "?") + p;
25580             if(this.nocache){
25581                 url += "&_dc=" + (new Date().getTime());
25582             }
25583             var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
25584             var trans = {
25585                 id : transId,
25586                 cb : "stcCallback"+transId,
25587                 scriptId : "stcScript"+transId,
25588                 params : params,
25589                 arg : arg,
25590                 url : url,
25591                 callback : callback,
25592                 scope : scope,
25593                 reader : reader
25594             };
25595             var conn = this;
25596
25597             window[trans.cb] = function(o){
25598                 conn.handleResponse(o, trans);
25599             };
25600
25601             url += String.format("&{0}={1}", this.callbackParam, trans.cb);
25602
25603             if(this.autoAbort !== false){
25604                 this.abort();
25605             }
25606
25607             trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
25608
25609             var script = document.createElement("script");
25610             script.setAttribute("src", url);
25611             script.setAttribute("type", "text/javascript");
25612             script.setAttribute("id", trans.scriptId);
25613             this.head.appendChild(script);
25614
25615             this.trans = trans;
25616         }else{
25617             callback.call(scope||this, null, arg, false);
25618         }
25619     },
25620
25621     // private
25622     isLoading : function(){
25623         return this.trans ? true : false;
25624     },
25625
25626     /**
25627      * Abort the current server request.
25628      */
25629     abort : function(){
25630         if(this.isLoading()){
25631             this.destroyTrans(this.trans);
25632         }
25633     },
25634
25635     // private
25636     destroyTrans : function(trans, isLoaded){
25637         this.head.removeChild(document.getElementById(trans.scriptId));
25638         clearTimeout(trans.timeoutId);
25639         if(isLoaded){
25640             window[trans.cb] = undefined;
25641             try{
25642                 delete window[trans.cb];
25643             }catch(e){}
25644         }else{
25645             // if hasn't been loaded, wait for load to remove it to prevent script error
25646             window[trans.cb] = function(){
25647                 window[trans.cb] = undefined;
25648                 try{
25649                     delete window[trans.cb];
25650                 }catch(e){}
25651             };
25652         }
25653     },
25654
25655     // private
25656     handleResponse : function(o, trans){
25657         this.trans = false;
25658         this.destroyTrans(trans, true);
25659         var result;
25660         try {
25661             result = trans.reader.readRecords(o);
25662         }catch(e){
25663             this.fireEvent("loadexception", this, o, trans.arg, e);
25664             trans.callback.call(trans.scope||window, null, trans.arg, false);
25665             return;
25666         }
25667         this.fireEvent("load", this, o, trans.arg);
25668         trans.callback.call(trans.scope||window, result, trans.arg, true);
25669     },
25670
25671     // private
25672     handleFailure : function(trans){
25673         this.trans = false;
25674         this.destroyTrans(trans, false);
25675         this.fireEvent("loadexception", this, null, trans.arg);
25676         trans.callback.call(trans.scope||window, null, trans.arg, false);
25677     }
25678 });/*
25679  * Based on:
25680  * Ext JS Library 1.1.1
25681  * Copyright(c) 2006-2007, Ext JS, LLC.
25682  *
25683  * Originally Released Under LGPL - original licence link has changed is not relivant.
25684  *
25685  * Fork - LGPL
25686  * <script type="text/javascript">
25687  */
25688
25689 /**
25690  * @class Roo.data.JsonReader
25691  * @extends Roo.data.DataReader
25692  * Data reader class to create an Array of Roo.data.Record objects from a JSON response
25693  * based on mappings in a provided Roo.data.Record constructor.
25694  * 
25695  * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
25696  * in the reply previously. 
25697  * 
25698  * <p>
25699  * Example code:
25700  * <pre><code>
25701 var RecordDef = Roo.data.Record.create([
25702     {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
25703     {name: 'occupation'}                 // This field will use "occupation" as the mapping.
25704 ]);
25705 var myReader = new Roo.data.JsonReader({
25706     totalProperty: "results",    // The property which contains the total dataset size (optional)
25707     root: "rows",                // The property which contains an Array of row objects
25708     id: "id"                     // The property within each row object that provides an ID for the record (optional)
25709 }, RecordDef);
25710 </code></pre>
25711  * <p>
25712  * This would consume a JSON file like this:
25713  * <pre><code>
25714 { 'results': 2, 'rows': [
25715     { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
25716     { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
25717 }
25718 </code></pre>
25719  * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
25720  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
25721  * paged from the remote server.
25722  * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
25723  * @cfg {String} root name of the property which contains the Array of row objects.
25724  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
25725  * @cfg {Array} fields Array of field definition objects
25726  * @constructor
25727  * Create a new JsonReader
25728  * @param {Object} meta Metadata configuration options
25729  * @param {Object} recordType Either an Array of field definition objects,
25730  * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
25731  */
25732 Roo.data.JsonReader = function(meta, recordType){
25733     
25734     meta = meta || {};
25735     // set some defaults:
25736     Roo.applyIf(meta, {
25737         totalProperty: 'total',
25738         successProperty : 'success',
25739         root : 'data',
25740         id : 'id'
25741     });
25742     
25743     Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
25744 };
25745 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
25746     
25747     readerType : 'Json',
25748     
25749     /**
25750      * @prop {Boolean} metaFromRemote  - if the meta data was loaded from the remote source.
25751      * Used by Store query builder to append _requestMeta to params.
25752      * 
25753      */
25754     metaFromRemote : false,
25755     /**
25756      * This method is only used by a DataProxy which has retrieved data from a remote server.
25757      * @param {Object} response The XHR object which contains the JSON data in its responseText.
25758      * @return {Object} data A data block which is used by an Roo.data.Store object as
25759      * a cache of Roo.data.Records.
25760      */
25761     read : function(response){
25762         var json = response.responseText;
25763        
25764         var o = /* eval:var:o */ eval("("+json+")");
25765         if(!o) {
25766             throw {message: "JsonReader.read: Json object not found"};
25767         }
25768         
25769         if(o.metaData){
25770             
25771             delete this.ef;
25772             this.metaFromRemote = true;
25773             this.meta = o.metaData;
25774             this.recordType = Roo.data.Record.create(o.metaData.fields);
25775             this.onMetaChange(this.meta, this.recordType, o);
25776         }
25777         return this.readRecords(o);
25778     },
25779
25780     // private function a store will implement
25781     onMetaChange : function(meta, recordType, o){
25782
25783     },
25784
25785     /**
25786          * @ignore
25787          */
25788     simpleAccess: function(obj, subsc) {
25789         return obj[subsc];
25790     },
25791
25792         /**
25793          * @ignore
25794          */
25795     getJsonAccessor: function(){
25796         var re = /[\[\.]/;
25797         return function(expr) {
25798             try {
25799                 return(re.test(expr))
25800                     ? new Function("obj", "return obj." + expr)
25801                     : function(obj){
25802                         return obj[expr];
25803                     };
25804             } catch(e){}
25805             return Roo.emptyFn;
25806         };
25807     }(),
25808
25809     /**
25810      * Create a data block containing Roo.data.Records from an XML document.
25811      * @param {Object} o An object which contains an Array of row objects in the property specified
25812      * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
25813      * which contains the total size of the dataset.
25814      * @return {Object} data A data block which is used by an Roo.data.Store object as
25815      * a cache of Roo.data.Records.
25816      */
25817     readRecords : function(o){
25818         /**
25819          * After any data loads, the raw JSON data is available for further custom processing.
25820          * @type Object
25821          */
25822         this.o = o;
25823         var s = this.meta, Record = this.recordType,
25824             f = Record ? Record.prototype.fields : null, fi = f ? f.items : [], fl = f ? f.length : 0;
25825
25826 //      Generate extraction functions for the totalProperty, the root, the id, and for each field
25827         if (!this.ef) {
25828             if(s.totalProperty) {
25829                     this.getTotal = this.getJsonAccessor(s.totalProperty);
25830                 }
25831                 if(s.successProperty) {
25832                     this.getSuccess = this.getJsonAccessor(s.successProperty);
25833                 }
25834                 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
25835                 if (s.id) {
25836                         var g = this.getJsonAccessor(s.id);
25837                         this.getId = function(rec) {
25838                                 var r = g(rec);  
25839                                 return (r === undefined || r === "") ? null : r;
25840                         };
25841                 } else {
25842                         this.getId = function(){return null;};
25843                 }
25844             this.ef = [];
25845             for(var jj = 0; jj < fl; jj++){
25846                 f = fi[jj];
25847                 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
25848                 this.ef[jj] = this.getJsonAccessor(map);
25849             }
25850         }
25851
25852         var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
25853         if(s.totalProperty){
25854             var vt = parseInt(this.getTotal(o), 10);
25855             if(!isNaN(vt)){
25856                 totalRecords = vt;
25857             }
25858         }
25859         if(s.successProperty){
25860             var vs = this.getSuccess(o);
25861             if(vs === false || vs === 'false'){
25862                 success = false;
25863             }
25864         }
25865         var records = [];
25866         for(var i = 0; i < c; i++){
25867             var n = root[i];
25868             var values = {};
25869             var id = this.getId(n);
25870             for(var j = 0; j < fl; j++){
25871                 f = fi[j];
25872                                 var v = this.ef[j](n);
25873                                 if (!f.convert) {
25874                                         Roo.log('missing convert for ' + f.name);
25875                                         Roo.log(f);
25876                                         continue;
25877                                 }
25878                                 values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
25879             }
25880                         if (!Record) {
25881                                 return {
25882                                         raw : { errorMsg : "JSON Reader Error: fields or metadata not available to create Record" },
25883                                         success : false,
25884                                         records : [],
25885                                         totalRecords : 0
25886                                 };
25887                         }
25888             var record = new Record(values, id);
25889             record.json = n;
25890             records[i] = record;
25891         }
25892         return {
25893             raw : o,
25894             success : success,
25895             records : records,
25896             totalRecords : totalRecords
25897         };
25898     },
25899     // used when loading children.. @see loadDataFromChildren
25900     toLoadData: function(rec)
25901     {
25902         // expect rec just to be an array.. eg [a,b,c, [...] << cn ]
25903         var data = typeof(rec.data.cn) == 'undefined' ? [] : rec.data.cn;
25904         return { data : data, total : data.length };
25905         
25906     }
25907 });/*
25908  * Based on:
25909  * Ext JS Library 1.1.1
25910  * Copyright(c) 2006-2007, Ext JS, LLC.
25911  *
25912  * Originally Released Under LGPL - original licence link has changed is not relivant.
25913  *
25914  * Fork - LGPL
25915  * <script type="text/javascript">
25916  */
25917
25918 /**
25919  * @class Roo.data.XmlReader
25920  * @extends Roo.data.DataReader
25921  * Data reader class to create an Array of {@link Roo.data.Record} objects from an XML document
25922  * based on mappings in a provided Roo.data.Record constructor.<br><br>
25923  * <p>
25924  * <em>Note that in order for the browser to parse a returned XML document, the Content-Type
25925  * header in the HTTP response must be set to "text/xml".</em>
25926  * <p>
25927  * Example code:
25928  * <pre><code>
25929 var RecordDef = Roo.data.Record.create([
25930    {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
25931    {name: 'occupation'}                 // This field will use "occupation" as the mapping.
25932 ]);
25933 var myReader = new Roo.data.XmlReader({
25934    totalRecords: "results", // The element which contains the total dataset size (optional)
25935    record: "row",           // The repeated element which contains row information
25936    id: "id"                 // The element within the row that provides an ID for the record (optional)
25937 }, RecordDef);
25938 </code></pre>
25939  * <p>
25940  * This would consume an XML file like this:
25941  * <pre><code>
25942 &lt;?xml?>
25943 &lt;dataset>
25944  &lt;results>2&lt;/results>
25945  &lt;row>
25946    &lt;id>1&lt;/id>
25947    &lt;name>Bill&lt;/name>
25948    &lt;occupation>Gardener&lt;/occupation>
25949  &lt;/row>
25950  &lt;row>
25951    &lt;id>2&lt;/id>
25952    &lt;name>Ben&lt;/name>
25953    &lt;occupation>Horticulturalist&lt;/occupation>
25954  &lt;/row>
25955 &lt;/dataset>
25956 </code></pre>
25957  * @cfg {String} totalRecords The DomQuery path from which to retrieve the total number of records
25958  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
25959  * paged from the remote server.
25960  * @cfg {String} record The DomQuery path to the repeated element which contains record information.
25961  * @cfg {String} success The DomQuery path to the success attribute used by forms.
25962  * @cfg {String} id The DomQuery path relative from the record element to the element that contains
25963  * a record identifier value.
25964  * @constructor
25965  * Create a new XmlReader
25966  * @param {Object} meta Metadata configuration options
25967  * @param {Mixed} recordType The definition of the data record type to produce.  This can be either a valid
25968  * Record subclass created with {@link Roo.data.Record#create}, or an array of objects with which to call
25969  * Roo.data.Record.create.  See the {@link Roo.data.Record} class for more details.
25970  */
25971 Roo.data.XmlReader = function(meta, recordType){
25972     meta = meta || {};
25973     Roo.data.XmlReader.superclass.constructor.call(this, meta, recordType||meta.fields);
25974 };
25975 Roo.extend(Roo.data.XmlReader, Roo.data.DataReader, {
25976     
25977     readerType : 'Xml',
25978     
25979     /**
25980      * This method is only used by a DataProxy which has retrieved data from a remote server.
25981          * @param {Object} response The XHR object which contains the parsed XML document.  The response is expected
25982          * to contain a method called 'responseXML' that returns an XML document object.
25983      * @return {Object} records A data block which is used by an {@link Roo.data.Store} as
25984      * a cache of Roo.data.Records.
25985      */
25986     read : function(response){
25987         var doc = response.responseXML;
25988         if(!doc) {
25989             throw {message: "XmlReader.read: XML Document not available"};
25990         }
25991         return this.readRecords(doc);
25992     },
25993
25994     /**
25995      * Create a data block containing Roo.data.Records from an XML document.
25996          * @param {Object} doc A parsed XML document.
25997      * @return {Object} records A data block which is used by an {@link Roo.data.Store} as
25998      * a cache of Roo.data.Records.
25999      */
26000     readRecords : function(doc){
26001         /**
26002          * After any data loads/reads, the raw XML Document is available for further custom processing.
26003          * @type XMLDocument
26004          */
26005         this.xmlData = doc;
26006         var root = doc.documentElement || doc;
26007         var q = Roo.DomQuery;
26008         var recordType = this.recordType, fields = recordType.prototype.fields;
26009         var sid = this.meta.id;
26010         var totalRecords = 0, success = true;
26011         if(this.meta.totalRecords){
26012             totalRecords = q.selectNumber(this.meta.totalRecords, root, 0);
26013         }
26014         
26015         if(this.meta.success){
26016             var sv = q.selectValue(this.meta.success, root, true);
26017             success = sv !== false && sv !== 'false';
26018         }
26019         var records = [];
26020         var ns = q.select(this.meta.record, root);
26021         for(var i = 0, len = ns.length; i < len; i++) {
26022                 var n = ns[i];
26023                 var values = {};
26024                 var id = sid ? q.selectValue(sid, n) : undefined;
26025                 for(var j = 0, jlen = fields.length; j < jlen; j++){
26026                     var f = fields.items[j];
26027                 var v = q.selectValue(f.mapping || f.name, n, f.defaultValue);
26028                     v = f.convert(v);
26029                     values[f.name] = v;
26030                 }
26031                 var record = new recordType(values, id);
26032                 record.node = n;
26033                 records[records.length] = record;
26034             }
26035
26036             return {
26037                 success : success,
26038                 records : records,
26039                 totalRecords : totalRecords || records.length
26040             };
26041     }
26042 });/*
26043  * Based on:
26044  * Ext JS Library 1.1.1
26045  * Copyright(c) 2006-2007, Ext JS, LLC.
26046  *
26047  * Originally Released Under LGPL - original licence link has changed is not relivant.
26048  *
26049  * Fork - LGPL
26050  * <script type="text/javascript">
26051  */
26052
26053 /**
26054  * @class Roo.data.ArrayReader
26055  * @extends Roo.data.DataReader
26056  * Data reader class to create an Array of Roo.data.Record objects from an Array.
26057  * Each element of that Array represents a row of data fields. The
26058  * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
26059  * of the field definition if it exists, or the field's ordinal position in the definition.<br>
26060  * <p>
26061  * Example code:.
26062  * <pre><code>
26063 var RecordDef = Roo.data.Record.create([
26064     {name: 'name', mapping: 1},         // "mapping" only needed if an "id" field is present which
26065     {name: 'occupation', mapping: 2}    // precludes using the ordinal position as the index.
26066 ]);
26067 var myReader = new Roo.data.ArrayReader({
26068     id: 0                     // The subscript within row Array that provides an ID for the Record (optional)
26069 }, RecordDef);
26070 </code></pre>
26071  * <p>
26072  * This would consume an Array like this:
26073  * <pre><code>
26074 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
26075   </code></pre>
26076  
26077  * @constructor
26078  * Create a new JsonReader
26079  * @param {Object} meta Metadata configuration options.
26080  * @param {Object|Array} recordType Either an Array of field definition objects
26081  * 
26082  * @cfg {Array} fields Array of field definition objects
26083  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
26084  * as specified to {@link Roo.data.Record#create},
26085  * or an {@link Roo.data.Record} object
26086  *
26087  * 
26088  * created using {@link Roo.data.Record#create}.
26089  */
26090 Roo.data.ArrayReader = function(meta, recordType)
26091 {    
26092     Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType||meta.fields);
26093 };
26094
26095 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
26096     
26097       /**
26098      * Create a data block containing Roo.data.Records from an XML document.
26099      * @param {Object} o An Array of row objects which represents the dataset.
26100      * @return {Object} A data block which is used by an {@link Roo.data.Store} object as
26101      * a cache of Roo.data.Records.
26102      */
26103     readRecords : function(o)
26104     {
26105         var sid = this.meta ? this.meta.id : null;
26106         var recordType = this.recordType, fields = recordType.prototype.fields;
26107         var records = [];
26108         var root = o;
26109         for(var i = 0; i < root.length; i++){
26110             var n = root[i];
26111             var values = {};
26112             var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
26113             for(var j = 0, jlen = fields.length; j < jlen; j++){
26114                 var f = fields.items[j];
26115                 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
26116                 var v = n[k] !== undefined ? n[k] : f.defaultValue;
26117                 v = f.convert(v);
26118                 values[f.name] = v;
26119             }
26120             var record = new recordType(values, id);
26121             record.json = n;
26122             records[records.length] = record;
26123         }
26124         return {
26125             records : records,
26126             totalRecords : records.length
26127         };
26128     },
26129     // used when loading children.. @see loadDataFromChildren
26130     toLoadData: function(rec)
26131     {
26132         // expect rec just to be an array.. eg [a,b,c, [...] << cn ]
26133         return typeof(rec.data.cn) == 'undefined' ? [] : rec.data.cn;
26134         
26135     }
26136     
26137     
26138 });/*
26139  * Based on:
26140  * Ext JS Library 1.1.1
26141  * Copyright(c) 2006-2007, Ext JS, LLC.
26142  *
26143  * Originally Released Under LGPL - original licence link has changed is not relivant.
26144  *
26145  * Fork - LGPL
26146  * <script type="text/javascript">
26147  */
26148
26149
26150 /**
26151  * @class Roo.data.Tree
26152  * @extends Roo.util.Observable
26153  * Represents a tree data structure and bubbles all the events for its nodes. The nodes
26154  * in the tree have most standard DOM functionality.
26155  * @constructor
26156  * @param {Node} root (optional) The root node
26157  */
26158 Roo.data.Tree = function(root){
26159    this.nodeHash = {};
26160    /**
26161     * The root node for this tree
26162     * @type Node
26163     */
26164    this.root = null;
26165    if(root){
26166        this.setRootNode(root);
26167    }
26168    this.addEvents({
26169        /**
26170         * @event append
26171         * Fires when a new child node is appended to a node in this tree.
26172         * @param {Tree} tree The owner tree
26173         * @param {Node} parent The parent node
26174         * @param {Node} node The newly appended node
26175         * @param {Number} index The index of the newly appended node
26176         */
26177        "append" : true,
26178        /**
26179         * @event remove
26180         * Fires when a child node is removed from a node in this tree.
26181         * @param {Tree} tree The owner tree
26182         * @param {Node} parent The parent node
26183         * @param {Node} node The child node removed
26184         */
26185        "remove" : true,
26186        /**
26187         * @event move
26188         * Fires when a node is moved to a new location in the tree
26189         * @param {Tree} tree The owner tree
26190         * @param {Node} node The node moved
26191         * @param {Node} oldParent The old parent of this node
26192         * @param {Node} newParent The new parent of this node
26193         * @param {Number} index The index it was moved to
26194         */
26195        "move" : true,
26196        /**
26197         * @event insert
26198         * Fires when a new child node is inserted in a node in this tree.
26199         * @param {Tree} tree The owner tree
26200         * @param {Node} parent The parent node
26201         * @param {Node} node The child node inserted
26202         * @param {Node} refNode The child node the node was inserted before
26203         */
26204        "insert" : true,
26205        /**
26206         * @event beforeappend
26207         * Fires before a new child is appended to a node in this tree, return false to cancel the append.
26208         * @param {Tree} tree The owner tree
26209         * @param {Node} parent The parent node
26210         * @param {Node} node The child node to be appended
26211         */
26212        "beforeappend" : true,
26213        /**
26214         * @event beforeremove
26215         * Fires before a child is removed from a node in this tree, return false to cancel the remove.
26216         * @param {Tree} tree The owner tree
26217         * @param {Node} parent The parent node
26218         * @param {Node} node The child node to be removed
26219         */
26220        "beforeremove" : true,
26221        /**
26222         * @event beforemove
26223         * Fires before a node is moved to a new location in the tree. Return false to cancel the move.
26224         * @param {Tree} tree The owner tree
26225         * @param {Node} node The node being moved
26226         * @param {Node} oldParent The parent of the node
26227         * @param {Node} newParent The new parent the node is moving to
26228         * @param {Number} index The index it is being moved to
26229         */
26230        "beforemove" : true,
26231        /**
26232         * @event beforeinsert
26233         * Fires before a new child is inserted in a node in this tree, return false to cancel the insert.
26234         * @param {Tree} tree The owner tree
26235         * @param {Node} parent The parent node
26236         * @param {Node} node The child node to be inserted
26237         * @param {Node} refNode The child node the node is being inserted before
26238         */
26239        "beforeinsert" : true
26240    });
26241
26242     Roo.data.Tree.superclass.constructor.call(this);
26243 };
26244
26245 Roo.extend(Roo.data.Tree, Roo.util.Observable, {
26246     pathSeparator: "/",
26247
26248     proxyNodeEvent : function(){
26249         return this.fireEvent.apply(this, arguments);
26250     },
26251
26252     /**
26253      * Returns the root node for this tree.
26254      * @return {Node}
26255      */
26256     getRootNode : function(){
26257         return this.root;
26258     },
26259
26260     /**
26261      * Sets the root node for this tree.
26262      * @param {Node} node
26263      * @return {Node}
26264      */
26265     setRootNode : function(node){
26266         this.root = node;
26267         node.ownerTree = this;
26268         node.isRoot = true;
26269         this.registerNode(node);
26270         return node;
26271     },
26272
26273     /**
26274      * Gets a node in this tree by its id.
26275      * @param {String} id
26276      * @return {Node}
26277      */
26278     getNodeById : function(id){
26279         return this.nodeHash[id];
26280     },
26281
26282     registerNode : function(node){
26283         this.nodeHash[node.id] = node;
26284     },
26285
26286     unregisterNode : function(node){
26287         delete this.nodeHash[node.id];
26288     },
26289
26290     toString : function(){
26291         return "[Tree"+(this.id?" "+this.id:"")+"]";
26292     }
26293 });
26294
26295 /**
26296  * @class Roo.data.Node
26297  * @extends Roo.util.Observable
26298  * @cfg {Boolean} leaf true if this node is a leaf and does not have children
26299  * @cfg {String} id The id for this node. If one is not specified, one is generated.
26300  * @constructor
26301  * @param {Object} attributes The attributes/config for the node
26302  */
26303 Roo.data.Node = function(attributes){
26304     /**
26305      * The attributes supplied for the node. You can use this property to access any custom attributes you supplied.
26306      * @type {Object}
26307      */
26308     this.attributes = attributes || {};
26309     this.leaf = this.attributes.leaf;
26310     /**
26311      * The node id. @type String
26312      */
26313     this.id = this.attributes.id;
26314     if(!this.id){
26315         this.id = Roo.id(null, "ynode-");
26316         this.attributes.id = this.id;
26317     }
26318      
26319     
26320     /**
26321      * All child nodes of this node. @type Array
26322      */
26323     this.childNodes = [];
26324     if(!this.childNodes.indexOf){ // indexOf is a must
26325         this.childNodes.indexOf = function(o){
26326             for(var i = 0, len = this.length; i < len; i++){
26327                 if(this[i] == o) {
26328                     return i;
26329                 }
26330             }
26331             return -1;
26332         };
26333     }
26334     /**
26335      * The parent node for this node. @type Node
26336      */
26337     this.parentNode = null;
26338     /**
26339      * The first direct child node of this node, or null if this node has no child nodes. @type Node
26340      */
26341     this.firstChild = null;
26342     /**
26343      * The last direct child node of this node, or null if this node has no child nodes. @type Node
26344      */
26345     this.lastChild = null;
26346     /**
26347      * The node immediately preceding this node in the tree, or null if there is no sibling node. @type Node
26348      */
26349     this.previousSibling = null;
26350     /**
26351      * The node immediately following this node in the tree, or null if there is no sibling node. @type Node
26352      */
26353     this.nextSibling = null;
26354
26355     this.addEvents({
26356        /**
26357         * @event append
26358         * Fires when a new child node is appended
26359         * @param {Tree} tree The owner tree
26360         * @param {Node} this This node
26361         * @param {Node} node The newly appended node
26362         * @param {Number} index The index of the newly appended node
26363         */
26364        "append" : true,
26365        /**
26366         * @event remove
26367         * Fires when a child node is removed
26368         * @param {Tree} tree The owner tree
26369         * @param {Node} this This node
26370         * @param {Node} node The removed node
26371         */
26372        "remove" : true,
26373        /**
26374         * @event move
26375         * Fires when this node is moved to a new location in the tree
26376         * @param {Tree} tree The owner tree
26377         * @param {Node} this This node
26378         * @param {Node} oldParent The old parent of this node
26379         * @param {Node} newParent The new parent of this node
26380         * @param {Number} index The index it was moved to
26381         */
26382        "move" : true,
26383        /**
26384         * @event insert
26385         * Fires when a new child node is inserted.
26386         * @param {Tree} tree The owner tree
26387         * @param {Node} this This node
26388         * @param {Node} node The child node inserted
26389         * @param {Node} refNode The child node the node was inserted before
26390         */
26391        "insert" : true,
26392        /**
26393         * @event beforeappend
26394         * Fires before a new child is appended, return false to cancel the append.
26395         * @param {Tree} tree The owner tree
26396         * @param {Node} this This node
26397         * @param {Node} node The child node to be appended
26398         */
26399        "beforeappend" : true,
26400        /**
26401         * @event beforeremove
26402         * Fires before a child is removed, return false to cancel the remove.
26403         * @param {Tree} tree The owner tree
26404         * @param {Node} this This node
26405         * @param {Node} node The child node to be removed
26406         */
26407        "beforeremove" : true,
26408        /**
26409         * @event beforemove
26410         * Fires before this node is moved to a new location in the tree. Return false to cancel the move.
26411         * @param {Tree} tree The owner tree
26412         * @param {Node} this This node
26413         * @param {Node} oldParent The parent of this node
26414         * @param {Node} newParent The new parent this node is moving to
26415         * @param {Number} index The index it is being moved to
26416         */
26417        "beforemove" : true,
26418        /**
26419         * @event beforeinsert
26420         * Fires before a new child is inserted, return false to cancel the insert.
26421         * @param {Tree} tree The owner tree
26422         * @param {Node} this This node
26423         * @param {Node} node The child node to be inserted
26424         * @param {Node} refNode The child node the node is being inserted before
26425         */
26426        "beforeinsert" : true
26427    });
26428     this.listeners = this.attributes.listeners;
26429     Roo.data.Node.superclass.constructor.call(this);
26430 };
26431
26432 Roo.extend(Roo.data.Node, Roo.util.Observable, {
26433     fireEvent : function(evtName){
26434         // first do standard event for this node
26435         if(Roo.data.Node.superclass.fireEvent.apply(this, arguments) === false){
26436             return false;
26437         }
26438         // then bubble it up to the tree if the event wasn't cancelled
26439         var ot = this.getOwnerTree();
26440         if(ot){
26441             if(ot.proxyNodeEvent.apply(ot, arguments) === false){
26442                 return false;
26443             }
26444         }
26445         return true;
26446     },
26447
26448     /**
26449      * Returns true if this node is a leaf
26450      * @return {Boolean}
26451      */
26452     isLeaf : function(){
26453         return this.leaf === true;
26454     },
26455
26456     // private
26457     setFirstChild : function(node){
26458         this.firstChild = node;
26459     },
26460
26461     //private
26462     setLastChild : function(node){
26463         this.lastChild = node;
26464     },
26465
26466
26467     /**
26468      * Returns true if this node is the last child of its parent
26469      * @return {Boolean}
26470      */
26471     isLast : function(){
26472        return (!this.parentNode ? true : this.parentNode.lastChild == this);
26473     },
26474
26475     /**
26476      * Returns true if this node is the first child of its parent
26477      * @return {Boolean}
26478      */
26479     isFirst : function(){
26480        return (!this.parentNode ? true : this.parentNode.firstChild == this);
26481     },
26482
26483     hasChildNodes : function(){
26484         return !this.isLeaf() && this.childNodes.length > 0;
26485     },
26486
26487     /**
26488      * Insert node(s) as the last child node of this node.
26489      * @param {Node/Array} node The node or Array of nodes to append
26490      * @return {Node} The appended node if single append, or null if an array was passed
26491      */
26492     appendChild : function(node){
26493         var multi = false;
26494         if(node instanceof Array){
26495             multi = node;
26496         }else if(arguments.length > 1){
26497             multi = arguments;
26498         }
26499         
26500         // if passed an array or multiple args do them one by one
26501         if(multi){
26502             for(var i = 0, len = multi.length; i < len; i++) {
26503                 this.appendChild(multi[i]);
26504             }
26505         }else{
26506             if(this.fireEvent("beforeappend", this.ownerTree, this, node) === false){
26507                 return false;
26508             }
26509             var index = this.childNodes.length;
26510             var oldParent = node.parentNode;
26511             // it's a move, make sure we move it cleanly
26512             if(oldParent){
26513                 if(node.fireEvent("beforemove", node.getOwnerTree(), node, oldParent, this, index) === false){
26514                     return false;
26515                 }
26516                 oldParent.removeChild(node);
26517             }
26518             
26519             index = this.childNodes.length;
26520             if(index == 0){
26521                 this.setFirstChild(node);
26522             }
26523             this.childNodes.push(node);
26524             node.parentNode = this;
26525             var ps = this.childNodes[index-1];
26526             if(ps){
26527                 node.previousSibling = ps;
26528                 ps.nextSibling = node;
26529             }else{
26530                 node.previousSibling = null;
26531             }
26532             node.nextSibling = null;
26533             this.setLastChild(node);
26534             node.setOwnerTree(this.getOwnerTree());
26535             this.fireEvent("append", this.ownerTree, this, node, index);
26536             if(this.ownerTree) {
26537                 this.ownerTree.fireEvent("appendnode", this, node, index);
26538             }
26539             if(oldParent){
26540                 node.fireEvent("move", this.ownerTree, node, oldParent, this, index);
26541             }
26542             return node;
26543         }
26544     },
26545
26546     /**
26547      * Removes a child node from this node.
26548      * @param {Node} node The node to remove
26549      * @return {Node} The removed node
26550      */
26551     removeChild : function(node){
26552         var index = this.childNodes.indexOf(node);
26553         if(index == -1){
26554             return false;
26555         }
26556         if(this.fireEvent("beforeremove", this.ownerTree, this, node) === false){
26557             return false;
26558         }
26559
26560         // remove it from childNodes collection
26561         this.childNodes.splice(index, 1);
26562
26563         // update siblings
26564         if(node.previousSibling){
26565             node.previousSibling.nextSibling = node.nextSibling;
26566         }
26567         if(node.nextSibling){
26568             node.nextSibling.previousSibling = node.previousSibling;
26569         }
26570
26571         // update child refs
26572         if(this.firstChild == node){
26573             this.setFirstChild(node.nextSibling);
26574         }
26575         if(this.lastChild == node){
26576             this.setLastChild(node.previousSibling);
26577         }
26578
26579         node.setOwnerTree(null);
26580         // clear any references from the node
26581         node.parentNode = null;
26582         node.previousSibling = null;
26583         node.nextSibling = null;
26584         this.fireEvent("remove", this.ownerTree, this, node);
26585         return node;
26586     },
26587
26588     /**
26589      * Inserts the first node before the second node in this nodes childNodes collection.
26590      * @param {Node} node The node to insert
26591      * @param {Node} refNode The node to insert before (if null the node is appended)
26592      * @return {Node} The inserted node
26593      */
26594     insertBefore : function(node, refNode){
26595         if(!refNode){ // like standard Dom, refNode can be null for append
26596             return this.appendChild(node);
26597         }
26598         // nothing to do
26599         if(node == refNode){
26600             return false;
26601         }
26602
26603         if(this.fireEvent("beforeinsert", this.ownerTree, this, node, refNode) === false){
26604             return false;
26605         }
26606         var index = this.childNodes.indexOf(refNode);
26607         var oldParent = node.parentNode;
26608         var refIndex = index;
26609
26610         // when moving internally, indexes will change after remove
26611         if(oldParent == this && this.childNodes.indexOf(node) < index){
26612             refIndex--;
26613         }
26614
26615         // it's a move, make sure we move it cleanly
26616         if(oldParent){
26617             if(node.fireEvent("beforemove", node.getOwnerTree(), node, oldParent, this, index, refNode) === false){
26618                 return false;
26619             }
26620             oldParent.removeChild(node);
26621         }
26622         if(refIndex == 0){
26623             this.setFirstChild(node);
26624         }
26625         this.childNodes.splice(refIndex, 0, node);
26626         node.parentNode = this;
26627         var ps = this.childNodes[refIndex-1];
26628         if(ps){
26629             node.previousSibling = ps;
26630             ps.nextSibling = node;
26631         }else{
26632             node.previousSibling = null;
26633         }
26634         node.nextSibling = refNode;
26635         refNode.previousSibling = node;
26636         node.setOwnerTree(this.getOwnerTree());
26637         this.fireEvent("insert", this.ownerTree, this, node, refNode);
26638         if(oldParent){
26639             node.fireEvent("move", this.ownerTree, node, oldParent, this, refIndex, refNode);
26640         }
26641         return node;
26642     },
26643
26644     /**
26645      * Returns the child node at the specified index.
26646      * @param {Number} index
26647      * @return {Node}
26648      */
26649     item : function(index){
26650         return this.childNodes[index];
26651     },
26652
26653     /**
26654      * Replaces one child node in this node with another.
26655      * @param {Node} newChild The replacement node
26656      * @param {Node} oldChild The node to replace
26657      * @return {Node} The replaced node
26658      */
26659     replaceChild : function(newChild, oldChild){
26660         this.insertBefore(newChild, oldChild);
26661         this.removeChild(oldChild);
26662         return oldChild;
26663     },
26664
26665     /**
26666      * Returns the index of a child node
26667      * @param {Node} node
26668      * @return {Number} The index of the node or -1 if it was not found
26669      */
26670     indexOf : function(child){
26671         return this.childNodes.indexOf(child);
26672     },
26673
26674     /**
26675      * Returns the tree this node is in.
26676      * @return {Tree}
26677      */
26678     getOwnerTree : function(){
26679         // if it doesn't have one, look for one
26680         if(!this.ownerTree){
26681             var p = this;
26682             while(p){
26683                 if(p.ownerTree){
26684                     this.ownerTree = p.ownerTree;
26685                     break;
26686                 }
26687                 p = p.parentNode;
26688             }
26689         }
26690         return this.ownerTree;
26691     },
26692
26693     /**
26694      * Returns depth of this node (the root node has a depth of 0)
26695      * @return {Number}
26696      */
26697     getDepth : function(){
26698         var depth = 0;
26699         var p = this;
26700         while(p.parentNode){
26701             ++depth;
26702             p = p.parentNode;
26703         }
26704         return depth;
26705     },
26706
26707     // private
26708     setOwnerTree : function(tree){
26709         // if it's move, we need to update everyone
26710         if(tree != this.ownerTree){
26711             if(this.ownerTree){
26712                 this.ownerTree.unregisterNode(this);
26713             }
26714             this.ownerTree = tree;
26715             var cs = this.childNodes;
26716             for(var i = 0, len = cs.length; i < len; i++) {
26717                 cs[i].setOwnerTree(tree);
26718             }
26719             if(tree){
26720                 tree.registerNode(this);
26721             }
26722         }
26723     },
26724
26725     /**
26726      * Returns the path for this node. The path can be used to expand or select this node programmatically.
26727      * @param {String} attr (optional) The attr to use for the path (defaults to the node's id)
26728      * @return {String} The path
26729      */
26730     getPath : function(attr){
26731         attr = attr || "id";
26732         var p = this.parentNode;
26733         var b = [this.attributes[attr]];
26734         while(p){
26735             b.unshift(p.attributes[attr]);
26736             p = p.parentNode;
26737         }
26738         var sep = this.getOwnerTree().pathSeparator;
26739         return sep + b.join(sep);
26740     },
26741
26742     /**
26743      * Bubbles up the tree from this node, calling the specified function with each node. The scope (<i>this</i>) of
26744      * function call will be the scope provided or the current node. The arguments to the function
26745      * will be the args provided or the current node. If the function returns false at any point,
26746      * the bubble is stopped.
26747      * @param {Function} fn The function to call
26748      * @param {Object} scope (optional) The scope of the function (defaults to current node)
26749      * @param {Array} args (optional) The args to call the function with (default to passing the current node)
26750      */
26751     bubble : function(fn, scope, args){
26752         var p = this;
26753         while(p){
26754             if(fn.call(scope || p, args || p) === false){
26755                 break;
26756             }
26757             p = p.parentNode;
26758         }
26759     },
26760
26761     /**
26762      * Cascades down the tree from this node, calling the specified function with each node. The scope (<i>this</i>) of
26763      * function call will be the scope provided or the current node. The arguments to the function
26764      * will be the args provided or the current node. If the function returns false at any point,
26765      * the cascade is stopped on that branch.
26766      * @param {Function} fn The function to call
26767      * @param {Object} scope (optional) The scope of the function (defaults to current node)
26768      * @param {Array} args (optional) The args to call the function with (default to passing the current node)
26769      */
26770     cascade : function(fn, scope, args){
26771         if(fn.call(scope || this, args || this) !== false){
26772             var cs = this.childNodes;
26773             for(var i = 0, len = cs.length; i < len; i++) {
26774                 cs[i].cascade(fn, scope, args);
26775             }
26776         }
26777     },
26778
26779     /**
26780      * Interates the child nodes of this node, calling the specified function with each node. The scope (<i>this</i>) of
26781      * function call will be the scope provided or the current node. The arguments to the function
26782      * will be the args provided or the current node. If the function returns false at any point,
26783      * the iteration stops.
26784      * @param {Function} fn The function to call
26785      * @param {Object} scope (optional) The scope of the function (defaults to current node)
26786      * @param {Array} args (optional) The args to call the function with (default to passing the current node)
26787      */
26788     eachChild : function(fn, scope, args){
26789         var cs = this.childNodes;
26790         for(var i = 0, len = cs.length; i < len; i++) {
26791                 if(fn.call(scope || this, args || cs[i]) === false){
26792                     break;
26793                 }
26794         }
26795     },
26796
26797     /**
26798      * Finds the first child that has the attribute with the specified value.
26799      * @param {String} attribute The attribute name
26800      * @param {Mixed} value The value to search for
26801      * @return {Node} The found child or null if none was found
26802      */
26803     findChild : function(attribute, value){
26804         var cs = this.childNodes;
26805         for(var i = 0, len = cs.length; i < len; i++) {
26806                 if(cs[i].attributes[attribute] == value){
26807                     return cs[i];
26808                 }
26809         }
26810         return null;
26811     },
26812
26813     /**
26814      * Finds the first child by a custom function. The child matches if the function passed
26815      * returns true.
26816      * @param {Function} fn
26817      * @param {Object} scope (optional)
26818      * @return {Node} The found child or null if none was found
26819      */
26820     findChildBy : function(fn, scope){
26821         var cs = this.childNodes;
26822         for(var i = 0, len = cs.length; i < len; i++) {
26823                 if(fn.call(scope||cs[i], cs[i]) === true){
26824                     return cs[i];
26825                 }
26826         }
26827         return null;
26828     },
26829
26830     /**
26831      * Sorts this nodes children using the supplied sort function
26832      * @param {Function} fn
26833      * @param {Object} scope (optional)
26834      */
26835     sort : function(fn, scope){
26836         var cs = this.childNodes;
26837         var len = cs.length;
26838         if(len > 0){
26839             var sortFn = scope ? function(){fn.apply(scope, arguments);} : fn;
26840             cs.sort(sortFn);
26841             for(var i = 0; i < len; i++){
26842                 var n = cs[i];
26843                 n.previousSibling = cs[i-1];
26844                 n.nextSibling = cs[i+1];
26845                 if(i == 0){
26846                     this.setFirstChild(n);
26847                 }
26848                 if(i == len-1){
26849                     this.setLastChild(n);
26850                 }
26851             }
26852         }
26853     },
26854
26855     /**
26856      * Returns true if this node is an ancestor (at any point) of the passed node.
26857      * @param {Node} node
26858      * @return {Boolean}
26859      */
26860     contains : function(node){
26861         return node.isAncestor(this);
26862     },
26863
26864     /**
26865      * Returns true if the passed node is an ancestor (at any point) of this node.
26866      * @param {Node} node
26867      * @return {Boolean}
26868      */
26869     isAncestor : function(node){
26870         var p = this.parentNode;
26871         while(p){
26872             if(p == node){
26873                 return true;
26874             }
26875             p = p.parentNode;
26876         }
26877         return false;
26878     },
26879
26880     toString : function(){
26881         return "[Node"+(this.id?" "+this.id:"")+"]";
26882     }
26883 });/*
26884  * Based on:
26885  * Ext JS Library 1.1.1
26886  * Copyright(c) 2006-2007, Ext JS, LLC.
26887  *
26888  * Originally Released Under LGPL - original licence link has changed is not relivant.
26889  *
26890  * Fork - LGPL
26891  * <script type="text/javascript">
26892  */
26893
26894
26895 /**
26896  * @class Roo.Shadow
26897  * Simple class that can provide a shadow effect for any element.  Note that the element MUST be absolutely positioned,
26898  * and the shadow does not provide any shimming.  This should be used only in simple cases -- for more advanced
26899  * functionality that can also provide the same shadow effect, see the {@link Roo.Layer} class.
26900  * @constructor
26901  * Create a new Shadow
26902  * @param {Object} config The config object
26903  */
26904 Roo.Shadow = function(config){
26905     Roo.apply(this, config);
26906     if(typeof this.mode != "string"){
26907         this.mode = this.defaultMode;
26908     }
26909     var o = this.offset, a = {h: 0};
26910     var rad = Math.floor(this.offset/2);
26911     switch(this.mode.toLowerCase()){ // all this hideous nonsense calculates the various offsets for shadows
26912         case "drop":
26913             a.w = 0;
26914             a.l = a.t = o;
26915             a.t -= 1;
26916             if(Roo.isIE){
26917                 a.l -= this.offset + rad;
26918                 a.t -= this.offset + rad;
26919                 a.w -= rad;
26920                 a.h -= rad;
26921                 a.t += 1;
26922             }
26923         break;
26924         case "sides":
26925             a.w = (o*2);
26926             a.l = -o;
26927             a.t = o-1;
26928             if(Roo.isIE){
26929                 a.l -= (this.offset - rad);
26930                 a.t -= this.offset + rad;
26931                 a.l += 1;
26932                 a.w -= (this.offset - rad)*2;
26933                 a.w -= rad + 1;
26934                 a.h -= 1;
26935             }
26936         break;
26937         case "frame":
26938             a.w = a.h = (o*2);
26939             a.l = a.t = -o;
26940             a.t += 1;
26941             a.h -= 2;
26942             if(Roo.isIE){
26943                 a.l -= (this.offset - rad);
26944                 a.t -= (this.offset - rad);
26945                 a.l += 1;
26946                 a.w -= (this.offset + rad + 1);
26947                 a.h -= (this.offset + rad);
26948                 a.h += 1;
26949             }
26950         break;
26951     };
26952
26953     this.adjusts = a;
26954 };
26955
26956 Roo.Shadow.prototype = {
26957     /**
26958      * @cfg {String} mode
26959      * The shadow display mode.  Supports the following options:<br />
26960      * sides: Shadow displays on both sides and bottom only<br />
26961      * frame: Shadow displays equally on all four sides<br />
26962      * drop: Traditional bottom-right drop shadow (default)
26963      */
26964     mode: false,
26965     /**
26966      * @cfg {String} offset
26967      * The number of pixels to offset the shadow from the element (defaults to 4)
26968      */
26969     offset: 4,
26970
26971     // private
26972     defaultMode: "drop",
26973
26974     /**
26975      * Displays the shadow under the target element
26976      * @param {String/HTMLElement/Element} targetEl The id or element under which the shadow should display
26977      */
26978     show : function(target){
26979         target = Roo.get(target);
26980         if(!this.el){
26981             this.el = Roo.Shadow.Pool.pull();
26982             if(this.el.dom.nextSibling != target.dom){
26983                 this.el.insertBefore(target);
26984             }
26985         }
26986         this.el.setStyle("z-index", this.zIndex || parseInt(target.getStyle("z-index"), 10)-1);
26987         if(Roo.isIE){
26988             this.el.dom.style.filter="progid:DXImageTransform.Microsoft.alpha(opacity=50) progid:DXImageTransform.Microsoft.Blur(pixelradius="+(this.offset)+")";
26989         }
26990         this.realign(
26991             target.getLeft(true),
26992             target.getTop(true),
26993             target.getWidth(),
26994             target.getHeight()
26995         );
26996         this.el.dom.style.display = "block";
26997     },
26998
26999     /**
27000      * Returns true if the shadow is visible, else false
27001      */
27002     isVisible : function(){
27003         return this.el ? true : false;  
27004     },
27005
27006     /**
27007      * Direct alignment when values are already available. Show must be called at least once before
27008      * calling this method to ensure it is initialized.
27009      * @param {Number} left The target element left position
27010      * @param {Number} top The target element top position
27011      * @param {Number} width The target element width
27012      * @param {Number} height The target element height
27013      */
27014     realign : function(l, t, w, h){
27015         if(!this.el){
27016             return;
27017         }
27018         var a = this.adjusts, d = this.el.dom, s = d.style;
27019         var iea = 0;
27020         s.left = (l+a.l)+"px";
27021         s.top = (t+a.t)+"px";
27022         var sw = (w+a.w), sh = (h+a.h), sws = sw +"px", shs = sh + "px";
27023  
27024         if(s.width != sws || s.height != shs){
27025             s.width = sws;
27026             s.height = shs;
27027             if(!Roo.isIE){
27028                 var cn = d.childNodes;
27029                 var sww = Math.max(0, (sw-12))+"px";
27030                 cn[0].childNodes[1].style.width = sww;
27031                 cn[1].childNodes[1].style.width = sww;
27032                 cn[2].childNodes[1].style.width = sww;
27033                 cn[1].style.height = Math.max(0, (sh-12))+"px";
27034             }
27035         }
27036     },
27037
27038     /**
27039      * Hides this shadow
27040      */
27041     hide : function(){
27042         if(this.el){
27043             this.el.dom.style.display = "none";
27044             Roo.Shadow.Pool.push(this.el);
27045             delete this.el;
27046         }
27047     },
27048
27049     /**
27050      * Adjust the z-index of this shadow
27051      * @param {Number} zindex The new z-index
27052      */
27053     setZIndex : function(z){
27054         this.zIndex = z;
27055         if(this.el){
27056             this.el.setStyle("z-index", z);
27057         }
27058     }
27059 };
27060
27061 // Private utility class that manages the internal Shadow cache
27062 Roo.Shadow.Pool = function(){
27063     var p = [];
27064     var markup = Roo.isIE ?
27065                  '<div class="x-ie-shadow"></div>' :
27066                  '<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>';
27067     return {
27068         pull : function(){
27069             var sh = p.shift();
27070             if(!sh){
27071                 sh = Roo.get(Roo.DomHelper.insertHtml("beforeBegin", document.body.firstChild, markup));
27072                 sh.autoBoxAdjust = false;
27073             }
27074             return sh;
27075         },
27076
27077         push : function(sh){
27078             p.push(sh);
27079         }
27080     };
27081 }();/*
27082  * Based on:
27083  * Ext JS Library 1.1.1
27084  * Copyright(c) 2006-2007, Ext JS, LLC.
27085  *
27086  * Originally Released Under LGPL - original licence link has changed is not relivant.
27087  *
27088  * Fork - LGPL
27089  * <script type="text/javascript">
27090  */
27091
27092
27093 /**
27094  * @class Roo.SplitBar
27095  * @extends Roo.util.Observable
27096  * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
27097  * <br><br>
27098  * Usage:
27099  * <pre><code>
27100 var split = new Roo.SplitBar("elementToDrag", "elementToSize",
27101                    Roo.SplitBar.HORIZONTAL, Roo.SplitBar.LEFT);
27102 split.setAdapter(new Roo.SplitBar.AbsoluteLayoutAdapter("container"));
27103 split.minSize = 100;
27104 split.maxSize = 600;
27105 split.animate = true;
27106 split.on('moved', splitterMoved);
27107 </code></pre>
27108  * @constructor
27109  * Create a new SplitBar
27110  * @param {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar. 
27111  * @param {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged 
27112  * @param {Number} orientation (optional) Either Roo.SplitBar.HORIZONTAL or Roo.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
27113  * @param {Number} placement (optional) Either Roo.SplitBar.LEFT or Roo.SplitBar.RIGHT for horizontal or  
27114                         Roo.SplitBar.TOP or Roo.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
27115                         position of the SplitBar).
27116  */
27117 Roo.SplitBar = function(dragElement, resizingElement, orientation, placement, existingProxy){
27118     
27119     /** @private */
27120     this.el = Roo.get(dragElement, true);
27121     this.el.dom.unselectable = "on";
27122     /** @private */
27123     this.resizingEl = Roo.get(resizingElement, true);
27124
27125     /**
27126      * @private
27127      * The orientation of the split. Either Roo.SplitBar.HORIZONTAL or Roo.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
27128      * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
27129      * @type Number
27130      */
27131     this.orientation = orientation || Roo.SplitBar.HORIZONTAL;
27132     
27133     /**
27134      * The minimum size of the resizing element. (Defaults to 0)
27135      * @type Number
27136      */
27137     this.minSize = 0;
27138     
27139     /**
27140      * The maximum size of the resizing element. (Defaults to 2000)
27141      * @type Number
27142      */
27143     this.maxSize = 2000;
27144     
27145     /**
27146      * Whether to animate the transition to the new size
27147      * @type Boolean
27148      */
27149     this.animate = false;
27150     
27151     /**
27152      * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
27153      * @type Boolean
27154      */
27155     this.useShim = false;
27156     
27157     /** @private */
27158     this.shim = null;
27159     
27160     if(!existingProxy){
27161         /** @private */
27162         this.proxy = Roo.SplitBar.createProxy(this.orientation);
27163     }else{
27164         this.proxy = Roo.get(existingProxy).dom;
27165     }
27166     /** @private */
27167     this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
27168     
27169     /** @private */
27170     this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
27171     
27172     /** @private */
27173     this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
27174     
27175     /** @private */
27176     this.dragSpecs = {};
27177     
27178     /**
27179      * @private The adapter to use to positon and resize elements
27180      */
27181     this.adapter = new Roo.SplitBar.BasicLayoutAdapter();
27182     this.adapter.init(this);
27183     
27184     if(this.orientation == Roo.SplitBar.HORIZONTAL){
27185         /** @private */
27186         this.placement = placement || (this.el.getX() > this.resizingEl.getX() ? Roo.SplitBar.LEFT : Roo.SplitBar.RIGHT);
27187         this.el.addClass("x-splitbar-h");
27188     }else{
27189         /** @private */
27190         this.placement = placement || (this.el.getY() > this.resizingEl.getY() ? Roo.SplitBar.TOP : Roo.SplitBar.BOTTOM);
27191         this.el.addClass("x-splitbar-v");
27192     }
27193     
27194     this.addEvents({
27195         /**
27196          * @event resize
27197          * Fires when the splitter is moved (alias for {@link #event-moved})
27198          * @param {Roo.SplitBar} this
27199          * @param {Number} newSize the new width or height
27200          */
27201         "resize" : true,
27202         /**
27203          * @event moved
27204          * Fires when the splitter is moved
27205          * @param {Roo.SplitBar} this
27206          * @param {Number} newSize the new width or height
27207          */
27208         "moved" : true,
27209         /**
27210          * @event beforeresize
27211          * Fires before the splitter is dragged
27212          * @param {Roo.SplitBar} this
27213          */
27214         "beforeresize" : true,
27215
27216         "beforeapply" : true
27217     });
27218
27219     Roo.util.Observable.call(this);
27220 };
27221
27222 Roo.extend(Roo.SplitBar, Roo.util.Observable, {
27223     onStartProxyDrag : function(x, y){
27224         this.fireEvent("beforeresize", this);
27225         if(!this.overlay){
27226             var o = Roo.DomHelper.insertFirst(document.body,  {cls: "x-drag-overlay", html: "&#160;"}, true);
27227             o.unselectable();
27228             o.enableDisplayMode("block");
27229             // all splitbars share the same overlay
27230             Roo.SplitBar.prototype.overlay = o;
27231         }
27232         this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
27233         this.overlay.show();
27234         Roo.get(this.proxy).setDisplayed("block");
27235         var size = this.adapter.getElementSize(this);
27236         this.activeMinSize = this.getMinimumSize();;
27237         this.activeMaxSize = this.getMaximumSize();;
27238         var c1 = size - this.activeMinSize;
27239         var c2 = Math.max(this.activeMaxSize - size, 0);
27240         if(this.orientation == Roo.SplitBar.HORIZONTAL){
27241             this.dd.resetConstraints();
27242             this.dd.setXConstraint(
27243                 this.placement == Roo.SplitBar.LEFT ? c1 : c2, 
27244                 this.placement == Roo.SplitBar.LEFT ? c2 : c1
27245             );
27246             this.dd.setYConstraint(0, 0);
27247         }else{
27248             this.dd.resetConstraints();
27249             this.dd.setXConstraint(0, 0);
27250             this.dd.setYConstraint(
27251                 this.placement == Roo.SplitBar.TOP ? c1 : c2, 
27252                 this.placement == Roo.SplitBar.TOP ? c2 : c1
27253             );
27254          }
27255         this.dragSpecs.startSize = size;
27256         this.dragSpecs.startPoint = [x, y];
27257         Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
27258     },
27259     
27260     /** 
27261      * @private Called after the drag operation by the DDProxy
27262      */
27263     onEndProxyDrag : function(e){
27264         Roo.get(this.proxy).setDisplayed(false);
27265         var endPoint = Roo.lib.Event.getXY(e);
27266         if(this.overlay){
27267             this.overlay.hide();
27268         }
27269         var newSize;
27270         if(this.orientation == Roo.SplitBar.HORIZONTAL){
27271             newSize = this.dragSpecs.startSize + 
27272                 (this.placement == Roo.SplitBar.LEFT ?
27273                     endPoint[0] - this.dragSpecs.startPoint[0] :
27274                     this.dragSpecs.startPoint[0] - endPoint[0]
27275                 );
27276         }else{
27277             newSize = this.dragSpecs.startSize + 
27278                 (this.placement == Roo.SplitBar.TOP ?
27279                     endPoint[1] - this.dragSpecs.startPoint[1] :
27280                     this.dragSpecs.startPoint[1] - endPoint[1]
27281                 );
27282         }
27283         newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
27284         if(newSize != this.dragSpecs.startSize){
27285             if(this.fireEvent('beforeapply', this, newSize) !== false){
27286                 this.adapter.setElementSize(this, newSize);
27287                 this.fireEvent("moved", this, newSize);
27288                 this.fireEvent("resize", this, newSize);
27289             }
27290         }
27291     },
27292     
27293     /**
27294      * Get the adapter this SplitBar uses
27295      * @return The adapter object
27296      */
27297     getAdapter : function(){
27298         return this.adapter;
27299     },
27300     
27301     /**
27302      * Set the adapter this SplitBar uses
27303      * @param {Object} adapter A SplitBar adapter object
27304      */
27305     setAdapter : function(adapter){
27306         this.adapter = adapter;
27307         this.adapter.init(this);
27308     },
27309     
27310     /**
27311      * Gets the minimum size for the resizing element
27312      * @return {Number} The minimum size
27313      */
27314     getMinimumSize : function(){
27315         return this.minSize;
27316     },
27317     
27318     /**
27319      * Sets the minimum size for the resizing element
27320      * @param {Number} minSize The minimum size
27321      */
27322     setMinimumSize : function(minSize){
27323         this.minSize = minSize;
27324     },
27325     
27326     /**
27327      * Gets the maximum size for the resizing element
27328      * @return {Number} The maximum size
27329      */
27330     getMaximumSize : function(){
27331         return this.maxSize;
27332     },
27333     
27334     /**
27335      * Sets the maximum size for the resizing element
27336      * @param {Number} maxSize The maximum size
27337      */
27338     setMaximumSize : function(maxSize){
27339         this.maxSize = maxSize;
27340     },
27341     
27342     /**
27343      * Sets the initialize size for the resizing element
27344      * @param {Number} size The initial size
27345      */
27346     setCurrentSize : function(size){
27347         var oldAnimate = this.animate;
27348         this.animate = false;
27349         this.adapter.setElementSize(this, size);
27350         this.animate = oldAnimate;
27351     },
27352     
27353     /**
27354      * Destroy this splitbar. 
27355      * @param {Boolean} removeEl True to remove the element
27356      */
27357     destroy : function(removeEl){
27358         if(this.shim){
27359             this.shim.remove();
27360         }
27361         this.dd.unreg();
27362         this.proxy.parentNode.removeChild(this.proxy);
27363         if(removeEl){
27364             this.el.remove();
27365         }
27366     }
27367 });
27368
27369 /**
27370  * @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.
27371  */
27372 Roo.SplitBar.createProxy = function(dir){
27373     var proxy = new Roo.Element(document.createElement("div"));
27374     proxy.unselectable();
27375     var cls = 'x-splitbar-proxy';
27376     proxy.addClass(cls + ' ' + (dir == Roo.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
27377     document.body.appendChild(proxy.dom);
27378     return proxy.dom;
27379 };
27380
27381 /** 
27382  * @class Roo.SplitBar.BasicLayoutAdapter
27383  * Default Adapter. It assumes the splitter and resizing element are not positioned
27384  * elements and only gets/sets the width of the element. Generally used for table based layouts.
27385  */
27386 Roo.SplitBar.BasicLayoutAdapter = function(){
27387 };
27388
27389 Roo.SplitBar.BasicLayoutAdapter.prototype = {
27390     // do nothing for now
27391     init : function(s){
27392     
27393     },
27394     /**
27395      * Called before drag operations to get the current size of the resizing element. 
27396      * @param {Roo.SplitBar} s The SplitBar using this adapter
27397      */
27398      getElementSize : function(s){
27399         if(s.orientation == Roo.SplitBar.HORIZONTAL){
27400             return s.resizingEl.getWidth();
27401         }else{
27402             return s.resizingEl.getHeight();
27403         }
27404     },
27405     
27406     /**
27407      * Called after drag operations to set the size of the resizing element.
27408      * @param {Roo.SplitBar} s The SplitBar using this adapter
27409      * @param {Number} newSize The new size to set
27410      * @param {Function} onComplete A function to be invoked when resizing is complete
27411      */
27412     setElementSize : function(s, newSize, onComplete){
27413         if(s.orientation == Roo.SplitBar.HORIZONTAL){
27414             if(!s.animate){
27415                 s.resizingEl.setWidth(newSize);
27416                 if(onComplete){
27417                     onComplete(s, newSize);
27418                 }
27419             }else{
27420                 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
27421             }
27422         }else{
27423             
27424             if(!s.animate){
27425                 s.resizingEl.setHeight(newSize);
27426                 if(onComplete){
27427                     onComplete(s, newSize);
27428                 }
27429             }else{
27430                 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
27431             }
27432         }
27433     }
27434 };
27435
27436 /** 
27437  *@class Roo.SplitBar.AbsoluteLayoutAdapter
27438  * @extends Roo.SplitBar.BasicLayoutAdapter
27439  * Adapter that  moves the splitter element to align with the resized sizing element. 
27440  * Used with an absolute positioned SplitBar.
27441  * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
27442  * document.body, make sure you assign an id to the body element.
27443  */
27444 Roo.SplitBar.AbsoluteLayoutAdapter = function(container){
27445     this.basic = new Roo.SplitBar.BasicLayoutAdapter();
27446     this.container = Roo.get(container);
27447 };
27448
27449 Roo.SplitBar.AbsoluteLayoutAdapter.prototype = {
27450     init : function(s){
27451         this.basic.init(s);
27452     },
27453     
27454     getElementSize : function(s){
27455         return this.basic.getElementSize(s);
27456     },
27457     
27458     setElementSize : function(s, newSize, onComplete){
27459         this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
27460     },
27461     
27462     moveSplitter : function(s){
27463         var yes = Roo.SplitBar;
27464         switch(s.placement){
27465             case yes.LEFT:
27466                 s.el.setX(s.resizingEl.getRight());
27467                 break;
27468             case yes.RIGHT:
27469                 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
27470                 break;
27471             case yes.TOP:
27472                 s.el.setY(s.resizingEl.getBottom());
27473                 break;
27474             case yes.BOTTOM:
27475                 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
27476                 break;
27477         }
27478     }
27479 };
27480
27481 /**
27482  * Orientation constant - Create a vertical SplitBar
27483  * @static
27484  * @type Number
27485  */
27486 Roo.SplitBar.VERTICAL = 1;
27487
27488 /**
27489  * Orientation constant - Create a horizontal SplitBar
27490  * @static
27491  * @type Number
27492  */
27493 Roo.SplitBar.HORIZONTAL = 2;
27494
27495 /**
27496  * Placement constant - The resizing element is to the left of the splitter element
27497  * @static
27498  * @type Number
27499  */
27500 Roo.SplitBar.LEFT = 1;
27501
27502 /**
27503  * Placement constant - The resizing element is to the right of the splitter element
27504  * @static
27505  * @type Number
27506  */
27507 Roo.SplitBar.RIGHT = 2;
27508
27509 /**
27510  * Placement constant - The resizing element is positioned above the splitter element
27511  * @static
27512  * @type Number
27513  */
27514 Roo.SplitBar.TOP = 3;
27515
27516 /**
27517  * Placement constant - The resizing element is positioned under splitter element
27518  * @static
27519  * @type Number
27520  */
27521 Roo.SplitBar.BOTTOM = 4;
27522 /*
27523  * Based on:
27524  * Ext JS Library 1.1.1
27525  * Copyright(c) 2006-2007, Ext JS, LLC.
27526  *
27527  * Originally Released Under LGPL - original licence link has changed is not relivant.
27528  *
27529  * Fork - LGPL
27530  * <script type="text/javascript">
27531  */
27532
27533 /**
27534  * @class Roo.View
27535  * @extends Roo.util.Observable
27536  * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template. 
27537  * This class also supports single and multi selection modes. <br>
27538  * Create a data model bound view:
27539  <pre><code>
27540  var store = new Roo.data.Store(...);
27541
27542  var view = new Roo.View({
27543     el : "my-element",
27544     tpl : '&lt;div id="{0}"&gt;{2} - {1}&lt;/div&gt;', // auto create template
27545  
27546     singleSelect: true,
27547     selectedClass: "ydataview-selected",
27548     store: store
27549  });
27550
27551  // listen for node click?
27552  view.on("click", function(vw, index, node, e){
27553  alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
27554  });
27555
27556  // load XML data
27557  dataModel.load("foobar.xml");
27558  </code></pre>
27559  For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
27560  * <br><br>
27561  * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
27562  * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
27563  * 
27564  * Note: old style constructor is still suported (container, template, config)
27565  * 
27566  * @constructor
27567  * Create a new View
27568  * @param {Object} config The config object
27569  * 
27570  */
27571 Roo.View = function(config, depreciated_tpl, depreciated_config){
27572     
27573     this.parent = false;
27574     
27575     if (typeof(depreciated_tpl) == 'undefined') {
27576         // new way.. - universal constructor.
27577         Roo.apply(this, config);
27578         this.el  = Roo.get(this.el);
27579     } else {
27580         // old format..
27581         this.el  = Roo.get(config);
27582         this.tpl = depreciated_tpl;
27583         Roo.apply(this, depreciated_config);
27584     }
27585     this.wrapEl  = this.el.wrap().wrap();
27586     ///this.el = this.wrapEla.appendChild(document.createElement("div"));
27587     
27588     
27589     if(typeof(this.tpl) == "string"){
27590         this.tpl = new Roo.Template(this.tpl);
27591     } else {
27592         // support xtype ctors..
27593         this.tpl = new Roo.factory(this.tpl, Roo);
27594     }
27595     
27596     
27597     this.tpl.compile();
27598     
27599     /** @private */
27600     this.addEvents({
27601         /**
27602          * @event beforeclick
27603          * Fires before a click is processed. Returns false to cancel the default action.
27604          * @param {Roo.View} this
27605          * @param {Number} index The index of the target node
27606          * @param {HTMLElement} node The target node
27607          * @param {Roo.EventObject} e The raw event object
27608          */
27609             "beforeclick" : true,
27610         /**
27611          * @event click
27612          * Fires when a template node is clicked.
27613          * @param {Roo.View} this
27614          * @param {Number} index The index of the target node
27615          * @param {HTMLElement} node The target node
27616          * @param {Roo.EventObject} e The raw event object
27617          */
27618             "click" : true,
27619         /**
27620          * @event dblclick
27621          * Fires when a template node is double clicked.
27622          * @param {Roo.View} this
27623          * @param {Number} index The index of the target node
27624          * @param {HTMLElement} node The target node
27625          * @param {Roo.EventObject} e The raw event object
27626          */
27627             "dblclick" : true,
27628         /**
27629          * @event contextmenu
27630          * Fires when a template node is right clicked.
27631          * @param {Roo.View} this
27632          * @param {Number} index The index of the target node
27633          * @param {HTMLElement} node The target node
27634          * @param {Roo.EventObject} e The raw event object
27635          */
27636             "contextmenu" : true,
27637         /**
27638          * @event selectionchange
27639          * Fires when the selected nodes change.
27640          * @param {Roo.View} this
27641          * @param {Array} selections Array of the selected nodes
27642          */
27643             "selectionchange" : true,
27644     
27645         /**
27646          * @event beforeselect
27647          * Fires before a selection is made. If any handlers return false, the selection is cancelled.
27648          * @param {Roo.View} this
27649          * @param {HTMLElement} node The node to be selected
27650          * @param {Array} selections Array of currently selected nodes
27651          */
27652             "beforeselect" : true,
27653         /**
27654          * @event preparedata
27655          * Fires on every row to render, to allow you to change the data.
27656          * @param {Roo.View} this
27657          * @param {Object} data to be rendered (change this)
27658          */
27659           "preparedata" : true
27660           
27661           
27662         });
27663
27664
27665
27666     this.el.on({
27667         "click": this.onClick,
27668         "dblclick": this.onDblClick,
27669         "contextmenu": this.onContextMenu,
27670         scope:this
27671     });
27672
27673     this.selections = [];
27674     this.nodes = [];
27675     this.cmp = new Roo.CompositeElementLite([]);
27676     if(this.store){
27677         this.store = Roo.factory(this.store, Roo.data);
27678         this.setStore(this.store, true);
27679     }
27680     
27681     if ( this.footer && this.footer.xtype) {
27682            
27683          var fctr = this.wrapEl.appendChild(document.createElement("div"));
27684         
27685         this.footer.dataSource = this.store;
27686         this.footer.container = fctr;
27687         this.footer = Roo.factory(this.footer, Roo);
27688         fctr.insertFirst(this.el);
27689         
27690         // this is a bit insane - as the paging toolbar seems to detach the el..
27691 //        dom.parentNode.parentNode.parentNode
27692          // they get detached?
27693     }
27694     
27695     
27696     Roo.View.superclass.constructor.call(this);
27697     
27698     
27699 };
27700
27701 Roo.extend(Roo.View, Roo.util.Observable, {
27702     
27703      /**
27704      * @cfg {Roo.data.Store} store Data store to load data from.
27705      */
27706     store : false,
27707     
27708     /**
27709      * @cfg {String|Roo.Element} el The container element.
27710      */
27711     el : '',
27712     
27713     /**
27714      * @cfg {String|Roo.Template} tpl The template used by this View 
27715      */
27716     tpl : false,
27717     /**
27718      * @cfg {String} dataName the named area of the template to use as the data area
27719      *                          Works with domtemplates roo-name="name"
27720      */
27721     dataName: false,
27722     /**
27723      * @cfg {String} selectedClass The css class to add to selected nodes
27724      */
27725     selectedClass : "x-view-selected",
27726      /**
27727      * @cfg {String} emptyText The empty text to show when nothing is loaded.
27728      */
27729     emptyText : "",
27730     
27731     /**
27732      * @cfg {String} text to display on mask (default Loading)
27733      */
27734     mask : false,
27735     /**
27736      * @cfg {Boolean} multiSelect Allow multiple selection
27737      */
27738     multiSelect : false,
27739     /**
27740      * @cfg {Boolean} singleSelect Allow single selection
27741      */
27742     singleSelect:  false,
27743     
27744     /**
27745      * @cfg {Boolean} toggleSelect - selecting 
27746      */
27747     toggleSelect : false,
27748     
27749     /**
27750      * @cfg {Boolean} tickable - selecting 
27751      */
27752     tickable : false,
27753     
27754     /**
27755      * Returns the element this view is bound to.
27756      * @return {Roo.Element}
27757      */
27758     getEl : function(){
27759         return this.wrapEl;
27760     },
27761     
27762     
27763
27764     /**
27765      * Refreshes the view. - called by datachanged on the store. - do not call directly.
27766      */
27767     refresh : function(){
27768         //Roo.log('refresh');
27769         var t = this.tpl;
27770         
27771         // if we are using something like 'domtemplate', then
27772         // the what gets used is:
27773         // t.applySubtemplate(NAME, data, wrapping data..)
27774         // the outer template then get' applied with
27775         //     the store 'extra data'
27776         // and the body get's added to the
27777         //      roo-name="data" node?
27778         //      <span class='roo-tpl-{name}'></span> ?????
27779         
27780         
27781         
27782         this.clearSelections();
27783         this.el.update("");
27784         var html = [];
27785         var records = this.store.getRange();
27786         if(records.length < 1) {
27787             
27788             // is this valid??  = should it render a template??
27789             
27790             this.el.update(this.emptyText);
27791             return;
27792         }
27793         var el = this.el;
27794         if (this.dataName) {
27795             this.el.update(t.apply(this.store.meta)); //????
27796             el = this.el.child('.roo-tpl-' + this.dataName);
27797         }
27798         
27799         for(var i = 0, len = records.length; i < len; i++){
27800             var data = this.prepareData(records[i].data, i, records[i]);
27801             this.fireEvent("preparedata", this, data, i, records[i]);
27802             
27803             var d = Roo.apply({}, data);
27804             
27805             if(this.tickable){
27806                 Roo.apply(d, {'roo-id' : Roo.id()});
27807                 
27808                 var _this = this;
27809             
27810                 Roo.each(this.parent.item, function(item){
27811                     if(item[_this.parent.valueField] != data[_this.parent.valueField]){
27812                         return;
27813                     }
27814                     Roo.apply(d, {'roo-data-checked' : 'checked'});
27815                 });
27816             }
27817             
27818             html[html.length] = Roo.util.Format.trim(
27819                 this.dataName ?
27820                     t.applySubtemplate(this.dataName, d, this.store.meta) :
27821                     t.apply(d)
27822             );
27823         }
27824         
27825         
27826         
27827         el.update(html.join(""));
27828         this.nodes = el.dom.childNodes;
27829         this.updateIndexes(0);
27830     },
27831     
27832
27833     /**
27834      * Function to override to reformat the data that is sent to
27835      * the template for each node.
27836      * DEPRICATED - use the preparedata event handler.
27837      * @param {Array/Object} data The raw data (array of colData for a data model bound view or
27838      * a JSON object for an UpdateManager bound view).
27839      */
27840     prepareData : function(data, index, record)
27841     {
27842         this.fireEvent("preparedata", this, data, index, record);
27843         return data;
27844     },
27845
27846     onUpdate : function(ds, record){
27847         // Roo.log('on update');   
27848         this.clearSelections();
27849         var index = this.store.indexOf(record);
27850         var n = this.nodes[index];
27851         this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
27852         n.parentNode.removeChild(n);
27853         this.updateIndexes(index, index);
27854     },
27855
27856     
27857     
27858 // --------- FIXME     
27859     onAdd : function(ds, records, index)
27860     {
27861         //Roo.log(['on Add', ds, records, index] );        
27862         this.clearSelections();
27863         if(this.nodes.length == 0){
27864             this.refresh();
27865             return;
27866         }
27867         var n = this.nodes[index];
27868         for(var i = 0, len = records.length; i < len; i++){
27869             var d = this.prepareData(records[i].data, i, records[i]);
27870             if(n){
27871                 this.tpl.insertBefore(n, d);
27872             }else{
27873                 
27874                 this.tpl.append(this.el, d);
27875             }
27876         }
27877         this.updateIndexes(index);
27878     },
27879
27880     onRemove : function(ds, record, index){
27881        // Roo.log('onRemove');
27882         this.clearSelections();
27883         var el = this.dataName  ?
27884             this.el.child('.roo-tpl-' + this.dataName) :
27885             this.el; 
27886         
27887         el.dom.removeChild(this.nodes[index]);
27888         this.updateIndexes(index);
27889     },
27890
27891     /**
27892      * Refresh an individual node.
27893      * @param {Number} index
27894      */
27895     refreshNode : function(index){
27896         this.onUpdate(this.store, this.store.getAt(index));
27897     },
27898
27899     updateIndexes : function(startIndex, endIndex){
27900         var ns = this.nodes;
27901         startIndex = startIndex || 0;
27902         endIndex = endIndex || ns.length - 1;
27903         for(var i = startIndex; i <= endIndex; i++){
27904             ns[i].nodeIndex = i;
27905         }
27906     },
27907
27908     /**
27909      * Changes the data store this view uses and refresh the view.
27910      * @param {Store} store
27911      */
27912     setStore : function(store, initial){
27913         if(!initial && this.store){
27914             this.store.un("datachanged", this.refresh);
27915             this.store.un("add", this.onAdd);
27916             this.store.un("remove", this.onRemove);
27917             this.store.un("update", this.onUpdate);
27918             this.store.un("clear", this.refresh);
27919             this.store.un("beforeload", this.onBeforeLoad);
27920             this.store.un("load", this.onLoad);
27921             this.store.un("loadexception", this.onLoad);
27922         }
27923         if(store){
27924           
27925             store.on("datachanged", this.refresh, this);
27926             store.on("add", this.onAdd, this);
27927             store.on("remove", this.onRemove, this);
27928             store.on("update", this.onUpdate, this);
27929             store.on("clear", this.refresh, this);
27930             store.on("beforeload", this.onBeforeLoad, this);
27931             store.on("load", this.onLoad, this);
27932             store.on("loadexception", this.onLoad, this);
27933         }
27934         
27935         if(store){
27936             this.refresh();
27937         }
27938     },
27939     /**
27940      * onbeforeLoad - masks the loading area.
27941      *
27942      */
27943     onBeforeLoad : function(store,opts)
27944     {
27945          //Roo.log('onBeforeLoad');   
27946         if (!opts.add) {
27947             this.el.update("");
27948         }
27949         this.el.mask(this.mask ? this.mask : "Loading" ); 
27950     },
27951     onLoad : function ()
27952     {
27953         this.el.unmask();
27954     },
27955     
27956
27957     /**
27958      * Returns the template node the passed child belongs to or null if it doesn't belong to one.
27959      * @param {HTMLElement} node
27960      * @return {HTMLElement} The template node
27961      */
27962     findItemFromChild : function(node){
27963         var el = this.dataName  ?
27964             this.el.child('.roo-tpl-' + this.dataName,true) :
27965             this.el.dom; 
27966         
27967         if(!node || node.parentNode == el){
27968                     return node;
27969             }
27970             var p = node.parentNode;
27971             while(p && p != el){
27972             if(p.parentNode == el){
27973                 return p;
27974             }
27975             p = p.parentNode;
27976         }
27977             return null;
27978     },
27979
27980     /** @ignore */
27981     onClick : function(e){
27982         var item = this.findItemFromChild(e.getTarget());
27983         if(item){
27984             var index = this.indexOf(item);
27985             if(this.onItemClick(item, index, e) !== false){
27986                 this.fireEvent("click", this, index, item, e);
27987             }
27988         }else{
27989             this.clearSelections();
27990         }
27991     },
27992
27993     /** @ignore */
27994     onContextMenu : function(e){
27995         var item = this.findItemFromChild(e.getTarget());
27996         if(item){
27997             this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
27998         }
27999     },
28000
28001     /** @ignore */
28002     onDblClick : function(e){
28003         var item = this.findItemFromChild(e.getTarget());
28004         if(item){
28005             this.fireEvent("dblclick", this, this.indexOf(item), item, e);
28006         }
28007     },
28008
28009     onItemClick : function(item, index, e)
28010     {
28011         if(this.fireEvent("beforeclick", this, index, item, e) === false){
28012             return false;
28013         }
28014         if (this.toggleSelect) {
28015             var m = this.isSelected(item) ? 'unselect' : 'select';
28016             //Roo.log(m);
28017             var _t = this;
28018             _t[m](item, true, false);
28019             return true;
28020         }
28021         if(this.multiSelect || this.singleSelect){
28022             if(this.multiSelect && e.shiftKey && this.lastSelection){
28023                 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
28024             }else{
28025                 this.select(item, this.multiSelect && e.ctrlKey);
28026                 this.lastSelection = item;
28027             }
28028             
28029             if(!this.tickable){
28030                 e.preventDefault();
28031             }
28032             
28033         }
28034         return true;
28035     },
28036
28037     /**
28038      * Get the number of selected nodes.
28039      * @return {Number}
28040      */
28041     getSelectionCount : function(){
28042         return this.selections.length;
28043     },
28044
28045     /**
28046      * Get the currently selected nodes.
28047      * @return {Array} An array of HTMLElements
28048      */
28049     getSelectedNodes : function(){
28050         return this.selections;
28051     },
28052
28053     /**
28054      * Get the indexes of the selected nodes.
28055      * @return {Array}
28056      */
28057     getSelectedIndexes : function(){
28058         var indexes = [], s = this.selections;
28059         for(var i = 0, len = s.length; i < len; i++){
28060             indexes.push(s[i].nodeIndex);
28061         }
28062         return indexes;
28063     },
28064
28065     /**
28066      * Clear all selections
28067      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
28068      */
28069     clearSelections : function(suppressEvent){
28070         if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
28071             this.cmp.elements = this.selections;
28072             this.cmp.removeClass(this.selectedClass);
28073             this.selections = [];
28074             if(!suppressEvent){
28075                 this.fireEvent("selectionchange", this, this.selections);
28076             }
28077         }
28078     },
28079
28080     /**
28081      * Returns true if the passed node is selected
28082      * @param {HTMLElement/Number} node The node or node index
28083      * @return {Boolean}
28084      */
28085     isSelected : function(node){
28086         var s = this.selections;
28087         if(s.length < 1){
28088             return false;
28089         }
28090         node = this.getNode(node);
28091         return s.indexOf(node) !== -1;
28092     },
28093
28094     /**
28095      * Selects nodes.
28096      * @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
28097      * @param {Boolean} keepExisting (optional) true to keep existing selections
28098      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
28099      */
28100     select : function(nodeInfo, keepExisting, suppressEvent){
28101         if(nodeInfo instanceof Array){
28102             if(!keepExisting){
28103                 this.clearSelections(true);
28104             }
28105             for(var i = 0, len = nodeInfo.length; i < len; i++){
28106                 this.select(nodeInfo[i], true, true);
28107             }
28108             return;
28109         } 
28110         var node = this.getNode(nodeInfo);
28111         if(!node || this.isSelected(node)){
28112             return; // already selected.
28113         }
28114         if(!keepExisting){
28115             this.clearSelections(true);
28116         }
28117         
28118         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
28119             Roo.fly(node).addClass(this.selectedClass);
28120             this.selections.push(node);
28121             if(!suppressEvent){
28122                 this.fireEvent("selectionchange", this, this.selections);
28123             }
28124         }
28125         
28126         
28127     },
28128       /**
28129      * Unselects nodes.
28130      * @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
28131      * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
28132      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
28133      */
28134     unselect : function(nodeInfo, keepExisting, suppressEvent)
28135     {
28136         if(nodeInfo instanceof Array){
28137             Roo.each(this.selections, function(s) {
28138                 this.unselect(s, nodeInfo);
28139             }, this);
28140             return;
28141         }
28142         var node = this.getNode(nodeInfo);
28143         if(!node || !this.isSelected(node)){
28144             //Roo.log("not selected");
28145             return; // not selected.
28146         }
28147         // fireevent???
28148         var ns = [];
28149         Roo.each(this.selections, function(s) {
28150             if (s == node ) {
28151                 Roo.fly(node).removeClass(this.selectedClass);
28152
28153                 return;
28154             }
28155             ns.push(s);
28156         },this);
28157         
28158         this.selections= ns;
28159         this.fireEvent("selectionchange", this, this.selections);
28160     },
28161
28162     /**
28163      * Gets a template node.
28164      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
28165      * @return {HTMLElement} The node or null if it wasn't found
28166      */
28167     getNode : function(nodeInfo){
28168         if(typeof nodeInfo == "string"){
28169             return document.getElementById(nodeInfo);
28170         }else if(typeof nodeInfo == "number"){
28171             return this.nodes[nodeInfo];
28172         }
28173         return nodeInfo;
28174     },
28175
28176     /**
28177      * Gets a range template nodes.
28178      * @param {Number} startIndex
28179      * @param {Number} endIndex
28180      * @return {Array} An array of nodes
28181      */
28182     getNodes : function(start, end){
28183         var ns = this.nodes;
28184         start = start || 0;
28185         end = typeof end == "undefined" ? ns.length - 1 : end;
28186         var nodes = [];
28187         if(start <= end){
28188             for(var i = start; i <= end; i++){
28189                 nodes.push(ns[i]);
28190             }
28191         } else{
28192             for(var i = start; i >= end; i--){
28193                 nodes.push(ns[i]);
28194             }
28195         }
28196         return nodes;
28197     },
28198
28199     /**
28200      * Finds the index of the passed node
28201      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
28202      * @return {Number} The index of the node or -1
28203      */
28204     indexOf : function(node){
28205         node = this.getNode(node);
28206         if(typeof node.nodeIndex == "number"){
28207             return node.nodeIndex;
28208         }
28209         var ns = this.nodes;
28210         for(var i = 0, len = ns.length; i < len; i++){
28211             if(ns[i] == node){
28212                 return i;
28213             }
28214         }
28215         return -1;
28216     }
28217 });
28218 /*
28219  * Based on:
28220  * Ext JS Library 1.1.1
28221  * Copyright(c) 2006-2007, Ext JS, LLC.
28222  *
28223  * Originally Released Under LGPL - original licence link has changed is not relivant.
28224  *
28225  * Fork - LGPL
28226  * <script type="text/javascript">
28227  */
28228
28229 /**
28230  * @class Roo.JsonView
28231  * @extends Roo.View
28232  * Shortcut class to create a JSON + {@link Roo.UpdateManager} template view. Usage:
28233 <pre><code>
28234 var view = new Roo.JsonView({
28235     container: "my-element",
28236     tpl: '&lt;div id="{id}"&gt;{foo} - {bar}&lt;/div&gt;', // auto create template
28237     multiSelect: true, 
28238     jsonRoot: "data" 
28239 });
28240
28241 // listen for node click?
28242 view.on("click", function(vw, index, node, e){
28243     alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
28244 });
28245
28246 // direct load of JSON data
28247 view.load("foobar.php");
28248
28249 // Example from my blog list
28250 var tpl = new Roo.Template(
28251     '&lt;div class="entry"&gt;' +
28252     '&lt;a class="entry-title" href="{link}"&gt;{title}&lt;/a&gt;' +
28253     "&lt;h4&gt;{date} by {author} | {comments} Comments&lt;/h4&gt;{description}" +
28254     "&lt;/div&gt;&lt;hr /&gt;"
28255 );
28256
28257 var moreView = new Roo.JsonView({
28258     container :  "entry-list", 
28259     template : tpl,
28260     jsonRoot: "posts"
28261 });
28262 moreView.on("beforerender", this.sortEntries, this);
28263 moreView.load({
28264     url: "/blog/get-posts.php",
28265     params: "allposts=true",
28266     text: "Loading Blog Entries..."
28267 });
28268 </code></pre>
28269
28270 * Note: old code is supported with arguments : (container, template, config)
28271
28272
28273  * @constructor
28274  * Create a new JsonView
28275  * 
28276  * @param {Object} config The config object
28277  * 
28278  */
28279 Roo.JsonView = function(config, depreciated_tpl, depreciated_config){
28280     
28281     
28282     Roo.JsonView.superclass.constructor.call(this, config, depreciated_tpl, depreciated_config);
28283
28284     var um = this.el.getUpdateManager();
28285     um.setRenderer(this);
28286     um.on("update", this.onLoad, this);
28287     um.on("failure", this.onLoadException, this);
28288
28289     /**
28290      * @event beforerender
28291      * Fires before rendering of the downloaded JSON data.
28292      * @param {Roo.JsonView} this
28293      * @param {Object} data The JSON data loaded
28294      */
28295     /**
28296      * @event load
28297      * Fires when data is loaded.
28298      * @param {Roo.JsonView} this
28299      * @param {Object} data The JSON data loaded
28300      * @param {Object} response The raw Connect response object
28301      */
28302     /**
28303      * @event loadexception
28304      * Fires when loading fails.
28305      * @param {Roo.JsonView} this
28306      * @param {Object} response The raw Connect response object
28307      */
28308     this.addEvents({
28309         'beforerender' : true,
28310         'load' : true,
28311         'loadexception' : true
28312     });
28313 };
28314 Roo.extend(Roo.JsonView, Roo.View, {
28315     /**
28316      * @type {String} The root property in the loaded JSON object that contains the data
28317      */
28318     jsonRoot : "",
28319
28320     /**
28321      * Refreshes the view.
28322      */
28323     refresh : function(){
28324         this.clearSelections();
28325         this.el.update("");
28326         var html = [];
28327         var o = this.jsonData;
28328         if(o && o.length > 0){
28329             for(var i = 0, len = o.length; i < len; i++){
28330                 var data = this.prepareData(o[i], i, o);
28331                 html[html.length] = this.tpl.apply(data);
28332             }
28333         }else{
28334             html.push(this.emptyText);
28335         }
28336         this.el.update(html.join(""));
28337         this.nodes = this.el.dom.childNodes;
28338         this.updateIndexes(0);
28339     },
28340
28341     /**
28342      * 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.
28343      * @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:
28344      <pre><code>
28345      view.load({
28346          url: "your-url.php",
28347          params: {param1: "foo", param2: "bar"}, // or a URL encoded string
28348          callback: yourFunction,
28349          scope: yourObject, //(optional scope)
28350          discardUrl: false,
28351          nocache: false,
28352          text: "Loading...",
28353          timeout: 30,
28354          scripts: false
28355      });
28356      </code></pre>
28357      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
28358      * 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.
28359      * @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}
28360      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess)
28361      * @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.
28362      */
28363     load : function(){
28364         var um = this.el.getUpdateManager();
28365         um.update.apply(um, arguments);
28366     },
28367
28368     // note - render is a standard framework call...
28369     // using it for the response is really flaky... - it's called by UpdateManager normally, except when called by the XComponent/addXtype.
28370     render : function(el, response){
28371         
28372         this.clearSelections();
28373         this.el.update("");
28374         var o;
28375         try{
28376             if (response != '') {
28377                 o = Roo.util.JSON.decode(response.responseText);
28378                 if(this.jsonRoot){
28379                     
28380                     o = o[this.jsonRoot];
28381                 }
28382             }
28383         } catch(e){
28384         }
28385         /**
28386          * The current JSON data or null
28387          */
28388         this.jsonData = o;
28389         this.beforeRender();
28390         this.refresh();
28391     },
28392
28393 /**
28394  * Get the number of records in the current JSON dataset
28395  * @return {Number}
28396  */
28397     getCount : function(){
28398         return this.jsonData ? this.jsonData.length : 0;
28399     },
28400
28401 /**
28402  * Returns the JSON object for the specified node(s)
28403  * @param {HTMLElement/Array} node The node or an array of nodes
28404  * @return {Object/Array} If you pass in an array, you get an array back, otherwise
28405  * you get the JSON object for the node
28406  */
28407     getNodeData : function(node){
28408         if(node instanceof Array){
28409             var data = [];
28410             for(var i = 0, len = node.length; i < len; i++){
28411                 data.push(this.getNodeData(node[i]));
28412             }
28413             return data;
28414         }
28415         return this.jsonData[this.indexOf(node)] || null;
28416     },
28417
28418     beforeRender : function(){
28419         this.snapshot = this.jsonData;
28420         if(this.sortInfo){
28421             this.sort.apply(this, this.sortInfo);
28422         }
28423         this.fireEvent("beforerender", this, this.jsonData);
28424     },
28425
28426     onLoad : function(el, o){
28427         this.fireEvent("load", this, this.jsonData, o);
28428     },
28429
28430     onLoadException : function(el, o){
28431         this.fireEvent("loadexception", this, o);
28432     },
28433
28434 /**
28435  * Filter the data by a specific property.
28436  * @param {String} property A property on your JSON objects
28437  * @param {String/RegExp} value Either string that the property values
28438  * should start with, or a RegExp to test against the property
28439  */
28440     filter : function(property, value){
28441         if(this.jsonData){
28442             var data = [];
28443             var ss = this.snapshot;
28444             if(typeof value == "string"){
28445                 var vlen = value.length;
28446                 if(vlen == 0){
28447                     this.clearFilter();
28448                     return;
28449                 }
28450                 value = value.toLowerCase();
28451                 for(var i = 0, len = ss.length; i < len; i++){
28452                     var o = ss[i];
28453                     if(o[property].substr(0, vlen).toLowerCase() == value){
28454                         data.push(o);
28455                     }
28456                 }
28457             } else if(value.exec){ // regex?
28458                 for(var i = 0, len = ss.length; i < len; i++){
28459                     var o = ss[i];
28460                     if(value.test(o[property])){
28461                         data.push(o);
28462                     }
28463                 }
28464             } else{
28465                 return;
28466             }
28467             this.jsonData = data;
28468             this.refresh();
28469         }
28470     },
28471
28472 /**
28473  * Filter by a function. The passed function will be called with each
28474  * object in the current dataset. If the function returns true the value is kept,
28475  * otherwise it is filtered.
28476  * @param {Function} fn
28477  * @param {Object} scope (optional) The scope of the function (defaults to this JsonView)
28478  */
28479     filterBy : function(fn, scope){
28480         if(this.jsonData){
28481             var data = [];
28482             var ss = this.snapshot;
28483             for(var i = 0, len = ss.length; i < len; i++){
28484                 var o = ss[i];
28485                 if(fn.call(scope || this, o)){
28486                     data.push(o);
28487                 }
28488             }
28489             this.jsonData = data;
28490             this.refresh();
28491         }
28492     },
28493
28494 /**
28495  * Clears the current filter.
28496  */
28497     clearFilter : function(){
28498         if(this.snapshot && this.jsonData != this.snapshot){
28499             this.jsonData = this.snapshot;
28500             this.refresh();
28501         }
28502     },
28503
28504
28505 /**
28506  * Sorts the data for this view and refreshes it.
28507  * @param {String} property A property on your JSON objects to sort on
28508  * @param {String} direction (optional) "desc" or "asc" (defaults to "asc")
28509  * @param {Function} sortType (optional) A function to call to convert the data to a sortable value.
28510  */
28511     sort : function(property, dir, sortType){
28512         this.sortInfo = Array.prototype.slice.call(arguments, 0);
28513         if(this.jsonData){
28514             var p = property;
28515             var dsc = dir && dir.toLowerCase() == "desc";
28516             var f = function(o1, o2){
28517                 var v1 = sortType ? sortType(o1[p]) : o1[p];
28518                 var v2 = sortType ? sortType(o2[p]) : o2[p];
28519                 ;
28520                 if(v1 < v2){
28521                     return dsc ? +1 : -1;
28522                 } else if(v1 > v2){
28523                     return dsc ? -1 : +1;
28524                 } else{
28525                     return 0;
28526                 }
28527             };
28528             this.jsonData.sort(f);
28529             this.refresh();
28530             if(this.jsonData != this.snapshot){
28531                 this.snapshot.sort(f);
28532             }
28533         }
28534     }
28535 });/*
28536  * Based on:
28537  * Ext JS Library 1.1.1
28538  * Copyright(c) 2006-2007, Ext JS, LLC.
28539  *
28540  * Originally Released Under LGPL - original licence link has changed is not relivant.
28541  *
28542  * Fork - LGPL
28543  * <script type="text/javascript">
28544  */
28545  
28546
28547 /**
28548  * @class Roo.ColorPalette
28549  * @extends Roo.Component
28550  * Simple color palette class for choosing colors.  The palette can be rendered to any container.<br />
28551  * Here's an example of typical usage:
28552  * <pre><code>
28553 var cp = new Roo.ColorPalette({value:'993300'});  // initial selected color
28554 cp.render('my-div');
28555
28556 cp.on('select', function(palette, selColor){
28557     // do something with selColor
28558 });
28559 </code></pre>
28560  * @constructor
28561  * Create a new ColorPalette
28562  * @param {Object} config The config object
28563  */
28564 Roo.ColorPalette = function(config){
28565     Roo.ColorPalette.superclass.constructor.call(this, config);
28566     this.addEvents({
28567         /**
28568              * @event select
28569              * Fires when a color is selected
28570              * @param {ColorPalette} this
28571              * @param {String} color The 6-digit color hex code (without the # symbol)
28572              */
28573         select: true
28574     });
28575
28576     if(this.handler){
28577         this.on("select", this.handler, this.scope, true);
28578     }
28579 };
28580 Roo.extend(Roo.ColorPalette, Roo.Component, {
28581     /**
28582      * @cfg {String} itemCls
28583      * The CSS class to apply to the containing element (defaults to "x-color-palette")
28584      */
28585     itemCls : "x-color-palette",
28586     /**
28587      * @cfg {String} value
28588      * The initial color to highlight (should be a valid 6-digit color hex code without the # symbol).  Note that
28589      * the hex codes are case-sensitive.
28590      */
28591     value : null,
28592     clickEvent:'click',
28593     // private
28594     ctype: "Roo.ColorPalette",
28595
28596     /**
28597      * @cfg {Boolean} allowReselect If set to true then reselecting a color that is already selected fires the selection event
28598      */
28599     allowReselect : false,
28600
28601     /**
28602      * <p>An array of 6-digit color hex code strings (without the # symbol).  This array can contain any number
28603      * of colors, and each hex code should be unique.  The width of the palette is controlled via CSS by adjusting
28604      * the width property of the 'x-color-palette' class (or assigning a custom class), so you can balance the number
28605      * of colors with the width setting until the box is symmetrical.</p>
28606      * <p>You can override individual colors if needed:</p>
28607      * <pre><code>
28608 var cp = new Roo.ColorPalette();
28609 cp.colors[0] = "FF0000";  // change the first box to red
28610 </code></pre>
28611
28612 Or you can provide a custom array of your own for complete control:
28613 <pre><code>
28614 var cp = new Roo.ColorPalette();
28615 cp.colors = ["000000", "993300", "333300"];
28616 </code></pre>
28617      * @type Array
28618      */
28619     colors : [
28620         "000000", "993300", "333300", "003300", "003366", "000080", "333399", "333333",
28621         "800000", "FF6600", "808000", "008000", "008080", "0000FF", "666699", "808080",
28622         "FF0000", "FF9900", "99CC00", "339966", "33CCCC", "3366FF", "800080", "969696",
28623         "FF00FF", "FFCC00", "FFFF00", "00FF00", "00FFFF", "00CCFF", "993366", "C0C0C0",
28624         "FF99CC", "FFCC99", "FFFF99", "CCFFCC", "CCFFFF", "99CCFF", "CC99FF", "FFFFFF"
28625     ],
28626
28627     // private
28628     onRender : function(container, position){
28629         var t = new Roo.MasterTemplate(
28630             '<tpl><a href="#" class="color-{0}" hidefocus="on"><em><span style="background:#{0}" unselectable="on">&#160;</span></em></a></tpl>'
28631         );
28632         var c = this.colors;
28633         for(var i = 0, len = c.length; i < len; i++){
28634             t.add([c[i]]);
28635         }
28636         var el = document.createElement("div");
28637         el.className = this.itemCls;
28638         t.overwrite(el);
28639         container.dom.insertBefore(el, position);
28640         this.el = Roo.get(el);
28641         this.el.on(this.clickEvent, this.handleClick,  this, {delegate: "a"});
28642         if(this.clickEvent != 'click'){
28643             this.el.on('click', Roo.emptyFn,  this, {delegate: "a", preventDefault:true});
28644         }
28645     },
28646
28647     // private
28648     afterRender : function(){
28649         Roo.ColorPalette.superclass.afterRender.call(this);
28650         if(this.value){
28651             var s = this.value;
28652             this.value = null;
28653             this.select(s);
28654         }
28655     },
28656
28657     // private
28658     handleClick : function(e, t){
28659         e.preventDefault();
28660         if(!this.disabled){
28661             var c = t.className.match(/(?:^|\s)color-(.{6})(?:\s|$)/)[1];
28662             this.select(c.toUpperCase());
28663         }
28664     },
28665
28666     /**
28667      * Selects the specified color in the palette (fires the select event)
28668      * @param {String} color A valid 6-digit color hex code (# will be stripped if included)
28669      */
28670     select : function(color){
28671         color = color.replace("#", "");
28672         if(color != this.value || this.allowReselect){
28673             var el = this.el;
28674             if(this.value){
28675                 el.child("a.color-"+this.value).removeClass("x-color-palette-sel");
28676             }
28677             el.child("a.color-"+color).addClass("x-color-palette-sel");
28678             this.value = color;
28679             this.fireEvent("select", this, color);
28680         }
28681     }
28682 });/*
28683  * Based on:
28684  * Ext JS Library 1.1.1
28685  * Copyright(c) 2006-2007, Ext JS, LLC.
28686  *
28687  * Originally Released Under LGPL - original licence link has changed is not relivant.
28688  *
28689  * Fork - LGPL
28690  * <script type="text/javascript">
28691  */
28692  
28693 /**
28694  * @class Roo.DatePicker
28695  * @extends Roo.Component
28696  * Simple date picker class.
28697  * @constructor
28698  * Create a new DatePicker
28699  * @param {Object} config The config object
28700  */
28701 Roo.DatePicker = function(config){
28702     Roo.DatePicker.superclass.constructor.call(this, config);
28703
28704     this.value = config && config.value ?
28705                  config.value.clearTime() : new Date().clearTime();
28706
28707     this.addEvents({
28708         /**
28709              * @event select
28710              * Fires when a date is selected
28711              * @param {DatePicker} this
28712              * @param {Date} date The selected date
28713              */
28714         'select': true,
28715         /**
28716              * @event monthchange
28717              * Fires when the displayed month changes 
28718              * @param {DatePicker} this
28719              * @param {Date} date The selected month
28720              */
28721         'monthchange': true
28722     });
28723
28724     if(this.handler){
28725         this.on("select", this.handler,  this.scope || this);
28726     }
28727     // build the disabledDatesRE
28728     if(!this.disabledDatesRE && this.disabledDates){
28729         var dd = this.disabledDates;
28730         var re = "(?:";
28731         for(var i = 0; i < dd.length; i++){
28732             re += dd[i];
28733             if(i != dd.length-1) {
28734                 re += "|";
28735             }
28736         }
28737         this.disabledDatesRE = new RegExp(re + ")");
28738     }
28739 };
28740
28741 Roo.extend(Roo.DatePicker, Roo.Component, {
28742     /**
28743      * @cfg {String} todayText
28744      * The text to display on the button that selects the current date (defaults to "Today")
28745      */
28746     todayText : "Today",
28747     /**
28748      * @cfg {String} okText
28749      * The text to display on the ok button
28750      */
28751     okText : "&#160;OK&#160;", // &#160; to give the user extra clicking room
28752     /**
28753      * @cfg {String} cancelText
28754      * The text to display on the cancel button
28755      */
28756     cancelText : "Cancel",
28757     /**
28758      * @cfg {String} todayTip
28759      * The tooltip to display for the button that selects the current date (defaults to "{current date} (Spacebar)")
28760      */
28761     todayTip : "{0} (Spacebar)",
28762     /**
28763      * @cfg {Date} minDate
28764      * Minimum allowable date (JavaScript date object, defaults to null)
28765      */
28766     minDate : null,
28767     /**
28768      * @cfg {Date} maxDate
28769      * Maximum allowable date (JavaScript date object, defaults to null)
28770      */
28771     maxDate : null,
28772     /**
28773      * @cfg {String} minText
28774      * The error text to display if the minDate validation fails (defaults to "This date is before the minimum date")
28775      */
28776     minText : "This date is before the minimum date",
28777     /**
28778      * @cfg {String} maxText
28779      * The error text to display if the maxDate validation fails (defaults to "This date is after the maximum date")
28780      */
28781     maxText : "This date is after the maximum date",
28782     /**
28783      * @cfg {String} format
28784      * The default date format string which can be overriden for localization support.  The format must be
28785      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
28786      */
28787     format : "m/d/y",
28788     /**
28789      * @cfg {Array} disabledDays
28790      * An array of days to disable, 0-based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
28791      */
28792     disabledDays : null,
28793     /**
28794      * @cfg {String} disabledDaysText
28795      * The tooltip to display when the date falls on a disabled day (defaults to "")
28796      */
28797     disabledDaysText : "",
28798     /**
28799      * @cfg {RegExp} disabledDatesRE
28800      * JavaScript regular expression used to disable a pattern of dates (defaults to null)
28801      */
28802     disabledDatesRE : null,
28803     /**
28804      * @cfg {String} disabledDatesText
28805      * The tooltip text to display when the date falls on a disabled date (defaults to "")
28806      */
28807     disabledDatesText : "",
28808     /**
28809      * @cfg {Boolean} constrainToViewport
28810      * True to constrain the date picker to the viewport (defaults to true)
28811      */
28812     constrainToViewport : true,
28813     /**
28814      * @cfg {Array} monthNames
28815      * An array of textual month names which can be overriden for localization support (defaults to Date.monthNames)
28816      */
28817     monthNames : Date.monthNames,
28818     /**
28819      * @cfg {Array} dayNames
28820      * An array of textual day names which can be overriden for localization support (defaults to Date.dayNames)
28821      */
28822     dayNames : Date.dayNames,
28823     /**
28824      * @cfg {String} nextText
28825      * The next month navigation button tooltip (defaults to 'Next Month (Control+Right)')
28826      */
28827     nextText: 'Next Month (Control+Right)',
28828     /**
28829      * @cfg {String} prevText
28830      * The previous month navigation button tooltip (defaults to 'Previous Month (Control+Left)')
28831      */
28832     prevText: 'Previous Month (Control+Left)',
28833     /**
28834      * @cfg {String} monthYearText
28835      * The header month selector tooltip (defaults to 'Choose a month (Control+Up/Down to move years)')
28836      */
28837     monthYearText: 'Choose a month (Control+Up/Down to move years)',
28838     /**
28839      * @cfg {Number} startDay
28840      * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
28841      */
28842     startDay : 0,
28843     /**
28844      * @cfg {Bool} showClear
28845      * Show a clear button (usefull for date form elements that can be blank.)
28846      */
28847     
28848     showClear: false,
28849     
28850     /**
28851      * Sets the value of the date field
28852      * @param {Date} value The date to set
28853      */
28854     setValue : function(value){
28855         var old = this.value;
28856         
28857         if (typeof(value) == 'string') {
28858          
28859             value = Date.parseDate(value, this.format);
28860         }
28861         if (!value) {
28862             value = new Date();
28863         }
28864         
28865         this.value = value.clearTime(true);
28866         if(this.el){
28867             this.update(this.value);
28868         }
28869     },
28870
28871     /**
28872      * Gets the current selected value of the date field
28873      * @return {Date} The selected date
28874      */
28875     getValue : function(){
28876         return this.value;
28877     },
28878
28879     // private
28880     focus : function(){
28881         if(this.el){
28882             this.update(this.activeDate);
28883         }
28884     },
28885
28886     // privateval
28887     onRender : function(container, position){
28888         
28889         var m = [
28890              '<table cellspacing="0">',
28891                 '<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>',
28892                 '<tr><td colspan="3"><table class="x-date-inner" cellspacing="0"><thead><tr>'];
28893         var dn = this.dayNames;
28894         for(var i = 0; i < 7; i++){
28895             var d = this.startDay+i;
28896             if(d > 6){
28897                 d = d-7;
28898             }
28899             m.push("<th><span>", dn[d].substr(0,1), "</span></th>");
28900         }
28901         m[m.length] = "</tr></thead><tbody><tr>";
28902         for(var i = 0; i < 42; i++) {
28903             if(i % 7 == 0 && i != 0){
28904                 m[m.length] = "</tr><tr>";
28905             }
28906             m[m.length] = '<td><a href="#" hidefocus="on" class="x-date-date" tabIndex="1"><em><span></span></em></a></td>';
28907         }
28908         m[m.length] = '</tr></tbody></table></td></tr><tr>'+
28909             '<td colspan="3" class="x-date-bottom" align="center"></td></tr></table><div class="x-date-mp"></div>';
28910
28911         var el = document.createElement("div");
28912         el.className = "x-date-picker";
28913         el.innerHTML = m.join("");
28914
28915         container.dom.insertBefore(el, position);
28916
28917         this.el = Roo.get(el);
28918         this.eventEl = Roo.get(el.firstChild);
28919
28920         new Roo.util.ClickRepeater(this.el.child("td.x-date-left a"), {
28921             handler: this.showPrevMonth,
28922             scope: this,
28923             preventDefault:true,
28924             stopDefault:true
28925         });
28926
28927         new Roo.util.ClickRepeater(this.el.child("td.x-date-right a"), {
28928             handler: this.showNextMonth,
28929             scope: this,
28930             preventDefault:true,
28931             stopDefault:true
28932         });
28933
28934         this.eventEl.on("mousewheel", this.handleMouseWheel,  this);
28935
28936         this.monthPicker = this.el.down('div.x-date-mp');
28937         this.monthPicker.enableDisplayMode('block');
28938         
28939         var kn = new Roo.KeyNav(this.eventEl, {
28940             "left" : function(e){
28941                 e.ctrlKey ?
28942                     this.showPrevMonth() :
28943                     this.update(this.activeDate.add("d", -1));
28944             },
28945
28946             "right" : function(e){
28947                 e.ctrlKey ?
28948                     this.showNextMonth() :
28949                     this.update(this.activeDate.add("d", 1));
28950             },
28951
28952             "up" : function(e){
28953                 e.ctrlKey ?
28954                     this.showNextYear() :
28955                     this.update(this.activeDate.add("d", -7));
28956             },
28957
28958             "down" : function(e){
28959                 e.ctrlKey ?
28960                     this.showPrevYear() :
28961                     this.update(this.activeDate.add("d", 7));
28962             },
28963
28964             "pageUp" : function(e){
28965                 this.showNextMonth();
28966             },
28967
28968             "pageDown" : function(e){
28969                 this.showPrevMonth();
28970             },
28971
28972             "enter" : function(e){
28973                 e.stopPropagation();
28974                 return true;
28975             },
28976
28977             scope : this
28978         });
28979
28980         this.eventEl.on("click", this.handleDateClick,  this, {delegate: "a.x-date-date"});
28981
28982         this.eventEl.addKeyListener(Roo.EventObject.SPACE, this.selectToday,  this);
28983
28984         this.el.unselectable();
28985         
28986         this.cells = this.el.select("table.x-date-inner tbody td");
28987         this.textNodes = this.el.query("table.x-date-inner tbody span");
28988
28989         this.mbtn = new Roo.Button(this.el.child("td.x-date-middle", true), {
28990             text: "&#160;",
28991             tooltip: this.monthYearText
28992         });
28993
28994         this.mbtn.on('click', this.showMonthPicker, this);
28995         this.mbtn.el.child(this.mbtn.menuClassTarget).addClass("x-btn-with-menu");
28996
28997
28998         var today = (new Date()).dateFormat(this.format);
28999         
29000         var baseTb = new Roo.Toolbar(this.el.child("td.x-date-bottom", true));
29001         if (this.showClear) {
29002             baseTb.add( new Roo.Toolbar.Fill());
29003         }
29004         baseTb.add({
29005             text: String.format(this.todayText, today),
29006             tooltip: String.format(this.todayTip, today),
29007             handler: this.selectToday,
29008             scope: this
29009         });
29010         
29011         //var todayBtn = new Roo.Button(this.el.child("td.x-date-bottom", true), {
29012             
29013         //});
29014         if (this.showClear) {
29015             
29016             baseTb.add( new Roo.Toolbar.Fill());
29017             baseTb.add({
29018                 text: '&#160;',
29019                 cls: 'x-btn-icon x-btn-clear',
29020                 handler: function() {
29021                     //this.value = '';
29022                     this.fireEvent("select", this, '');
29023                 },
29024                 scope: this
29025             });
29026         }
29027         
29028         
29029         if(Roo.isIE){
29030             this.el.repaint();
29031         }
29032         this.update(this.value);
29033     },
29034
29035     createMonthPicker : function(){
29036         if(!this.monthPicker.dom.firstChild){
29037             var buf = ['<table border="0" cellspacing="0">'];
29038             for(var i = 0; i < 6; i++){
29039                 buf.push(
29040                     '<tr><td class="x-date-mp-month"><a href="#">', this.monthNames[i].substr(0, 3), '</a></td>',
29041                     '<td class="x-date-mp-month x-date-mp-sep"><a href="#">', this.monthNames[i+6].substr(0, 3), '</a></td>',
29042                     i == 0 ?
29043                     '<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>' :
29044                     '<td class="x-date-mp-year"><a href="#"></a></td><td class="x-date-mp-year"><a href="#"></a></td></tr>'
29045                 );
29046             }
29047             buf.push(
29048                 '<tr class="x-date-mp-btns"><td colspan="4"><button type="button" class="x-date-mp-ok">',
29049                     this.okText,
29050                     '</button><button type="button" class="x-date-mp-cancel">',
29051                     this.cancelText,
29052                     '</button></td></tr>',
29053                 '</table>'
29054             );
29055             this.monthPicker.update(buf.join(''));
29056             this.monthPicker.on('click', this.onMonthClick, this);
29057             this.monthPicker.on('dblclick', this.onMonthDblClick, this);
29058
29059             this.mpMonths = this.monthPicker.select('td.x-date-mp-month');
29060             this.mpYears = this.monthPicker.select('td.x-date-mp-year');
29061
29062             this.mpMonths.each(function(m, a, i){
29063                 i += 1;
29064                 if((i%2) == 0){
29065                     m.dom.xmonth = 5 + Math.round(i * .5);
29066                 }else{
29067                     m.dom.xmonth = Math.round((i-1) * .5);
29068                 }
29069             });
29070         }
29071     },
29072
29073     showMonthPicker : function(){
29074         this.createMonthPicker();
29075         var size = this.el.getSize();
29076         this.monthPicker.setSize(size);
29077         this.monthPicker.child('table').setSize(size);
29078
29079         this.mpSelMonth = (this.activeDate || this.value).getMonth();
29080         this.updateMPMonth(this.mpSelMonth);
29081         this.mpSelYear = (this.activeDate || this.value).getFullYear();
29082         this.updateMPYear(this.mpSelYear);
29083
29084         this.monthPicker.slideIn('t', {duration:.2});
29085     },
29086
29087     updateMPYear : function(y){
29088         this.mpyear = y;
29089         var ys = this.mpYears.elements;
29090         for(var i = 1; i <= 10; i++){
29091             var td = ys[i-1], y2;
29092             if((i%2) == 0){
29093                 y2 = y + Math.round(i * .5);
29094                 td.firstChild.innerHTML = y2;
29095                 td.xyear = y2;
29096             }else{
29097                 y2 = y - (5-Math.round(i * .5));
29098                 td.firstChild.innerHTML = y2;
29099                 td.xyear = y2;
29100             }
29101             this.mpYears.item(i-1)[y2 == this.mpSelYear ? 'addClass' : 'removeClass']('x-date-mp-sel');
29102         }
29103     },
29104
29105     updateMPMonth : function(sm){
29106         this.mpMonths.each(function(m, a, i){
29107             m[m.dom.xmonth == sm ? 'addClass' : 'removeClass']('x-date-mp-sel');
29108         });
29109     },
29110
29111     selectMPMonth: function(m){
29112         
29113     },
29114
29115     onMonthClick : function(e, t){
29116         e.stopEvent();
29117         var el = new Roo.Element(t), pn;
29118         if(el.is('button.x-date-mp-cancel')){
29119             this.hideMonthPicker();
29120         }
29121         else if(el.is('button.x-date-mp-ok')){
29122             this.update(new Date(this.mpSelYear, this.mpSelMonth, (this.activeDate || this.value).getDate()));
29123             this.hideMonthPicker();
29124         }
29125         else if(pn = el.up('td.x-date-mp-month', 2)){
29126             this.mpMonths.removeClass('x-date-mp-sel');
29127             pn.addClass('x-date-mp-sel');
29128             this.mpSelMonth = pn.dom.xmonth;
29129         }
29130         else if(pn = el.up('td.x-date-mp-year', 2)){
29131             this.mpYears.removeClass('x-date-mp-sel');
29132             pn.addClass('x-date-mp-sel');
29133             this.mpSelYear = pn.dom.xyear;
29134         }
29135         else if(el.is('a.x-date-mp-prev')){
29136             this.updateMPYear(this.mpyear-10);
29137         }
29138         else if(el.is('a.x-date-mp-next')){
29139             this.updateMPYear(this.mpyear+10);
29140         }
29141     },
29142
29143     onMonthDblClick : function(e, t){
29144         e.stopEvent();
29145         var el = new Roo.Element(t), pn;
29146         if(pn = el.up('td.x-date-mp-month', 2)){
29147             this.update(new Date(this.mpSelYear, pn.dom.xmonth, (this.activeDate || this.value).getDate()));
29148             this.hideMonthPicker();
29149         }
29150         else if(pn = el.up('td.x-date-mp-year', 2)){
29151             this.update(new Date(pn.dom.xyear, this.mpSelMonth, (this.activeDate || this.value).getDate()));
29152             this.hideMonthPicker();
29153         }
29154     },
29155
29156     hideMonthPicker : function(disableAnim){
29157         if(this.monthPicker){
29158             if(disableAnim === true){
29159                 this.monthPicker.hide();
29160             }else{
29161                 this.monthPicker.slideOut('t', {duration:.2});
29162             }
29163         }
29164     },
29165
29166     // private
29167     showPrevMonth : function(e){
29168         this.update(this.activeDate.add("mo", -1));
29169     },
29170
29171     // private
29172     showNextMonth : function(e){
29173         this.update(this.activeDate.add("mo", 1));
29174     },
29175
29176     // private
29177     showPrevYear : function(){
29178         this.update(this.activeDate.add("y", -1));
29179     },
29180
29181     // private
29182     showNextYear : function(){
29183         this.update(this.activeDate.add("y", 1));
29184     },
29185
29186     // private
29187     handleMouseWheel : function(e){
29188         var delta = e.getWheelDelta();
29189         if(delta > 0){
29190             this.showPrevMonth();
29191             e.stopEvent();
29192         } else if(delta < 0){
29193             this.showNextMonth();
29194             e.stopEvent();
29195         }
29196     },
29197
29198     // private
29199     handleDateClick : function(e, t){
29200         e.stopEvent();
29201         if(t.dateValue && !Roo.fly(t.parentNode).hasClass("x-date-disabled")){
29202             this.setValue(new Date(t.dateValue));
29203             this.fireEvent("select", this, this.value);
29204         }
29205     },
29206
29207     // private
29208     selectToday : function(){
29209         this.setValue(new Date().clearTime());
29210         this.fireEvent("select", this, this.value);
29211     },
29212
29213     // private
29214     update : function(date)
29215     {
29216         var vd = this.activeDate;
29217         this.activeDate = date;
29218         if(vd && this.el){
29219             var t = date.getTime();
29220             if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
29221                 this.cells.removeClass("x-date-selected");
29222                 this.cells.each(function(c){
29223                    if(c.dom.firstChild.dateValue == t){
29224                        c.addClass("x-date-selected");
29225                        setTimeout(function(){
29226                             try{c.dom.firstChild.focus();}catch(e){}
29227                        }, 50);
29228                        return false;
29229                    }
29230                 });
29231                 return;
29232             }
29233         }
29234         
29235         var days = date.getDaysInMonth();
29236         var firstOfMonth = date.getFirstDateOfMonth();
29237         var startingPos = firstOfMonth.getDay()-this.startDay;
29238
29239         if(startingPos <= this.startDay){
29240             startingPos += 7;
29241         }
29242
29243         var pm = date.add("mo", -1);
29244         var prevStart = pm.getDaysInMonth()-startingPos;
29245
29246         var cells = this.cells.elements;
29247         var textEls = this.textNodes;
29248         days += startingPos;
29249
29250         // convert everything to numbers so it's fast
29251         var day = 86400000;
29252         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
29253         var today = new Date().clearTime().getTime();
29254         var sel = date.clearTime().getTime();
29255         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
29256         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
29257         var ddMatch = this.disabledDatesRE;
29258         var ddText = this.disabledDatesText;
29259         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
29260         var ddaysText = this.disabledDaysText;
29261         var format = this.format;
29262
29263         var setCellClass = function(cal, cell){
29264             cell.title = "";
29265             var t = d.getTime();
29266             cell.firstChild.dateValue = t;
29267             if(t == today){
29268                 cell.className += " x-date-today";
29269                 cell.title = cal.todayText;
29270             }
29271             if(t == sel){
29272                 cell.className += " x-date-selected";
29273                 setTimeout(function(){
29274                     try{cell.firstChild.focus();}catch(e){}
29275                 }, 50);
29276             }
29277             // disabling
29278             if(t < min) {
29279                 cell.className = " x-date-disabled";
29280                 cell.title = cal.minText;
29281                 return;
29282             }
29283             if(t > max) {
29284                 cell.className = " x-date-disabled";
29285                 cell.title = cal.maxText;
29286                 return;
29287             }
29288             if(ddays){
29289                 if(ddays.indexOf(d.getDay()) != -1){
29290                     cell.title = ddaysText;
29291                     cell.className = " x-date-disabled";
29292                 }
29293             }
29294             if(ddMatch && format){
29295                 var fvalue = d.dateFormat(format);
29296                 if(ddMatch.test(fvalue)){
29297                     cell.title = ddText.replace("%0", fvalue);
29298                     cell.className = " x-date-disabled";
29299                 }
29300             }
29301         };
29302
29303         var i = 0;
29304         for(; i < startingPos; i++) {
29305             textEls[i].innerHTML = (++prevStart);
29306             d.setDate(d.getDate()+1);
29307             cells[i].className = "x-date-prevday";
29308             setCellClass(this, cells[i]);
29309         }
29310         for(; i < days; i++){
29311             intDay = i - startingPos + 1;
29312             textEls[i].innerHTML = (intDay);
29313             d.setDate(d.getDate()+1);
29314             cells[i].className = "x-date-active";
29315             setCellClass(this, cells[i]);
29316         }
29317         var extraDays = 0;
29318         for(; i < 42; i++) {
29319              textEls[i].innerHTML = (++extraDays);
29320              d.setDate(d.getDate()+1);
29321              cells[i].className = "x-date-nextday";
29322              setCellClass(this, cells[i]);
29323         }
29324
29325         this.mbtn.setText(this.monthNames[date.getMonth()] + " " + date.getFullYear());
29326         this.fireEvent('monthchange', this, date);
29327         
29328         if(!this.internalRender){
29329             var main = this.el.dom.firstChild;
29330             var w = main.offsetWidth;
29331             this.el.setWidth(w + this.el.getBorderWidth("lr"));
29332             Roo.fly(main).setWidth(w);
29333             this.internalRender = true;
29334             // opera does not respect the auto grow header center column
29335             // then, after it gets a width opera refuses to recalculate
29336             // without a second pass
29337             if(Roo.isOpera && !this.secondPass){
29338                 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
29339                 this.secondPass = true;
29340                 this.update.defer(10, this, [date]);
29341             }
29342         }
29343         
29344         
29345     }
29346 });        /*
29347  * Based on:
29348  * Ext JS Library 1.1.1
29349  * Copyright(c) 2006-2007, Ext JS, LLC.
29350  *
29351  * Originally Released Under LGPL - original licence link has changed is not relivant.
29352  *
29353  * Fork - LGPL
29354  * <script type="text/javascript">
29355  */
29356 /**
29357  * @class Roo.TabPanel
29358  * @extends Roo.util.Observable
29359  * A lightweight tab container.
29360  * <br><br>
29361  * Usage:
29362  * <pre><code>
29363 // basic tabs 1, built from existing content
29364 var tabs = new Roo.TabPanel("tabs1");
29365 tabs.addTab("script", "View Script");
29366 tabs.addTab("markup", "View Markup");
29367 tabs.activate("script");
29368
29369 // more advanced tabs, built from javascript
29370 var jtabs = new Roo.TabPanel("jtabs");
29371 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
29372
29373 // set up the UpdateManager
29374 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
29375 var updater = tab2.getUpdateManager();
29376 updater.setDefaultUrl("ajax1.htm");
29377 tab2.on('activate', updater.refresh, updater, true);
29378
29379 // Use setUrl for Ajax loading
29380 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
29381 tab3.setUrl("ajax2.htm", null, true);
29382
29383 // Disabled tab
29384 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
29385 tab4.disable();
29386
29387 jtabs.activate("jtabs-1");
29388  * </code></pre>
29389  * @constructor
29390  * Create a new TabPanel.
29391  * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
29392  * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
29393  */
29394 Roo.TabPanel = function(container, config){
29395     /**
29396     * The container element for this TabPanel.
29397     * @type Roo.Element
29398     */
29399     this.el = Roo.get(container, true);
29400     if(config){
29401         if(typeof config == "boolean"){
29402             this.tabPosition = config ? "bottom" : "top";
29403         }else{
29404             Roo.apply(this, config);
29405         }
29406     }
29407     if(this.tabPosition == "bottom"){
29408         this.bodyEl = Roo.get(this.createBody(this.el.dom));
29409         this.el.addClass("x-tabs-bottom");
29410     }
29411     this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
29412     this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
29413     this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
29414     if(Roo.isIE){
29415         Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
29416     }
29417     if(this.tabPosition != "bottom"){
29418         /** The body element that contains {@link Roo.TabPanelItem} bodies. +
29419          * @type Roo.Element
29420          */
29421         this.bodyEl = Roo.get(this.createBody(this.el.dom));
29422         this.el.addClass("x-tabs-top");
29423     }
29424     this.items = [];
29425
29426     this.bodyEl.setStyle("position", "relative");
29427
29428     this.active = null;
29429     this.activateDelegate = this.activate.createDelegate(this);
29430
29431     this.addEvents({
29432         /**
29433          * @event tabchange
29434          * Fires when the active tab changes
29435          * @param {Roo.TabPanel} this
29436          * @param {Roo.TabPanelItem} activePanel The new active tab
29437          */
29438         "tabchange": true,
29439         /**
29440          * @event beforetabchange
29441          * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
29442          * @param {Roo.TabPanel} this
29443          * @param {Object} e Set cancel to true on this object to cancel the tab change
29444          * @param {Roo.TabPanelItem} tab The tab being changed to
29445          */
29446         "beforetabchange" : true
29447     });
29448
29449     Roo.EventManager.onWindowResize(this.onResize, this);
29450     this.cpad = this.el.getPadding("lr");
29451     this.hiddenCount = 0;
29452
29453
29454     // toolbar on the tabbar support...
29455     if (this.toolbar) {
29456         var tcfg = this.toolbar;
29457         tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');  
29458         this.toolbar = new Roo.Toolbar(tcfg);
29459         if (Roo.isSafari) {
29460             var tbl = tcfg.container.child('table', true);
29461             tbl.setAttribute('width', '100%');
29462         }
29463         
29464     }
29465    
29466
29467
29468     Roo.TabPanel.superclass.constructor.call(this);
29469 };
29470
29471 Roo.extend(Roo.TabPanel, Roo.util.Observable, {
29472     /*
29473      *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
29474      */
29475     tabPosition : "top",
29476     /*
29477      *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
29478      */
29479     currentTabWidth : 0,
29480     /*
29481      *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
29482      */
29483     minTabWidth : 40,
29484     /*
29485      *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
29486      */
29487     maxTabWidth : 250,
29488     /*
29489      *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
29490      */
29491     preferredTabWidth : 175,
29492     /*
29493      *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
29494      */
29495     resizeTabs : false,
29496     /*
29497      *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
29498      */
29499     monitorResize : true,
29500     /*
29501      *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar. 
29502      */
29503     toolbar : false,
29504
29505     /**
29506      * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
29507      * @param {String} id The id of the div to use <b>or create</b>
29508      * @param {String} text The text for the tab
29509      * @param {String} content (optional) Content to put in the TabPanelItem body
29510      * @param {Boolean} closable (optional) True to create a close icon on the tab
29511      * @return {Roo.TabPanelItem} The created TabPanelItem
29512      */
29513     addTab : function(id, text, content, closable){
29514         var item = new Roo.TabPanelItem(this, id, text, closable);
29515         this.addTabItem(item);
29516         if(content){
29517             item.setContent(content);
29518         }
29519         return item;
29520     },
29521
29522     /**
29523      * Returns the {@link Roo.TabPanelItem} with the specified id/index
29524      * @param {String/Number} id The id or index of the TabPanelItem to fetch.
29525      * @return {Roo.TabPanelItem}
29526      */
29527     getTab : function(id){
29528         return this.items[id];
29529     },
29530
29531     /**
29532      * Hides the {@link Roo.TabPanelItem} with the specified id/index
29533      * @param {String/Number} id The id or index of the TabPanelItem to hide.
29534      */
29535     hideTab : function(id){
29536         var t = this.items[id];
29537         if(!t.isHidden()){
29538            t.setHidden(true);
29539            this.hiddenCount++;
29540            this.autoSizeTabs();
29541         }
29542     },
29543
29544     /**
29545      * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
29546      * @param {String/Number} id The id or index of the TabPanelItem to unhide.
29547      */
29548     unhideTab : function(id){
29549         var t = this.items[id];
29550         if(t.isHidden()){
29551            t.setHidden(false);
29552            this.hiddenCount--;
29553            this.autoSizeTabs();
29554         }
29555     },
29556
29557     /**
29558      * Adds an existing {@link Roo.TabPanelItem}.
29559      * @param {Roo.TabPanelItem} item The TabPanelItem to add
29560      */
29561     addTabItem : function(item){
29562         this.items[item.id] = item;
29563         this.items.push(item);
29564         if(this.resizeTabs){
29565            item.setWidth(this.currentTabWidth || this.preferredTabWidth);
29566            this.autoSizeTabs();
29567         }else{
29568             item.autoSize();
29569         }
29570     },
29571
29572     /**
29573      * Removes a {@link Roo.TabPanelItem}.
29574      * @param {String/Number} id The id or index of the TabPanelItem to remove.
29575      */
29576     removeTab : function(id){
29577         var items = this.items;
29578         var tab = items[id];
29579         if(!tab) { return; }
29580         var index = items.indexOf(tab);
29581         if(this.active == tab && items.length > 1){
29582             var newTab = this.getNextAvailable(index);
29583             if(newTab) {
29584                 newTab.activate();
29585             }
29586         }
29587         this.stripEl.dom.removeChild(tab.pnode.dom);
29588         if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
29589             this.bodyEl.dom.removeChild(tab.bodyEl.dom);
29590         }
29591         items.splice(index, 1);
29592         delete this.items[tab.id];
29593         tab.fireEvent("close", tab);
29594         tab.purgeListeners();
29595         this.autoSizeTabs();
29596     },
29597
29598     getNextAvailable : function(start){
29599         var items = this.items;
29600         var index = start;
29601         // look for a next tab that will slide over to
29602         // replace the one being removed
29603         while(index < items.length){
29604             var item = items[++index];
29605             if(item && !item.isHidden()){
29606                 return item;
29607             }
29608         }
29609         // if one isn't found select the previous tab (on the left)
29610         index = start;
29611         while(index >= 0){
29612             var item = items[--index];
29613             if(item && !item.isHidden()){
29614                 return item;
29615             }
29616         }
29617         return null;
29618     },
29619
29620     /**
29621      * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
29622      * @param {String/Number} id The id or index of the TabPanelItem to disable.
29623      */
29624     disableTab : function(id){
29625         var tab = this.items[id];
29626         if(tab && this.active != tab){
29627             tab.disable();
29628         }
29629     },
29630
29631     /**
29632      * Enables a {@link Roo.TabPanelItem} that is disabled.
29633      * @param {String/Number} id The id or index of the TabPanelItem to enable.
29634      */
29635     enableTab : function(id){
29636         var tab = this.items[id];
29637         tab.enable();
29638     },
29639
29640     /**
29641      * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
29642      * @param {String/Number} id The id or index of the TabPanelItem to activate.
29643      * @return {Roo.TabPanelItem} The TabPanelItem.
29644      */
29645     activate : function(id){
29646         var tab = this.items[id];
29647         if(!tab){
29648             return null;
29649         }
29650         if(tab == this.active || tab.disabled){
29651             return tab;
29652         }
29653         var e = {};
29654         this.fireEvent("beforetabchange", this, e, tab);
29655         if(e.cancel !== true && !tab.disabled){
29656             if(this.active){
29657                 this.active.hide();
29658             }
29659             this.active = this.items[id];
29660             this.active.show();
29661             this.fireEvent("tabchange", this, this.active);
29662         }
29663         return tab;
29664     },
29665
29666     /**
29667      * Gets the active {@link Roo.TabPanelItem}.
29668      * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
29669      */
29670     getActiveTab : function(){
29671         return this.active;
29672     },
29673
29674     /**
29675      * Updates the tab body element to fit the height of the container element
29676      * for overflow scrolling
29677      * @param {Number} targetHeight (optional) Override the starting height from the elements height
29678      */
29679     syncHeight : function(targetHeight){
29680         var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
29681         var bm = this.bodyEl.getMargins();
29682         var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
29683         this.bodyEl.setHeight(newHeight);
29684         return newHeight;
29685     },
29686
29687     onResize : function(){
29688         if(this.monitorResize){
29689             this.autoSizeTabs();
29690         }
29691     },
29692
29693     /**
29694      * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
29695      */
29696     beginUpdate : function(){
29697         this.updating = true;
29698     },
29699
29700     /**
29701      * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
29702      */
29703     endUpdate : function(){
29704         this.updating = false;
29705         this.autoSizeTabs();
29706     },
29707
29708     /**
29709      * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
29710      */
29711     autoSizeTabs : function(){
29712         var count = this.items.length;
29713         var vcount = count - this.hiddenCount;
29714         if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) {
29715             return;
29716         }
29717         var w = Math.max(this.el.getWidth() - this.cpad, 10);
29718         var availWidth = Math.floor(w / vcount);
29719         var b = this.stripBody;
29720         if(b.getWidth() > w){
29721             var tabs = this.items;
29722             this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
29723             if(availWidth < this.minTabWidth){
29724                 /*if(!this.sleft){    // incomplete scrolling code
29725                     this.createScrollButtons();
29726                 }
29727                 this.showScroll();
29728                 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
29729             }
29730         }else{
29731             if(this.currentTabWidth < this.preferredTabWidth){
29732                 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
29733             }
29734         }
29735     },
29736
29737     /**
29738      * Returns the number of tabs in this TabPanel.
29739      * @return {Number}
29740      */
29741      getCount : function(){
29742          return this.items.length;
29743      },
29744
29745     /**
29746      * Resizes all the tabs to the passed width
29747      * @param {Number} The new width
29748      */
29749     setTabWidth : function(width){
29750         this.currentTabWidth = width;
29751         for(var i = 0, len = this.items.length; i < len; i++) {
29752                 if(!this.items[i].isHidden()) {
29753                 this.items[i].setWidth(width);
29754             }
29755         }
29756     },
29757
29758     /**
29759      * Destroys this TabPanel
29760      * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
29761      */
29762     destroy : function(removeEl){
29763         Roo.EventManager.removeResizeListener(this.onResize, this);
29764         for(var i = 0, len = this.items.length; i < len; i++){
29765             this.items[i].purgeListeners();
29766         }
29767         if(removeEl === true){
29768             this.el.update("");
29769             this.el.remove();
29770         }
29771     }
29772 });
29773
29774 /**
29775  * @class Roo.TabPanelItem
29776  * @extends Roo.util.Observable
29777  * Represents an individual item (tab plus body) in a TabPanel.
29778  * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
29779  * @param {String} id The id of this TabPanelItem
29780  * @param {String} text The text for the tab of this TabPanelItem
29781  * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
29782  */
29783 Roo.TabPanelItem = function(tabPanel, id, text, closable){
29784     /**
29785      * The {@link Roo.TabPanel} this TabPanelItem belongs to
29786      * @type Roo.TabPanel
29787      */
29788     this.tabPanel = tabPanel;
29789     /**
29790      * The id for this TabPanelItem
29791      * @type String
29792      */
29793     this.id = id;
29794     /** @private */
29795     this.disabled = false;
29796     /** @private */
29797     this.text = text;
29798     /** @private */
29799     this.loaded = false;
29800     this.closable = closable;
29801
29802     /**
29803      * The body element for this TabPanelItem.
29804      * @type Roo.Element
29805      */
29806     this.bodyEl = Roo.get(tabPanel.createItemBody(tabPanel.bodyEl.dom, id));
29807     this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
29808     this.bodyEl.setStyle("display", "block");
29809     this.bodyEl.setStyle("zoom", "1");
29810     this.hideAction();
29811
29812     var els = tabPanel.createStripElements(tabPanel.stripEl.dom, text, closable);
29813     /** @private */
29814     this.el = Roo.get(els.el, true);
29815     this.inner = Roo.get(els.inner, true);
29816     this.textEl = Roo.get(this.el.dom.firstChild.firstChild.firstChild, true);
29817     this.pnode = Roo.get(els.el.parentNode, true);
29818     this.el.on("mousedown", this.onTabMouseDown, this);
29819     this.el.on("click", this.onTabClick, this);
29820     /** @private */
29821     if(closable){
29822         var c = Roo.get(els.close, true);
29823         c.dom.title = this.closeText;
29824         c.addClassOnOver("close-over");
29825         c.on("click", this.closeClick, this);
29826      }
29827
29828     this.addEvents({
29829          /**
29830          * @event activate
29831          * Fires when this tab becomes the active tab.
29832          * @param {Roo.TabPanel} tabPanel The parent TabPanel
29833          * @param {Roo.TabPanelItem} this
29834          */
29835         "activate": true,
29836         /**
29837          * @event beforeclose
29838          * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
29839          * @param {Roo.TabPanelItem} this
29840          * @param {Object} e Set cancel to true on this object to cancel the close.
29841          */
29842         "beforeclose": true,
29843         /**
29844          * @event close
29845          * Fires when this tab is closed.
29846          * @param {Roo.TabPanelItem} this
29847          */
29848          "close": true,
29849         /**
29850          * @event deactivate
29851          * Fires when this tab is no longer the active tab.
29852          * @param {Roo.TabPanel} tabPanel The parent TabPanel
29853          * @param {Roo.TabPanelItem} this
29854          */
29855          "deactivate" : true
29856     });
29857     this.hidden = false;
29858
29859     Roo.TabPanelItem.superclass.constructor.call(this);
29860 };
29861
29862 Roo.extend(Roo.TabPanelItem, Roo.util.Observable, {
29863     purgeListeners : function(){
29864        Roo.util.Observable.prototype.purgeListeners.call(this);
29865        this.el.removeAllListeners();
29866     },
29867     /**
29868      * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
29869      */
29870     show : function(){
29871         this.pnode.addClass("on");
29872         this.showAction();
29873         if(Roo.isOpera){
29874             this.tabPanel.stripWrap.repaint();
29875         }
29876         this.fireEvent("activate", this.tabPanel, this);
29877     },
29878
29879     /**
29880      * Returns true if this tab is the active tab.
29881      * @return {Boolean}
29882      */
29883     isActive : function(){
29884         return this.tabPanel.getActiveTab() == this;
29885     },
29886
29887     /**
29888      * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
29889      */
29890     hide : function(){
29891         this.pnode.removeClass("on");
29892         this.hideAction();
29893         this.fireEvent("deactivate", this.tabPanel, this);
29894     },
29895
29896     hideAction : function(){
29897         this.bodyEl.hide();
29898         this.bodyEl.setStyle("position", "absolute");
29899         this.bodyEl.setLeft("-20000px");
29900         this.bodyEl.setTop("-20000px");
29901     },
29902
29903     showAction : function(){
29904         this.bodyEl.setStyle("position", "relative");
29905         this.bodyEl.setTop("");
29906         this.bodyEl.setLeft("");
29907         this.bodyEl.show();
29908     },
29909
29910     /**
29911      * Set the tooltip for the tab.
29912      * @param {String} tooltip The tab's tooltip
29913      */
29914     setTooltip : function(text){
29915         if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
29916             this.textEl.dom.qtip = text;
29917             this.textEl.dom.removeAttribute('title');
29918         }else{
29919             this.textEl.dom.title = text;
29920         }
29921     },
29922
29923     onTabClick : function(e){
29924         e.preventDefault();
29925         this.tabPanel.activate(this.id);
29926     },
29927
29928     onTabMouseDown : function(e){
29929         e.preventDefault();
29930         this.tabPanel.activate(this.id);
29931     },
29932
29933     getWidth : function(){
29934         return this.inner.getWidth();
29935     },
29936
29937     setWidth : function(width){
29938         var iwidth = width - this.pnode.getPadding("lr");
29939         this.inner.setWidth(iwidth);
29940         this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
29941         this.pnode.setWidth(width);
29942     },
29943
29944     /**
29945      * Show or hide the tab
29946      * @param {Boolean} hidden True to hide or false to show.
29947      */
29948     setHidden : function(hidden){
29949         this.hidden = hidden;
29950         this.pnode.setStyle("display", hidden ? "none" : "");
29951     },
29952
29953     /**
29954      * Returns true if this tab is "hidden"
29955      * @return {Boolean}
29956      */
29957     isHidden : function(){
29958         return this.hidden;
29959     },
29960
29961     /**
29962      * Returns the text for this tab
29963      * @return {String}
29964      */
29965     getText : function(){
29966         return this.text;
29967     },
29968
29969     autoSize : function(){
29970         //this.el.beginMeasure();
29971         this.textEl.setWidth(1);
29972         /*
29973          *  #2804 [new] Tabs in Roojs
29974          *  increase the width by 2-4 pixels to prevent the ellipssis showing in chrome
29975          */
29976         this.setWidth(this.textEl.dom.scrollWidth+this.pnode.getPadding("lr")+this.inner.getPadding("lr") + 2);
29977         //this.el.endMeasure();
29978     },
29979
29980     /**
29981      * Sets the text for the tab (Note: this also sets the tooltip text)
29982      * @param {String} text The tab's text and tooltip
29983      */
29984     setText : function(text){
29985         this.text = text;
29986         this.textEl.update(text);
29987         this.setTooltip(text);
29988         if(!this.tabPanel.resizeTabs){
29989             this.autoSize();
29990         }
29991     },
29992     /**
29993      * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
29994      */
29995     activate : function(){
29996         this.tabPanel.activate(this.id);
29997     },
29998
29999     /**
30000      * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
30001      */
30002     disable : function(){
30003         if(this.tabPanel.active != this){
30004             this.disabled = true;
30005             this.pnode.addClass("disabled");
30006         }
30007     },
30008
30009     /**
30010      * Enables this TabPanelItem if it was previously disabled.
30011      */
30012     enable : function(){
30013         this.disabled = false;
30014         this.pnode.removeClass("disabled");
30015     },
30016
30017     /**
30018      * Sets the content for this TabPanelItem.
30019      * @param {String} content The content
30020      * @param {Boolean} loadScripts true to look for and load scripts
30021      */
30022     setContent : function(content, loadScripts){
30023         this.bodyEl.update(content, loadScripts);
30024     },
30025
30026     /**
30027      * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
30028      * @return {Roo.UpdateManager} The UpdateManager
30029      */
30030     getUpdateManager : function(){
30031         return this.bodyEl.getUpdateManager();
30032     },
30033
30034     /**
30035      * Set a URL to be used to load the content for this TabPanelItem.
30036      * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
30037      * @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)
30038      * @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)
30039      * @return {Roo.UpdateManager} The UpdateManager
30040      */
30041     setUrl : function(url, params, loadOnce){
30042         if(this.refreshDelegate){
30043             this.un('activate', this.refreshDelegate);
30044         }
30045         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
30046         this.on("activate", this.refreshDelegate);
30047         return this.bodyEl.getUpdateManager();
30048     },
30049
30050     /** @private */
30051     _handleRefresh : function(url, params, loadOnce){
30052         if(!loadOnce || !this.loaded){
30053             var updater = this.bodyEl.getUpdateManager();
30054             updater.update(url, params, this._setLoaded.createDelegate(this));
30055         }
30056     },
30057
30058     /**
30059      *   Forces a content refresh from the URL specified in the {@link #setUrl} method.
30060      *   Will fail silently if the setUrl method has not been called.
30061      *   This does not activate the panel, just updates its content.
30062      */
30063     refresh : function(){
30064         if(this.refreshDelegate){
30065            this.loaded = false;
30066            this.refreshDelegate();
30067         }
30068     },
30069
30070     /** @private */
30071     _setLoaded : function(){
30072         this.loaded = true;
30073     },
30074
30075     /** @private */
30076     closeClick : function(e){
30077         var o = {};
30078         e.stopEvent();
30079         this.fireEvent("beforeclose", this, o);
30080         if(o.cancel !== true){
30081             this.tabPanel.removeTab(this.id);
30082         }
30083     },
30084     /**
30085      * The text displayed in the tooltip for the close icon.
30086      * @type String
30087      */
30088     closeText : "Close this tab"
30089 });
30090
30091 /** @private */
30092 Roo.TabPanel.prototype.createStrip = function(container){
30093     var strip = document.createElement("div");
30094     strip.className = "x-tabs-wrap";
30095     container.appendChild(strip);
30096     return strip;
30097 };
30098 /** @private */
30099 Roo.TabPanel.prototype.createStripList = function(strip){
30100     // div wrapper for retard IE
30101     // returns the "tr" element.
30102     strip.innerHTML = '<div class="x-tabs-strip-wrap">'+
30103         '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
30104         '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
30105     return strip.firstChild.firstChild.firstChild.firstChild;
30106 };
30107 /** @private */
30108 Roo.TabPanel.prototype.createBody = function(container){
30109     var body = document.createElement("div");
30110     Roo.id(body, "tab-body");
30111     Roo.fly(body).addClass("x-tabs-body");
30112     container.appendChild(body);
30113     return body;
30114 };
30115 /** @private */
30116 Roo.TabPanel.prototype.createItemBody = function(bodyEl, id){
30117     var body = Roo.getDom(id);
30118     if(!body){
30119         body = document.createElement("div");
30120         body.id = id;
30121     }
30122     Roo.fly(body).addClass("x-tabs-item-body");
30123     bodyEl.insertBefore(body, bodyEl.firstChild);
30124     return body;
30125 };
30126 /** @private */
30127 Roo.TabPanel.prototype.createStripElements = function(stripEl, text, closable){
30128     var td = document.createElement("td");
30129     stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
30130     //stripEl.appendChild(td);
30131     if(closable){
30132         td.className = "x-tabs-closable";
30133         if(!this.closeTpl){
30134             this.closeTpl = new Roo.Template(
30135                '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
30136                '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
30137                '<div unselectable="on" class="close-icon">&#160;</div></em></span></a>'
30138             );
30139         }
30140         var el = this.closeTpl.overwrite(td, {"text": text});
30141         var close = el.getElementsByTagName("div")[0];
30142         var inner = el.getElementsByTagName("em")[0];
30143         return {"el": el, "close": close, "inner": inner};
30144     } else {
30145         if(!this.tabTpl){
30146             this.tabTpl = new Roo.Template(
30147                '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
30148                '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
30149             );
30150         }
30151         var el = this.tabTpl.overwrite(td, {"text": text});
30152         var inner = el.getElementsByTagName("em")[0];
30153         return {"el": el, "inner": inner};
30154     }
30155 };/*
30156  * Based on:
30157  * Ext JS Library 1.1.1
30158  * Copyright(c) 2006-2007, Ext JS, LLC.
30159  *
30160  * Originally Released Under LGPL - original licence link has changed is not relivant.
30161  *
30162  * Fork - LGPL
30163  * <script type="text/javascript">
30164  */
30165
30166 /**
30167  * @class Roo.Button
30168  * @extends Roo.util.Observable
30169  * Simple Button class
30170  * @cfg {String} text The button text
30171  * @cfg {String} icon The path to an image to display in the button (the image will be set as the background-image
30172  * CSS property of the button by default, so if you want a mixed icon/text button, set cls:"x-btn-text-icon")
30173  * @cfg {Function} handler A function called when the button is clicked (can be used instead of click event)
30174  * @cfg {Object} scope The scope of the handler
30175  * @cfg {Number} minWidth The minimum width for this button (used to give a set of buttons a common width)
30176  * @cfg {String/Object} tooltip The tooltip for the button - can be a string or QuickTips config object
30177  * @cfg {Boolean} hidden True to start hidden (defaults to false)
30178  * @cfg {Boolean} disabled True to start disabled (defaults to false)
30179  * @cfg {Boolean} pressed True to start pressed (only if enableToggle = true)
30180  * @cfg {String} toggleGroup The group this toggle button is a member of (only 1 per group can be pressed, only
30181    applies if enableToggle = true)
30182  * @cfg {String/HTMLElement/Element} renderTo The element to append the button to
30183  * @cfg {Boolean/Object} repeat True to repeat fire the click event while the mouse is down. This can also be
30184   an {@link Roo.util.ClickRepeater} config object (defaults to false).
30185  * @constructor
30186  * Create a new button
30187  * @param {Object} config The config object
30188  */
30189 Roo.Button = function(renderTo, config)
30190 {
30191     if (!config) {
30192         config = renderTo;
30193         renderTo = config.renderTo || false;
30194     }
30195     
30196     Roo.apply(this, config);
30197     this.addEvents({
30198         /**
30199              * @event click
30200              * Fires when this button is clicked
30201              * @param {Button} this
30202              * @param {EventObject} e The click event
30203              */
30204             "click" : true,
30205         /**
30206              * @event toggle
30207              * Fires when the "pressed" state of this button changes (only if enableToggle = true)
30208              * @param {Button} this
30209              * @param {Boolean} pressed
30210              */
30211             "toggle" : true,
30212         /**
30213              * @event mouseover
30214              * Fires when the mouse hovers over the button
30215              * @param {Button} this
30216              * @param {Event} e The event object
30217              */
30218         'mouseover' : true,
30219         /**
30220              * @event mouseout
30221              * Fires when the mouse exits the button
30222              * @param {Button} this
30223              * @param {Event} e The event object
30224              */
30225         'mouseout': true,
30226          /**
30227              * @event render
30228              * Fires when the button is rendered
30229              * @param {Button} this
30230              */
30231         'render': true
30232     });
30233     if(this.menu){
30234         this.menu = Roo.menu.MenuMgr.get(this.menu);
30235     }
30236     // register listeners first!!  - so render can be captured..
30237     Roo.util.Observable.call(this);
30238     if(renderTo){
30239         this.render(renderTo);
30240     }
30241     
30242   
30243 };
30244
30245 Roo.extend(Roo.Button, Roo.util.Observable, {
30246     /**
30247      * 
30248      */
30249     
30250     /**
30251      * Read-only. True if this button is hidden
30252      * @type Boolean
30253      */
30254     hidden : false,
30255     /**
30256      * Read-only. True if this button is disabled
30257      * @type Boolean
30258      */
30259     disabled : false,
30260     /**
30261      * Read-only. True if this button is pressed (only if enableToggle = true)
30262      * @type Boolean
30263      */
30264     pressed : false,
30265
30266     /**
30267      * @cfg {Number} tabIndex 
30268      * The DOM tabIndex for this button (defaults to undefined)
30269      */
30270     tabIndex : undefined,
30271
30272     /**
30273      * @cfg {Boolean} enableToggle
30274      * True to enable pressed/not pressed toggling (defaults to false)
30275      */
30276     enableToggle: false,
30277     /**
30278      * @cfg {Roo.menu.Menu} menu
30279      * Standard menu attribute consisting of a reference to a menu object, a menu id or a menu config blob (defaults to undefined).
30280      */
30281     menu : undefined,
30282     /**
30283      * @cfg {String} menuAlign
30284      * The position to align the menu to (see {@link Roo.Element#alignTo} for more details, defaults to 'tl-bl?').
30285      */
30286     menuAlign : "tl-bl?",
30287
30288     /**
30289      * @cfg {String} iconCls
30290      * A css class which sets a background image to be used as the icon for this button (defaults to undefined).
30291      */
30292     iconCls : undefined,
30293     /**
30294      * @cfg {String} type
30295      * The button's type, corresponding to the DOM input element type attribute.  Either "submit," "reset" or "button" (default).
30296      */
30297     type : 'button',
30298
30299     // private
30300     menuClassTarget: 'tr',
30301
30302     /**
30303      * @cfg {String} clickEvent
30304      * The type of event to map to the button's event handler (defaults to 'click')
30305      */
30306     clickEvent : 'click',
30307
30308     /**
30309      * @cfg {Boolean} handleMouseEvents
30310      * False to disable visual cues on mouseover, mouseout and mousedown (defaults to true)
30311      */
30312     handleMouseEvents : true,
30313
30314     /**
30315      * @cfg {String} tooltipType
30316      * The type of tooltip to use. Either "qtip" (default) for QuickTips or "title" for title attribute.
30317      */
30318     tooltipType : 'qtip',
30319
30320     /**
30321      * @cfg {String} cls
30322      * A CSS class to apply to the button's main element.
30323      */
30324     
30325     /**
30326      * @cfg {Roo.Template} template (Optional)
30327      * An {@link Roo.Template} with which to create the Button's main element. This Template must
30328      * contain numeric substitution parameter 0 if it is to display the tRoo property. Changing the template could
30329      * require code modifications if required elements (e.g. a button) aren't present.
30330      */
30331
30332     // private
30333     render : function(renderTo){
30334         var btn;
30335         if(this.hideParent){
30336             this.parentEl = Roo.get(renderTo);
30337         }
30338         if(!this.dhconfig){
30339             if(!this.template){
30340                 if(!Roo.Button.buttonTemplate){
30341                     // hideous table template
30342                     Roo.Button.buttonTemplate = new Roo.Template(
30343                         '<table border="0" cellpadding="0" cellspacing="0" class="x-btn-wrap"><tbody><tr>',
30344                         '<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>',
30345                         "</tr></tbody></table>");
30346                 }
30347                 this.template = Roo.Button.buttonTemplate;
30348             }
30349             btn = this.template.append(renderTo, [this.text || '&#160;', this.type], true);
30350             var btnEl = btn.child("button:first");
30351             btnEl.on('focus', this.onFocus, this);
30352             btnEl.on('blur', this.onBlur, this);
30353             if(this.cls){
30354                 btn.addClass(this.cls);
30355             }
30356             if(this.icon){
30357                 btnEl.setStyle('background-image', 'url(' +this.icon +')');
30358             }
30359             if(this.iconCls){
30360                 btnEl.addClass(this.iconCls);
30361                 if(!this.cls){
30362                     btn.addClass(this.text ? 'x-btn-text-icon' : 'x-btn-icon');
30363                 }
30364             }
30365             if(this.tabIndex !== undefined){
30366                 btnEl.dom.tabIndex = this.tabIndex;
30367             }
30368             if(this.tooltip){
30369                 if(typeof this.tooltip == 'object'){
30370                     Roo.QuickTips.tips(Roo.apply({
30371                           target: btnEl.id
30372                     }, this.tooltip));
30373                 } else {
30374                     btnEl.dom[this.tooltipType] = this.tooltip;
30375                 }
30376             }
30377         }else{
30378             btn = Roo.DomHelper.append(Roo.get(renderTo).dom, this.dhconfig, true);
30379         }
30380         this.el = btn;
30381         if(this.id){
30382             this.el.dom.id = this.el.id = this.id;
30383         }
30384         if(this.menu){
30385             this.el.child(this.menuClassTarget).addClass("x-btn-with-menu");
30386             this.menu.on("show", this.onMenuShow, this);
30387             this.menu.on("hide", this.onMenuHide, this);
30388         }
30389         btn.addClass("x-btn");
30390         if(Roo.isIE && !Roo.isIE7){
30391             this.autoWidth.defer(1, this);
30392         }else{
30393             this.autoWidth();
30394         }
30395         if(this.handleMouseEvents){
30396             btn.on("mouseover", this.onMouseOver, this);
30397             btn.on("mouseout", this.onMouseOut, this);
30398             btn.on("mousedown", this.onMouseDown, this);
30399         }
30400         btn.on(this.clickEvent, this.onClick, this);
30401         //btn.on("mouseup", this.onMouseUp, this);
30402         if(this.hidden){
30403             this.hide();
30404         }
30405         if(this.disabled){
30406             this.disable();
30407         }
30408         Roo.ButtonToggleMgr.register(this);
30409         if(this.pressed){
30410             this.el.addClass("x-btn-pressed");
30411         }
30412         if(this.repeat){
30413             var repeater = new Roo.util.ClickRepeater(btn,
30414                 typeof this.repeat == "object" ? this.repeat : {}
30415             );
30416             repeater.on("click", this.onClick,  this);
30417         }
30418         
30419         this.fireEvent('render', this);
30420         
30421     },
30422     /**
30423      * Returns the button's underlying element
30424      * @return {Roo.Element} The element
30425      */
30426     getEl : function(){
30427         return this.el;  
30428     },
30429     
30430     /**
30431      * Destroys this Button and removes any listeners.
30432      */
30433     destroy : function(){
30434         Roo.ButtonToggleMgr.unregister(this);
30435         this.el.removeAllListeners();
30436         this.purgeListeners();
30437         this.el.remove();
30438     },
30439
30440     // private
30441     autoWidth : function(){
30442         if(this.el){
30443             this.el.setWidth("auto");
30444             if(Roo.isIE7 && Roo.isStrict){
30445                 var ib = this.el.child('button');
30446                 if(ib && ib.getWidth() > 20){
30447                     ib.clip();
30448                     ib.setWidth(Roo.util.TextMetrics.measure(ib, this.text).width+ib.getFrameWidth('lr'));
30449                 }
30450             }
30451             if(this.minWidth){
30452                 if(this.hidden){
30453                     this.el.beginMeasure();
30454                 }
30455                 if(this.el.getWidth() < this.minWidth){
30456                     this.el.setWidth(this.minWidth);
30457                 }
30458                 if(this.hidden){
30459                     this.el.endMeasure();
30460                 }
30461             }
30462         }
30463     },
30464
30465     /**
30466      * Assigns this button's click handler
30467      * @param {Function} handler The function to call when the button is clicked
30468      * @param {Object} scope (optional) Scope for the function passed in
30469      */
30470     setHandler : function(handler, scope){
30471         this.handler = handler;
30472         this.scope = scope;  
30473     },
30474     
30475     /**
30476      * Sets this button's text
30477      * @param {String} text The button text
30478      */
30479     setText : function(text){
30480         this.text = text;
30481         if(this.el){
30482             this.el.child("td.x-btn-center button.x-btn-text").update(text);
30483         }
30484         this.autoWidth();
30485     },
30486     
30487     /**
30488      * Gets the text for this button
30489      * @return {String} The button text
30490      */
30491     getText : function(){
30492         return this.text;  
30493     },
30494     
30495     /**
30496      * Show this button
30497      */
30498     show: function(){
30499         this.hidden = false;
30500         if(this.el){
30501             this[this.hideParent? 'parentEl' : 'el'].setStyle("display", "");
30502         }
30503     },
30504     
30505     /**
30506      * Hide this button
30507      */
30508     hide: function(){
30509         this.hidden = true;
30510         if(this.el){
30511             this[this.hideParent? 'parentEl' : 'el'].setStyle("display", "none");
30512         }
30513     },
30514     
30515     /**
30516      * Convenience function for boolean show/hide
30517      * @param {Boolean} visible True to show, false to hide
30518      */
30519     setVisible: function(visible){
30520         if(visible) {
30521             this.show();
30522         }else{
30523             this.hide();
30524         }
30525     },
30526     
30527     /**
30528      * If a state it passed, it becomes the pressed state otherwise the current state is toggled.
30529      * @param {Boolean} state (optional) Force a particular state
30530      */
30531     toggle : function(state){
30532         state = state === undefined ? !this.pressed : state;
30533         if(state != this.pressed){
30534             if(state){
30535                 this.el.addClass("x-btn-pressed");
30536                 this.pressed = true;
30537                 this.fireEvent("toggle", this, true);
30538             }else{
30539                 this.el.removeClass("x-btn-pressed");
30540                 this.pressed = false;
30541                 this.fireEvent("toggle", this, false);
30542             }
30543             if(this.toggleHandler){
30544                 this.toggleHandler.call(this.scope || this, this, state);
30545             }
30546         }
30547     },
30548     
30549     /**
30550      * Focus the button
30551      */
30552     focus : function(){
30553         this.el.child('button:first').focus();
30554     },
30555     
30556     /**
30557      * Disable this button
30558      */
30559     disable : function(){
30560         if(this.el){
30561             this.el.addClass("x-btn-disabled");
30562         }
30563         this.disabled = true;
30564     },
30565     
30566     /**
30567      * Enable this button
30568      */
30569     enable : function(){
30570         if(this.el){
30571             this.el.removeClass("x-btn-disabled");
30572         }
30573         this.disabled = false;
30574     },
30575
30576     /**
30577      * Convenience function for boolean enable/disable
30578      * @param {Boolean} enabled True to enable, false to disable
30579      */
30580     setDisabled : function(v){
30581         this[v !== true ? "enable" : "disable"]();
30582     },
30583
30584     // private
30585     onClick : function(e)
30586     {
30587         if(e){
30588             e.preventDefault();
30589         }
30590         if(e.button != 0){
30591             return;
30592         }
30593         if(!this.disabled){
30594             if(this.enableToggle){
30595                 this.toggle();
30596             }
30597             if(this.menu && !this.menu.isVisible()){
30598                 this.menu.show(this.el, this.menuAlign);
30599             }
30600             this.fireEvent("click", this, e);
30601             if(this.handler){
30602                 this.el.removeClass("x-btn-over");
30603                 this.handler.call(this.scope || this, this, e);
30604             }
30605         }
30606     },
30607     // private
30608     onMouseOver : function(e){
30609         if(!this.disabled){
30610             this.el.addClass("x-btn-over");
30611             this.fireEvent('mouseover', this, e);
30612         }
30613     },
30614     // private
30615     onMouseOut : function(e){
30616         if(!e.within(this.el,  true)){
30617             this.el.removeClass("x-btn-over");
30618             this.fireEvent('mouseout', this, e);
30619         }
30620     },
30621     // private
30622     onFocus : function(e){
30623         if(!this.disabled){
30624             this.el.addClass("x-btn-focus");
30625         }
30626     },
30627     // private
30628     onBlur : function(e){
30629         this.el.removeClass("x-btn-focus");
30630     },
30631     // private
30632     onMouseDown : function(e){
30633         if(!this.disabled && e.button == 0){
30634             this.el.addClass("x-btn-click");
30635             Roo.get(document).on('mouseup', this.onMouseUp, this);
30636         }
30637     },
30638     // private
30639     onMouseUp : function(e){
30640         if(e.button == 0){
30641             this.el.removeClass("x-btn-click");
30642             Roo.get(document).un('mouseup', this.onMouseUp, this);
30643         }
30644     },
30645     // private
30646     onMenuShow : function(e){
30647         this.el.addClass("x-btn-menu-active");
30648     },
30649     // private
30650     onMenuHide : function(e){
30651         this.el.removeClass("x-btn-menu-active");
30652     }   
30653 });
30654
30655 // Private utility class used by Button
30656 Roo.ButtonToggleMgr = function(){
30657    var groups = {};
30658    
30659    function toggleGroup(btn, state){
30660        if(state){
30661            var g = groups[btn.toggleGroup];
30662            for(var i = 0, l = g.length; i < l; i++){
30663                if(g[i] != btn){
30664                    g[i].toggle(false);
30665                }
30666            }
30667        }
30668    }
30669    
30670    return {
30671        register : function(btn){
30672            if(!btn.toggleGroup){
30673                return;
30674            }
30675            var g = groups[btn.toggleGroup];
30676            if(!g){
30677                g = groups[btn.toggleGroup] = [];
30678            }
30679            g.push(btn);
30680            btn.on("toggle", toggleGroup);
30681        },
30682        
30683        unregister : function(btn){
30684            if(!btn.toggleGroup){
30685                return;
30686            }
30687            var g = groups[btn.toggleGroup];
30688            if(g){
30689                g.remove(btn);
30690                btn.un("toggle", toggleGroup);
30691            }
30692        }
30693    };
30694 }();/*
30695  * Based on:
30696  * Ext JS Library 1.1.1
30697  * Copyright(c) 2006-2007, Ext JS, LLC.
30698  *
30699  * Originally Released Under LGPL - original licence link has changed is not relivant.
30700  *
30701  * Fork - LGPL
30702  * <script type="text/javascript">
30703  */
30704  
30705 /**
30706  * @class Roo.SplitButton
30707  * @extends Roo.Button
30708  * A split button that provides a built-in dropdown arrow that can fire an event separately from the default
30709  * click event of the button.  Typically this would be used to display a dropdown menu that provides additional
30710  * options to the primary button action, but any custom handler can provide the arrowclick implementation.
30711  * @cfg {Function} arrowHandler A function called when the arrow button is clicked (can be used instead of click event)
30712  * @cfg {String} arrowTooltip The title attribute of the arrow
30713  * @constructor
30714  * Create a new menu button
30715  * @param {String/HTMLElement/Element} renderTo The element to append the button to
30716  * @param {Object} config The config object
30717  */
30718 Roo.SplitButton = function(renderTo, config){
30719     Roo.SplitButton.superclass.constructor.call(this, renderTo, config);
30720     /**
30721      * @event arrowclick
30722      * Fires when this button's arrow is clicked
30723      * @param {SplitButton} this
30724      * @param {EventObject} e The click event
30725      */
30726     this.addEvents({"arrowclick":true});
30727 };
30728
30729 Roo.extend(Roo.SplitButton, Roo.Button, {
30730     render : function(renderTo){
30731         // this is one sweet looking template!
30732         var tpl = new Roo.Template(
30733             '<table cellspacing="0" class="x-btn-menu-wrap x-btn"><tr><td>',
30734             '<table cellspacing="0" class="x-btn-wrap x-btn-menu-text-wrap"><tbody>',
30735             '<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>',
30736             "</tbody></table></td><td>",
30737             '<table cellspacing="0" class="x-btn-wrap x-btn-menu-arrow-wrap"><tbody>',
30738             '<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>',
30739             "</tbody></table></td></tr></table>"
30740         );
30741         var btn = tpl.append(renderTo, [this.text, this.type], true);
30742         var btnEl = btn.child("button");
30743         if(this.cls){
30744             btn.addClass(this.cls);
30745         }
30746         if(this.icon){
30747             btnEl.setStyle('background-image', 'url(' +this.icon +')');
30748         }
30749         if(this.iconCls){
30750             btnEl.addClass(this.iconCls);
30751             if(!this.cls){
30752                 btn.addClass(this.text ? 'x-btn-text-icon' : 'x-btn-icon');
30753             }
30754         }
30755         this.el = btn;
30756         if(this.handleMouseEvents){
30757             btn.on("mouseover", this.onMouseOver, this);
30758             btn.on("mouseout", this.onMouseOut, this);
30759             btn.on("mousedown", this.onMouseDown, this);
30760             btn.on("mouseup", this.onMouseUp, this);
30761         }
30762         btn.on(this.clickEvent, this.onClick, this);
30763         if(this.tooltip){
30764             if(typeof this.tooltip == 'object'){
30765                 Roo.QuickTips.tips(Roo.apply({
30766                       target: btnEl.id
30767                 }, this.tooltip));
30768             } else {
30769                 btnEl.dom[this.tooltipType] = this.tooltip;
30770             }
30771         }
30772         if(this.arrowTooltip){
30773             btn.child("button:nth(2)").dom[this.tooltipType] = this.arrowTooltip;
30774         }
30775         if(this.hidden){
30776             this.hide();
30777         }
30778         if(this.disabled){
30779             this.disable();
30780         }
30781         if(this.pressed){
30782             this.el.addClass("x-btn-pressed");
30783         }
30784         if(Roo.isIE && !Roo.isIE7){
30785             this.autoWidth.defer(1, this);
30786         }else{
30787             this.autoWidth();
30788         }
30789         if(this.menu){
30790             this.menu.on("show", this.onMenuShow, this);
30791             this.menu.on("hide", this.onMenuHide, this);
30792         }
30793         this.fireEvent('render', this);
30794     },
30795
30796     // private
30797     autoWidth : function(){
30798         if(this.el){
30799             var tbl = this.el.child("table:first");
30800             var tbl2 = this.el.child("table:last");
30801             this.el.setWidth("auto");
30802             tbl.setWidth("auto");
30803             if(Roo.isIE7 && Roo.isStrict){
30804                 var ib = this.el.child('button:first');
30805                 if(ib && ib.getWidth() > 20){
30806                     ib.clip();
30807                     ib.setWidth(Roo.util.TextMetrics.measure(ib, this.text).width+ib.getFrameWidth('lr'));
30808                 }
30809             }
30810             if(this.minWidth){
30811                 if(this.hidden){
30812                     this.el.beginMeasure();
30813                 }
30814                 if((tbl.getWidth()+tbl2.getWidth()) < this.minWidth){
30815                     tbl.setWidth(this.minWidth-tbl2.getWidth());
30816                 }
30817                 if(this.hidden){
30818                     this.el.endMeasure();
30819                 }
30820             }
30821             this.el.setWidth(tbl.getWidth()+tbl2.getWidth());
30822         } 
30823     },
30824     /**
30825      * Sets this button's click handler
30826      * @param {Function} handler The function to call when the button is clicked
30827      * @param {Object} scope (optional) Scope for the function passed above
30828      */
30829     setHandler : function(handler, scope){
30830         this.handler = handler;
30831         this.scope = scope;  
30832     },
30833     
30834     /**
30835      * Sets this button's arrow click handler
30836      * @param {Function} handler The function to call when the arrow is clicked
30837      * @param {Object} scope (optional) Scope for the function passed above
30838      */
30839     setArrowHandler : function(handler, scope){
30840         this.arrowHandler = handler;
30841         this.scope = scope;  
30842     },
30843     
30844     /**
30845      * Focus the button
30846      */
30847     focus : function(){
30848         if(this.el){
30849             this.el.child("button:first").focus();
30850         }
30851     },
30852
30853     // private
30854     onClick : function(e){
30855         e.preventDefault();
30856         if(!this.disabled){
30857             if(e.getTarget(".x-btn-menu-arrow-wrap")){
30858                 if(this.menu && !this.menu.isVisible()){
30859                     this.menu.show(this.el, this.menuAlign);
30860                 }
30861                 this.fireEvent("arrowclick", this, e);
30862                 if(this.arrowHandler){
30863                     this.arrowHandler.call(this.scope || this, this, e);
30864                 }
30865             }else{
30866                 this.fireEvent("click", this, e);
30867                 if(this.handler){
30868                     this.handler.call(this.scope || this, this, e);
30869                 }
30870             }
30871         }
30872     },
30873     // private
30874     onMouseDown : function(e){
30875         if(!this.disabled){
30876             Roo.fly(e.getTarget("table")).addClass("x-btn-click");
30877         }
30878     },
30879     // private
30880     onMouseUp : function(e){
30881         Roo.fly(e.getTarget("table")).removeClass("x-btn-click");
30882     }   
30883 });
30884
30885
30886 // backwards compat
30887 Roo.MenuButton = Roo.SplitButton;/*
30888  * Based on:
30889  * Ext JS Library 1.1.1
30890  * Copyright(c) 2006-2007, Ext JS, LLC.
30891  *
30892  * Originally Released Under LGPL - original licence link has changed is not relivant.
30893  *
30894  * Fork - LGPL
30895  * <script type="text/javascript">
30896  */
30897
30898 /**
30899  * @class Roo.Toolbar
30900  * @children   Roo.Toolbar.Item Roo.form.Field
30901  * Basic Toolbar class.
30902  * @constructor
30903  * Creates a new Toolbar
30904  * @param {Object} container The config object
30905  */ 
30906 Roo.Toolbar = function(container, buttons, config)
30907 {
30908     /// old consturctor format still supported..
30909     if(container instanceof Array){ // omit the container for later rendering
30910         buttons = container;
30911         config = buttons;
30912         container = null;
30913     }
30914     if (typeof(container) == 'object' && container.xtype) {
30915         config = container;
30916         container = config.container;
30917         buttons = config.buttons || []; // not really - use items!!
30918     }
30919     var xitems = [];
30920     if (config && config.items) {
30921         xitems = config.items;
30922         delete config.items;
30923     }
30924     Roo.apply(this, config);
30925     this.buttons = buttons;
30926     
30927     if(container){
30928         this.render(container);
30929     }
30930     this.xitems = xitems;
30931     Roo.each(xitems, function(b) {
30932         this.add(b);
30933     }, this);
30934     
30935 };
30936
30937 Roo.Toolbar.prototype = {
30938     /**
30939      * @cfg {Array} items
30940      * array of button configs or elements to add (will be converted to a MixedCollection)
30941      */
30942     items: false,
30943     /**
30944      * @cfg {String/HTMLElement/Element} container
30945      * The id or element that will contain the toolbar
30946      */
30947     // private
30948     render : function(ct){
30949         this.el = Roo.get(ct);
30950         if(this.cls){
30951             this.el.addClass(this.cls);
30952         }
30953         // using a table allows for vertical alignment
30954         // 100% width is needed by Safari...
30955         this.el.update('<div class="x-toolbar x-small-editor"><table cellspacing="0"><tr></tr></table></div>');
30956         this.tr = this.el.child("tr", true);
30957         var autoId = 0;
30958         this.items = new Roo.util.MixedCollection(false, function(o){
30959             return o.id || ("item" + (++autoId));
30960         });
30961         if(this.buttons){
30962             this.add.apply(this, this.buttons);
30963             delete this.buttons;
30964         }
30965     },
30966
30967     /**
30968      * Adds element(s) to the toolbar -- this function takes a variable number of 
30969      * arguments of mixed type and adds them to the toolbar.
30970      * @param {Mixed} arg1 The following types of arguments are all valid:<br />
30971      * <ul>
30972      * <li>{@link Roo.Toolbar.Button} config: A valid button config object (equivalent to {@link #addButton})</li>
30973      * <li>HtmlElement: Any standard HTML element (equivalent to {@link #addElement})</li>
30974      * <li>Field: Any form field (equivalent to {@link #addField})</li>
30975      * <li>Item: Any subclass of {@link Roo.Toolbar.Item} (equivalent to {@link #addItem})</li>
30976      * <li>String: Any generic string (gets wrapped in a {@link Roo.Toolbar.TextItem}, equivalent to {@link #addText}).
30977      * Note that there are a few special strings that are treated differently as explained nRoo.</li>
30978      * <li>'separator' or '-': Creates a separator element (equivalent to {@link #addSeparator})</li>
30979      * <li>' ': Creates a spacer element (equivalent to {@link #addSpacer})</li>
30980      * <li>'->': Creates a fill element (equivalent to {@link #addFill})</li>
30981      * </ul>
30982      * @param {Mixed} arg2
30983      * @param {Mixed} etc.
30984      */
30985     add : function(){
30986         var a = arguments, l = a.length;
30987         for(var i = 0; i < l; i++){
30988             this._add(a[i]);
30989         }
30990     },
30991     // private..
30992     _add : function(el) {
30993         
30994         if (el.xtype) {
30995             el = Roo.factory(el, typeof(Roo.Toolbar[el.xtype]) == 'undefined' ? Roo.form : Roo.Toolbar);
30996         }
30997         
30998         if (el.applyTo){ // some kind of form field
30999             return this.addField(el);
31000         } 
31001         if (el.render){ // some kind of Toolbar.Item
31002             return this.addItem(el);
31003         }
31004         if (typeof el == "string"){ // string
31005             if(el == "separator" || el == "-"){
31006                 return this.addSeparator();
31007             }
31008             if (el == " "){
31009                 return this.addSpacer();
31010             }
31011             if(el == "->"){
31012                 return this.addFill();
31013             }
31014             return this.addText(el);
31015             
31016         }
31017         if(el.tagName){ // element
31018             return this.addElement(el);
31019         }
31020         if(typeof el == "object"){ // must be button config?
31021             return this.addButton(el);
31022         }
31023         // and now what?!?!
31024         return false;
31025         
31026     },
31027     
31028     /**
31029      * Add an Xtype element
31030      * @param {Object} xtype Xtype Object
31031      * @return {Object} created Object
31032      */
31033     addxtype : function(e){
31034         return this.add(e);  
31035     },
31036     
31037     /**
31038      * Returns the Element for this toolbar.
31039      * @return {Roo.Element}
31040      */
31041     getEl : function(){
31042         return this.el;  
31043     },
31044     
31045     /**
31046      * Adds a separator
31047      * @return {Roo.Toolbar.Item} The separator item
31048      */
31049     addSeparator : function(){
31050         return this.addItem(new Roo.Toolbar.Separator());
31051     },
31052
31053     /**
31054      * Adds a spacer element
31055      * @return {Roo.Toolbar.Spacer} The spacer item
31056      */
31057     addSpacer : function(){
31058         return this.addItem(new Roo.Toolbar.Spacer());
31059     },
31060
31061     /**
31062      * Adds a fill element that forces subsequent additions to the right side of the toolbar
31063      * @return {Roo.Toolbar.Fill} The fill item
31064      */
31065     addFill : function(){
31066         return this.addItem(new Roo.Toolbar.Fill());
31067     },
31068
31069     /**
31070      * Adds any standard HTML element to the toolbar
31071      * @param {String/HTMLElement/Element} el The element or id of the element to add
31072      * @return {Roo.Toolbar.Item} The element's item
31073      */
31074     addElement : function(el){
31075         return this.addItem(new Roo.Toolbar.Item(el));
31076     },
31077     /**
31078      * Collection of items on the toolbar.. (only Toolbar Items, so use fields to retrieve fields)
31079      * @type Roo.util.MixedCollection  
31080      */
31081     items : false,
31082      
31083     /**
31084      * Adds any Toolbar.Item or subclass
31085      * @param {Roo.Toolbar.Item} item
31086      * @return {Roo.Toolbar.Item} The item
31087      */
31088     addItem : function(item){
31089         var td = this.nextBlock();
31090         item.render(td);
31091         this.items.add(item);
31092         return item;
31093     },
31094     
31095     /**
31096      * Adds a button (or buttons). See {@link Roo.Toolbar.Button} for more info on the config.
31097      * @param {Object/Array} config A button config or array of configs
31098      * @return {Roo.Toolbar.Button/Array}
31099      */
31100     addButton : function(config){
31101         if(config instanceof Array){
31102             var buttons = [];
31103             for(var i = 0, len = config.length; i < len; i++) {
31104                 buttons.push(this.addButton(config[i]));
31105             }
31106             return buttons;
31107         }
31108         var b = config;
31109         if(!(config instanceof Roo.Toolbar.Button)){
31110             b = config.split ?
31111                 new Roo.Toolbar.SplitButton(config) :
31112                 new Roo.Toolbar.Button(config);
31113         }
31114         var td = this.nextBlock();
31115         b.render(td);
31116         this.items.add(b);
31117         return b;
31118     },
31119     
31120     /**
31121      * Adds text to the toolbar
31122      * @param {String} text The text to add
31123      * @return {Roo.Toolbar.Item} The element's item
31124      */
31125     addText : function(text){
31126         return this.addItem(new Roo.Toolbar.TextItem(text));
31127     },
31128     
31129     /**
31130      * Inserts any {@link Roo.Toolbar.Item}/{@link Roo.Toolbar.Button} at the specified index.
31131      * @param {Number} index The index where the item is to be inserted
31132      * @param {Object/Roo.Toolbar.Item/Roo.Toolbar.Button (may be Array)} item The button, or button config object to be inserted.
31133      * @return {Roo.Toolbar.Button/Item}
31134      */
31135     insertButton : function(index, item){
31136         if(item instanceof Array){
31137             var buttons = [];
31138             for(var i = 0, len = item.length; i < len; i++) {
31139                buttons.push(this.insertButton(index + i, item[i]));
31140             }
31141             return buttons;
31142         }
31143         if (!(item instanceof Roo.Toolbar.Button)){
31144            item = new Roo.Toolbar.Button(item);
31145         }
31146         var td = document.createElement("td");
31147         this.tr.insertBefore(td, this.tr.childNodes[index]);
31148         item.render(td);
31149         this.items.insert(index, item);
31150         return item;
31151     },
31152     
31153     /**
31154      * Adds a new element to the toolbar from the passed {@link Roo.DomHelper} config.
31155      * @param {Object} config
31156      * @return {Roo.Toolbar.Item} The element's item
31157      */
31158     addDom : function(config, returnEl){
31159         var td = this.nextBlock();
31160         Roo.DomHelper.overwrite(td, config);
31161         var ti = new Roo.Toolbar.Item(td.firstChild);
31162         ti.render(td);
31163         this.items.add(ti);
31164         return ti;
31165     },
31166
31167     /**
31168      * Collection of fields on the toolbar.. usefull for quering (value is false if there are no fields)
31169      * @type Roo.util.MixedCollection  
31170      */
31171     fields : false,
31172     
31173     /**
31174      * Adds a dynamically rendered Roo.form field (TextField, ComboBox, etc).
31175      * Note: the field should not have been rendered yet. For a field that has already been
31176      * rendered, use {@link #addElement}.
31177      * @param {Roo.form.Field} field
31178      * @return {Roo.ToolbarItem}
31179      */
31180      
31181       
31182     addField : function(field) {
31183         if (!this.fields) {
31184             var autoId = 0;
31185             this.fields = new Roo.util.MixedCollection(false, function(o){
31186                 return o.id || ("item" + (++autoId));
31187             });
31188
31189         }
31190         
31191         var td = this.nextBlock();
31192         field.render(td);
31193         var ti = new Roo.Toolbar.Item(td.firstChild);
31194         ti.render(td);
31195         this.items.add(ti);
31196         this.fields.add(field);
31197         return ti;
31198     },
31199     /**
31200      * Hide the toolbar
31201      * @method hide
31202      */
31203      
31204       
31205     hide : function()
31206     {
31207         this.el.child('div').setVisibilityMode(Roo.Element.DISPLAY);
31208         this.el.child('div').hide();
31209     },
31210     /**
31211      * Show the toolbar
31212      * @method show
31213      */
31214     show : function()
31215     {
31216         this.el.child('div').show();
31217     },
31218       
31219     // private
31220     nextBlock : function(){
31221         var td = document.createElement("td");
31222         this.tr.appendChild(td);
31223         return td;
31224     },
31225
31226     // private
31227     destroy : function(){
31228         if(this.items){ // rendered?
31229             Roo.destroy.apply(Roo, this.items.items);
31230         }
31231         if(this.fields){ // rendered?
31232             Roo.destroy.apply(Roo, this.fields.items);
31233         }
31234         Roo.Element.uncache(this.el, this.tr);
31235     }
31236 };
31237
31238 /**
31239  * @class Roo.Toolbar.Item
31240  * The base class that other classes should extend in order to get some basic common toolbar item functionality.
31241  * @constructor
31242  * Creates a new Item
31243  * @param {HTMLElement} el 
31244  */
31245 Roo.Toolbar.Item = function(el){
31246     var cfg = {};
31247     if (typeof (el.xtype) != 'undefined') {
31248         cfg = el;
31249         el = cfg.el;
31250     }
31251     
31252     this.el = Roo.getDom(el);
31253     this.id = Roo.id(this.el);
31254     this.hidden = false;
31255     
31256     this.addEvents({
31257          /**
31258              * @event render
31259              * Fires when the button is rendered
31260              * @param {Button} this
31261              */
31262         'render': true
31263     });
31264     Roo.Toolbar.Item.superclass.constructor.call(this,cfg);
31265 };
31266 Roo.extend(Roo.Toolbar.Item, Roo.util.Observable, {
31267 //Roo.Toolbar.Item.prototype = {
31268     
31269     /**
31270      * Get this item's HTML Element
31271      * @return {HTMLElement}
31272      */
31273     getEl : function(){
31274        return this.el;  
31275     },
31276
31277     // private
31278     render : function(td){
31279         
31280          this.td = td;
31281         td.appendChild(this.el);
31282         
31283         this.fireEvent('render', this);
31284     },
31285     
31286     /**
31287      * Removes and destroys this item.
31288      */
31289     destroy : function(){
31290         this.td.parentNode.removeChild(this.td);
31291     },
31292     
31293     /**
31294      * Shows this item.
31295      */
31296     show: function(){
31297         this.hidden = false;
31298         this.td.style.display = "";
31299     },
31300     
31301     /**
31302      * Hides this item.
31303      */
31304     hide: function(){
31305         this.hidden = true;
31306         this.td.style.display = "none";
31307     },
31308     
31309     /**
31310      * Convenience function for boolean show/hide.
31311      * @param {Boolean} visible true to show/false to hide
31312      */
31313     setVisible: function(visible){
31314         if(visible) {
31315             this.show();
31316         }else{
31317             this.hide();
31318         }
31319     },
31320     
31321     /**
31322      * Try to focus this item.
31323      */
31324     focus : function(){
31325         Roo.fly(this.el).focus();
31326     },
31327     
31328     /**
31329      * Disables this item.
31330      */
31331     disable : function(){
31332         Roo.fly(this.td).addClass("x-item-disabled");
31333         this.disabled = true;
31334         this.el.disabled = true;
31335     },
31336     
31337     /**
31338      * Enables this item.
31339      */
31340     enable : function(){
31341         Roo.fly(this.td).removeClass("x-item-disabled");
31342         this.disabled = false;
31343         this.el.disabled = false;
31344     }
31345 });
31346
31347
31348 /**
31349  * @class Roo.Toolbar.Separator
31350  * @extends Roo.Toolbar.Item
31351  * A simple toolbar separator class
31352  * @constructor
31353  * Creates a new Separator
31354  */
31355 Roo.Toolbar.Separator = function(cfg){
31356     
31357     var s = document.createElement("span");
31358     s.className = "ytb-sep";
31359     if (cfg) {
31360         cfg.el = s;
31361     }
31362     
31363     Roo.Toolbar.Separator.superclass.constructor.call(this, cfg || s);
31364 };
31365 Roo.extend(Roo.Toolbar.Separator, Roo.Toolbar.Item, {
31366     enable:Roo.emptyFn,
31367     disable:Roo.emptyFn,
31368     focus:Roo.emptyFn
31369 });
31370
31371 /**
31372  * @class Roo.Toolbar.Spacer
31373  * @extends Roo.Toolbar.Item
31374  * A simple element that adds extra horizontal space to a toolbar.
31375  * @constructor
31376  * Creates a new Spacer
31377  */
31378 Roo.Toolbar.Spacer = function(cfg){
31379     var s = document.createElement("div");
31380     s.className = "ytb-spacer";
31381     if (cfg) {
31382         cfg.el = s;
31383     }
31384     Roo.Toolbar.Spacer.superclass.constructor.call(this, cfg || s);
31385 };
31386 Roo.extend(Roo.Toolbar.Spacer, Roo.Toolbar.Item, {
31387     enable:Roo.emptyFn,
31388     disable:Roo.emptyFn,
31389     focus:Roo.emptyFn
31390 });
31391
31392 /**
31393  * @class Roo.Toolbar.Fill
31394  * @extends Roo.Toolbar.Spacer
31395  * A simple element that adds a greedy (100% width) horizontal space to a toolbar.
31396  * @constructor
31397  * Creates a new Spacer
31398  */
31399 Roo.Toolbar.Fill = Roo.extend(Roo.Toolbar.Spacer, {
31400     // private
31401     render : function(td){
31402         td.style.width = '100%';
31403         Roo.Toolbar.Fill.superclass.render.call(this, td);
31404     }
31405 });
31406
31407 /**
31408  * @class Roo.Toolbar.TextItem
31409  * @extends Roo.Toolbar.Item
31410  * A simple class that renders text directly into a toolbar.
31411  * @constructor
31412  * Creates a new TextItem
31413  * @cfg {string} text 
31414  */
31415 Roo.Toolbar.TextItem = function(cfg){
31416     var  text = cfg || "";
31417     if (typeof(cfg) == 'object') {
31418         text = cfg.text || "";
31419     }  else {
31420         cfg = null;
31421     }
31422     var s = document.createElement("span");
31423     s.className = "ytb-text";
31424     s.innerHTML = text;
31425     if (cfg) {
31426         cfg.el  = s;
31427     }
31428     
31429     Roo.Toolbar.TextItem.superclass.constructor.call(this, cfg ||  s);
31430 };
31431 Roo.extend(Roo.Toolbar.TextItem, Roo.Toolbar.Item, {
31432     
31433      
31434     enable:Roo.emptyFn,
31435     disable:Roo.emptyFn,
31436     focus:Roo.emptyFn
31437 });
31438
31439 /**
31440  * @class Roo.Toolbar.Button
31441  * @extends Roo.Button
31442  * A button that renders into a toolbar.
31443  * @constructor
31444  * Creates a new Button
31445  * @param {Object} config A standard {@link Roo.Button} config object
31446  */
31447 Roo.Toolbar.Button = function(config){
31448     Roo.Toolbar.Button.superclass.constructor.call(this, null, config);
31449 };
31450 Roo.extend(Roo.Toolbar.Button, Roo.Button,
31451 {
31452     
31453     
31454     render : function(td){
31455         this.td = td;
31456         Roo.Toolbar.Button.superclass.render.call(this, td);
31457     },
31458     
31459     /**
31460      * Removes and destroys this button
31461      */
31462     destroy : function(){
31463         Roo.Toolbar.Button.superclass.destroy.call(this);
31464         this.td.parentNode.removeChild(this.td);
31465     },
31466     
31467     /**
31468      * Shows this button
31469      */
31470     show: function(){
31471         this.hidden = false;
31472         this.td.style.display = "";
31473     },
31474     
31475     /**
31476      * Hides this button
31477      */
31478     hide: function(){
31479         this.hidden = true;
31480         this.td.style.display = "none";
31481     },
31482
31483     /**
31484      * Disables this item
31485      */
31486     disable : function(){
31487         Roo.fly(this.td).addClass("x-item-disabled");
31488         this.disabled = true;
31489     },
31490
31491     /**
31492      * Enables this item
31493      */
31494     enable : function(){
31495         Roo.fly(this.td).removeClass("x-item-disabled");
31496         this.disabled = false;
31497     }
31498 });
31499 // backwards compat
31500 Roo.ToolbarButton = Roo.Toolbar.Button;
31501
31502 /**
31503  * @class Roo.Toolbar.SplitButton
31504  * @extends Roo.SplitButton
31505  * A menu button that renders into a toolbar.
31506  * @constructor
31507  * Creates a new SplitButton
31508  * @param {Object} config A standard {@link Roo.SplitButton} config object
31509  */
31510 Roo.Toolbar.SplitButton = function(config){
31511     Roo.Toolbar.SplitButton.superclass.constructor.call(this, null, config);
31512 };
31513 Roo.extend(Roo.Toolbar.SplitButton, Roo.SplitButton, {
31514     render : function(td){
31515         this.td = td;
31516         Roo.Toolbar.SplitButton.superclass.render.call(this, td);
31517     },
31518     
31519     /**
31520      * Removes and destroys this button
31521      */
31522     destroy : function(){
31523         Roo.Toolbar.SplitButton.superclass.destroy.call(this);
31524         this.td.parentNode.removeChild(this.td);
31525     },
31526     
31527     /**
31528      * Shows this button
31529      */
31530     show: function(){
31531         this.hidden = false;
31532         this.td.style.display = "";
31533     },
31534     
31535     /**
31536      * Hides this button
31537      */
31538     hide: function(){
31539         this.hidden = true;
31540         this.td.style.display = "none";
31541     }
31542 });
31543
31544 // backwards compat
31545 Roo.Toolbar.MenuButton = Roo.Toolbar.SplitButton;/*
31546  * Based on:
31547  * Ext JS Library 1.1.1
31548  * Copyright(c) 2006-2007, Ext JS, LLC.
31549  *
31550  * Originally Released Under LGPL - original licence link has changed is not relivant.
31551  *
31552  * Fork - LGPL
31553  * <script type="text/javascript">
31554  */
31555  
31556 /**
31557  * @class Roo.PagingToolbar
31558  * @extends Roo.Toolbar
31559  * @children   Roo.Toolbar.Item Roo.form.Field
31560  * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
31561  * @constructor
31562  * Create a new PagingToolbar
31563  * @param {Object} config The config object
31564  */
31565 Roo.PagingToolbar = function(el, ds, config)
31566 {
31567     // old args format still supported... - xtype is prefered..
31568     if (typeof(el) == 'object' && el.xtype) {
31569         // created from xtype...
31570         config = el;
31571         ds = el.dataSource;
31572         el = config.container;
31573     }
31574     var items = [];
31575     if (config.items) {
31576         items = config.items;
31577         config.items = [];
31578     }
31579     
31580     Roo.PagingToolbar.superclass.constructor.call(this, el, null, config);
31581     this.ds = ds;
31582     this.cursor = 0;
31583     this.renderButtons(this.el);
31584     this.bind(ds);
31585     
31586     // supprot items array.
31587    
31588     Roo.each(items, function(e) {
31589         this.add(Roo.factory(e));
31590     },this);
31591     
31592 };
31593
31594 Roo.extend(Roo.PagingToolbar, Roo.Toolbar, {
31595    
31596     /**
31597      * @cfg {String/HTMLElement/Element} container
31598      * container The id or element that will contain the toolbar
31599      */
31600     /**
31601      * @cfg {Boolean} displayInfo
31602      * True to display the displayMsg (defaults to false)
31603      */
31604     
31605     
31606     /**
31607      * @cfg {Number} pageSize
31608      * The number of records to display per page (defaults to 20)
31609      */
31610     pageSize: 20,
31611     /**
31612      * @cfg {String} displayMsg
31613      * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
31614      */
31615     displayMsg : 'Displaying {0} - {1} of {2}',
31616     /**
31617      * @cfg {String} emptyMsg
31618      * The message to display when no records are found (defaults to "No data to display")
31619      */
31620     emptyMsg : 'No data to display',
31621     /**
31622      * Customizable piece of the default paging text (defaults to "Page")
31623      * @type String
31624      */
31625     beforePageText : "Page",
31626     /**
31627      * Customizable piece of the default paging text (defaults to "of %0")
31628      * @type String
31629      */
31630     afterPageText : "of {0}",
31631     /**
31632      * Customizable piece of the default paging text (defaults to "First Page")
31633      * @type String
31634      */
31635     firstText : "First Page",
31636     /**
31637      * Customizable piece of the default paging text (defaults to "Previous Page")
31638      * @type String
31639      */
31640     prevText : "Previous Page",
31641     /**
31642      * Customizable piece of the default paging text (defaults to "Next Page")
31643      * @type String
31644      */
31645     nextText : "Next Page",
31646     /**
31647      * Customizable piece of the default paging text (defaults to "Last Page")
31648      * @type String
31649      */
31650     lastText : "Last Page",
31651     /**
31652      * Customizable piece of the default paging text (defaults to "Refresh")
31653      * @type String
31654      */
31655     refreshText : "Refresh",
31656
31657     // private
31658     renderButtons : function(el){
31659         Roo.PagingToolbar.superclass.render.call(this, el);
31660         this.first = this.addButton({
31661             tooltip: this.firstText,
31662             cls: "x-btn-icon x-grid-page-first",
31663             disabled: true,
31664             handler: this.onClick.createDelegate(this, ["first"])
31665         });
31666         this.prev = this.addButton({
31667             tooltip: this.prevText,
31668             cls: "x-btn-icon x-grid-page-prev",
31669             disabled: true,
31670             handler: this.onClick.createDelegate(this, ["prev"])
31671         });
31672         //this.addSeparator();
31673         this.add(this.beforePageText);
31674         this.field = Roo.get(this.addDom({
31675            tag: "input",
31676            type: "text",
31677            size: "3",
31678            value: "1",
31679            cls: "x-grid-page-number"
31680         }).el);
31681         this.field.on("keydown", this.onPagingKeydown, this);
31682         this.field.on("focus", function(){this.dom.select();});
31683         this.afterTextEl = this.addText(String.format(this.afterPageText, 1));
31684         this.field.setHeight(18);
31685         //this.addSeparator();
31686         this.next = this.addButton({
31687             tooltip: this.nextText,
31688             cls: "x-btn-icon x-grid-page-next",
31689             disabled: true,
31690             handler: this.onClick.createDelegate(this, ["next"])
31691         });
31692         this.last = this.addButton({
31693             tooltip: this.lastText,
31694             cls: "x-btn-icon x-grid-page-last",
31695             disabled: true,
31696             handler: this.onClick.createDelegate(this, ["last"])
31697         });
31698         //this.addSeparator();
31699         this.loading = this.addButton({
31700             tooltip: this.refreshText,
31701             cls: "x-btn-icon x-grid-loading",
31702             handler: this.onClick.createDelegate(this, ["refresh"])
31703         });
31704
31705         if(this.displayInfo){
31706             this.displayEl = Roo.fly(this.el.dom.firstChild).createChild({cls:'x-paging-info'});
31707         }
31708     },
31709
31710     // private
31711     updateInfo : function(){
31712         if(this.displayEl){
31713             var count = this.ds.getCount();
31714             var msg = count == 0 ?
31715                 this.emptyMsg :
31716                 String.format(
31717                     this.displayMsg,
31718                     this.cursor+1, this.cursor+count, this.ds.getTotalCount()    
31719                 );
31720             this.displayEl.update(msg);
31721         }
31722     },
31723
31724     // private
31725     onLoad : function(ds, r, o){
31726        this.cursor = o.params ? o.params.start : 0;
31727        var d = this.getPageData(), ap = d.activePage, ps = d.pages;
31728
31729        this.afterTextEl.el.innerHTML = String.format(this.afterPageText, d.pages);
31730        this.field.dom.value = ap;
31731        this.first.setDisabled(ap == 1);
31732        this.prev.setDisabled(ap == 1);
31733        this.next.setDisabled(ap == ps);
31734        this.last.setDisabled(ap == ps);
31735        this.loading.enable();
31736        this.updateInfo();
31737     },
31738
31739     // private
31740     getPageData : function(){
31741         var total = this.ds.getTotalCount();
31742         return {
31743             total : total,
31744             activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
31745             pages :  total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
31746         };
31747     },
31748
31749     // private
31750     onLoadError : function(){
31751         this.loading.enable();
31752     },
31753
31754     // private
31755     onPagingKeydown : function(e){
31756         var k = e.getKey();
31757         var d = this.getPageData();
31758         if(k == e.RETURN){
31759             var v = this.field.dom.value, pageNum;
31760             if(!v || isNaN(pageNum = parseInt(v, 10))){
31761                 this.field.dom.value = d.activePage;
31762                 return;
31763             }
31764             pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
31765             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
31766             e.stopEvent();
31767         }
31768         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))
31769         {
31770           var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
31771           this.field.dom.value = pageNum;
31772           this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
31773           e.stopEvent();
31774         }
31775         else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
31776         {
31777           var v = this.field.dom.value, pageNum; 
31778           var increment = (e.shiftKey) ? 10 : 1;
31779           if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN) {
31780             increment *= -1;
31781           }
31782           if(!v || isNaN(pageNum = parseInt(v, 10))) {
31783             this.field.dom.value = d.activePage;
31784             return;
31785           }
31786           else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
31787           {
31788             this.field.dom.value = parseInt(v, 10) + increment;
31789             pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
31790             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
31791           }
31792           e.stopEvent();
31793         }
31794     },
31795
31796     // private
31797     beforeLoad : function(){
31798         if(this.loading){
31799             this.loading.disable();
31800         }
31801     },
31802
31803     // private
31804     onClick : function(which){
31805         var ds = this.ds;
31806         switch(which){
31807             case "first":
31808                 ds.load({params:{start: 0, limit: this.pageSize}});
31809             break;
31810             case "prev":
31811                 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
31812             break;
31813             case "next":
31814                 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
31815             break;
31816             case "last":
31817                 var total = ds.getTotalCount();
31818                 var extra = total % this.pageSize;
31819                 var lastStart = extra ? (total - extra) : total-this.pageSize;
31820                 ds.load({params:{start: lastStart, limit: this.pageSize}});
31821             break;
31822             case "refresh":
31823                 ds.load({params:{start: this.cursor, limit: this.pageSize}});
31824             break;
31825         }
31826     },
31827
31828     /**
31829      * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
31830      * @param {Roo.data.Store} store The data store to unbind
31831      */
31832     unbind : function(ds){
31833         ds.un("beforeload", this.beforeLoad, this);
31834         ds.un("load", this.onLoad, this);
31835         ds.un("loadexception", this.onLoadError, this);
31836         ds.un("remove", this.updateInfo, this);
31837         ds.un("add", this.updateInfo, this);
31838         this.ds = undefined;
31839     },
31840
31841     /**
31842      * Binds the paging toolbar to the specified {@link Roo.data.Store}
31843      * @param {Roo.data.Store} store The data store to bind
31844      */
31845     bind : function(ds){
31846         ds.on("beforeload", this.beforeLoad, this);
31847         ds.on("load", this.onLoad, this);
31848         ds.on("loadexception", this.onLoadError, this);
31849         ds.on("remove", this.updateInfo, this);
31850         ds.on("add", this.updateInfo, this);
31851         this.ds = ds;
31852     }
31853 });/*
31854  * Based on:
31855  * Ext JS Library 1.1.1
31856  * Copyright(c) 2006-2007, Ext JS, LLC.
31857  *
31858  * Originally Released Under LGPL - original licence link has changed is not relivant.
31859  *
31860  * Fork - LGPL
31861  * <script type="text/javascript">
31862  */
31863
31864 /**
31865  * @class Roo.Resizable
31866  * @extends Roo.util.Observable
31867  * <p>Applies drag handles to an element to make it resizable. The drag handles are inserted into the element
31868  * and positioned absolute. Some elements, such as a textarea or image, don't support this. To overcome that, you can wrap
31869  * 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
31870  * the element will be wrapped for you automatically.</p>
31871  * <p>Here is the list of valid resize handles:</p>
31872  * <pre>
31873 Value   Description
31874 ------  -------------------
31875  'n'     north
31876  's'     south
31877  'e'     east
31878  'w'     west
31879  'nw'    northwest
31880  'sw'    southwest
31881  'se'    southeast
31882  'ne'    northeast
31883  'hd'    horizontal drag
31884  'all'   all
31885 </pre>
31886  * <p>Here's an example showing the creation of a typical Resizable:</p>
31887  * <pre><code>
31888 var resizer = new Roo.Resizable("element-id", {
31889     handles: 'all',
31890     minWidth: 200,
31891     minHeight: 100,
31892     maxWidth: 500,
31893     maxHeight: 400,
31894     pinned: true
31895 });
31896 resizer.on("resize", myHandler);
31897 </code></pre>
31898  * <p>To hide a particular handle, set its display to none in CSS, or through script:<br>
31899  * resizer.east.setDisplayed(false);</p>
31900  * @cfg {Boolean/String/Element} resizeChild True to resize the first child, or id/element to resize (defaults to false)
31901  * @cfg {Array/String} adjustments String "auto" or an array [width, height] with values to be <b>added</b> to the
31902  * resize operation's new size (defaults to [0, 0])
31903  * @cfg {Number} minWidth The minimum width for the element (defaults to 5)
31904  * @cfg {Number} minHeight The minimum height for the element (defaults to 5)
31905  * @cfg {Number} maxWidth The maximum width for the element (defaults to 10000)
31906  * @cfg {Number} maxHeight The maximum height for the element (defaults to 10000)
31907  * @cfg {Boolean} enabled False to disable resizing (defaults to true)
31908  * @cfg {Boolean} wrap True to wrap an element with a div if needed (required for textareas and images, defaults to false)
31909  * @cfg {Number} width The width of the element in pixels (defaults to null)
31910  * @cfg {Number} height The height of the element in pixels (defaults to null)
31911  * @cfg {Boolean} animate True to animate the resize (not compatible with dynamic sizing, defaults to false)
31912  * @cfg {Number} duration Animation duration if animate = true (defaults to .35)
31913  * @cfg {Boolean} dynamic True to resize the element while dragging instead of using a proxy (defaults to false)
31914  * @cfg {String} handles String consisting of the resize handles to display (defaults to undefined)
31915  * @cfg {Boolean} multiDirectional <b>Deprecated</b>.  The old style of adding multi-direction resize handles, deprecated
31916  * in favor of the handles config option (defaults to false)
31917  * @cfg {Boolean} disableTrackOver True to disable mouse tracking. This is only applied at config time. (defaults to false)
31918  * @cfg {String} easing Animation easing if animate = true (defaults to 'easingOutStrong')
31919  * @cfg {Number} widthIncrement The increment to snap the width resize in pixels (dynamic must be true, defaults to 0)
31920  * @cfg {Number} heightIncrement The increment to snap the height resize in pixels (dynamic must be true, defaults to 0)
31921  * @cfg {Boolean} pinned True to ensure that the resize handles are always visible, false to display them only when the
31922  * user mouses over the resizable borders. This is only applied at config time. (defaults to false)
31923  * @cfg {Boolean} preserveRatio True to preserve the original ratio between height and width during resize (defaults to false)
31924  * @cfg {Boolean} transparent True for transparent handles. This is only applied at config time. (defaults to false)
31925  * @cfg {Number} minX The minimum allowed page X for the element (only used for west resizing, defaults to 0)
31926  * @cfg {Number} minY The minimum allowed page Y for the element (only used for north resizing, defaults to 0)
31927  * @cfg {Boolean} draggable Convenience to initialize drag drop (defaults to false)
31928  * @constructor
31929  * Create a new resizable component
31930  * @param {String/HTMLElement/Roo.Element} el The id or element to resize
31931  * @param {Object} config configuration options
31932   */
31933 Roo.Resizable = function(el, config)
31934 {
31935     this.el = Roo.get(el);
31936
31937     if(config && config.wrap){
31938         config.resizeChild = this.el;
31939         this.el = this.el.wrap(typeof config.wrap == "object" ? config.wrap : {cls:"xresizable-wrap"});
31940         this.el.id = this.el.dom.id = config.resizeChild.id + "-rzwrap";
31941         this.el.setStyle("overflow", "hidden");
31942         this.el.setPositioning(config.resizeChild.getPositioning());
31943         config.resizeChild.clearPositioning();
31944         if(!config.width || !config.height){
31945             var csize = config.resizeChild.getSize();
31946             this.el.setSize(csize.width, csize.height);
31947         }
31948         if(config.pinned && !config.adjustments){
31949             config.adjustments = "auto";
31950         }
31951     }
31952
31953     this.proxy = this.el.createProxy({tag: "div", cls: "x-resizable-proxy", id: this.el.id + "-rzproxy"});
31954     this.proxy.unselectable();
31955     this.proxy.enableDisplayMode('block');
31956
31957     Roo.apply(this, config);
31958
31959     if(this.pinned){
31960         this.disableTrackOver = true;
31961         this.el.addClass("x-resizable-pinned");
31962     }
31963     // if the element isn't positioned, make it relative
31964     var position = this.el.getStyle("position");
31965     if(position != "absolute" && position != "fixed"){
31966         this.el.setStyle("position", "relative");
31967     }
31968     if(!this.handles){ // no handles passed, must be legacy style
31969         this.handles = 's,e,se';
31970         if(this.multiDirectional){
31971             this.handles += ',n,w';
31972         }
31973     }
31974     if(this.handles == "all"){
31975         this.handles = "n s e w ne nw se sw";
31976     }
31977     var hs = this.handles.split(/\s*?[,;]\s*?| /);
31978     var ps = Roo.Resizable.positions;
31979     for(var i = 0, len = hs.length; i < len; i++){
31980         if(hs[i] && ps[hs[i]]){
31981             var pos = ps[hs[i]];
31982             this[pos] = new Roo.Resizable.Handle(this, pos, this.disableTrackOver, this.transparent);
31983         }
31984     }
31985     // legacy
31986     this.corner = this.southeast;
31987     
31988     // updateBox = the box can move..
31989     if(this.handles.indexOf("n") != -1 || this.handles.indexOf("w") != -1 || this.handles.indexOf("hd") != -1) {
31990         this.updateBox = true;
31991     }
31992
31993     this.activeHandle = null;
31994
31995     if(this.resizeChild){
31996         if(typeof this.resizeChild == "boolean"){
31997             this.resizeChild = Roo.get(this.el.dom.firstChild, true);
31998         }else{
31999             this.resizeChild = Roo.get(this.resizeChild, true);
32000         }
32001     }
32002     
32003     if(this.adjustments == "auto"){
32004         var rc = this.resizeChild;
32005         var hw = this.west, he = this.east, hn = this.north, hs = this.south;
32006         if(rc && (hw || hn)){
32007             rc.position("relative");
32008             rc.setLeft(hw ? hw.el.getWidth() : 0);
32009             rc.setTop(hn ? hn.el.getHeight() : 0);
32010         }
32011         this.adjustments = [
32012             (he ? -he.el.getWidth() : 0) + (hw ? -hw.el.getWidth() : 0),
32013             (hn ? -hn.el.getHeight() : 0) + (hs ? -hs.el.getHeight() : 0) -1
32014         ];
32015     }
32016
32017     if(this.draggable){
32018         this.dd = this.dynamic ?
32019             this.el.initDD(null) : this.el.initDDProxy(null, {dragElId: this.proxy.id});
32020         this.dd.setHandleElId(this.resizeChild ? this.resizeChild.id : this.el.id);
32021     }
32022
32023     // public events
32024     this.addEvents({
32025         /**
32026          * @event beforeresize
32027          * Fired before resize is allowed. Set enabled to false to cancel resize.
32028          * @param {Roo.Resizable} this
32029          * @param {Roo.EventObject} e The mousedown event
32030          */
32031         "beforeresize" : true,
32032         /**
32033          * @event resizing
32034          * Fired a resizing.
32035          * @param {Roo.Resizable} this
32036          * @param {Number} x The new x position
32037          * @param {Number} y The new y position
32038          * @param {Number} w The new w width
32039          * @param {Number} h The new h hight
32040          * @param {Roo.EventObject} e The mouseup event
32041          */
32042         "resizing" : true,
32043         /**
32044          * @event resize
32045          * Fired after a resize.
32046          * @param {Roo.Resizable} this
32047          * @param {Number} width The new width
32048          * @param {Number} height The new height
32049          * @param {Roo.EventObject} e The mouseup event
32050          */
32051         "resize" : true
32052     });
32053
32054     if(this.width !== null && this.height !== null){
32055         this.resizeTo(this.width, this.height);
32056     }else{
32057         this.updateChildSize();
32058     }
32059     if(Roo.isIE){
32060         this.el.dom.style.zoom = 1;
32061     }
32062     Roo.Resizable.superclass.constructor.call(this);
32063 };
32064
32065 Roo.extend(Roo.Resizable, Roo.util.Observable, {
32066         resizeChild : false,
32067         adjustments : [0, 0],
32068         minWidth : 5,
32069         minHeight : 5,
32070         maxWidth : 10000,
32071         maxHeight : 10000,
32072         enabled : true,
32073         animate : false,
32074         duration : .35,
32075         dynamic : false,
32076         handles : false,
32077         multiDirectional : false,
32078         disableTrackOver : false,
32079         easing : 'easeOutStrong',
32080         widthIncrement : 0,
32081         heightIncrement : 0,
32082         pinned : false,
32083         width : null,
32084         height : null,
32085         preserveRatio : false,
32086         transparent: false,
32087         minX: 0,
32088         minY: 0,
32089         draggable: false,
32090
32091         /**
32092          * @cfg {String/HTMLElement/Element} constrainTo Constrain the resize to a particular element
32093          */
32094         constrainTo: undefined,
32095         /**
32096          * @cfg {Roo.lib.Region} resizeRegion Constrain the resize to a particular region
32097          */
32098         resizeRegion: undefined,
32099
32100
32101     /**
32102      * Perform a manual resize
32103      * @param {Number} width
32104      * @param {Number} height
32105      */
32106     resizeTo : function(width, height){
32107         this.el.setSize(width, height);
32108         this.updateChildSize();
32109         this.fireEvent("resize", this, width, height, null);
32110     },
32111
32112     // private
32113     startSizing : function(e, handle){
32114         this.fireEvent("beforeresize", this, e);
32115         if(this.enabled){ // 2nd enabled check in case disabled before beforeresize handler
32116
32117             if(!this.overlay){
32118                 this.overlay = this.el.createProxy({tag: "div", cls: "x-resizable-overlay", html: "&#160;"});
32119                 this.overlay.unselectable();
32120                 this.overlay.enableDisplayMode("block");
32121                 this.overlay.on("mousemove", this.onMouseMove, this);
32122                 this.overlay.on("mouseup", this.onMouseUp, this);
32123             }
32124             this.overlay.setStyle("cursor", handle.el.getStyle("cursor"));
32125
32126             this.resizing = true;
32127             this.startBox = this.el.getBox();
32128             this.startPoint = e.getXY();
32129             this.offsets = [(this.startBox.x + this.startBox.width) - this.startPoint[0],
32130                             (this.startBox.y + this.startBox.height) - this.startPoint[1]];
32131
32132             this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
32133             this.overlay.show();
32134
32135             if(this.constrainTo) {
32136                 var ct = Roo.get(this.constrainTo);
32137                 this.resizeRegion = ct.getRegion().adjust(
32138                     ct.getFrameWidth('t'),
32139                     ct.getFrameWidth('l'),
32140                     -ct.getFrameWidth('b'),
32141                     -ct.getFrameWidth('r')
32142                 );
32143             }
32144
32145             this.proxy.setStyle('visibility', 'hidden'); // workaround display none
32146             this.proxy.show();
32147             this.proxy.setBox(this.startBox);
32148             if(!this.dynamic){
32149                 this.proxy.setStyle('visibility', 'visible');
32150             }
32151         }
32152     },
32153
32154     // private
32155     onMouseDown : function(handle, e){
32156         if(this.enabled){
32157             e.stopEvent();
32158             this.activeHandle = handle;
32159             this.startSizing(e, handle);
32160         }
32161     },
32162
32163     // private
32164     onMouseUp : function(e){
32165         var size = this.resizeElement();
32166         this.resizing = false;
32167         this.handleOut();
32168         this.overlay.hide();
32169         this.proxy.hide();
32170         this.fireEvent("resize", this, size.width, size.height, e);
32171     },
32172
32173     // private
32174     updateChildSize : function(){
32175         
32176         if(this.resizeChild){
32177             var el = this.el;
32178             var child = this.resizeChild;
32179             var adj = this.adjustments;
32180             if(el.dom.offsetWidth){
32181                 var b = el.getSize(true);
32182                 child.setSize(b.width+adj[0], b.height+adj[1]);
32183             }
32184             // Second call here for IE
32185             // The first call enables instant resizing and
32186             // the second call corrects scroll bars if they
32187             // exist
32188             if(Roo.isIE){
32189                 setTimeout(function(){
32190                     if(el.dom.offsetWidth){
32191                         var b = el.getSize(true);
32192                         child.setSize(b.width+adj[0], b.height+adj[1]);
32193                     }
32194                 }, 10);
32195             }
32196         }
32197     },
32198
32199     // private
32200     snap : function(value, inc, min){
32201         if(!inc || !value) {
32202             return value;
32203         }
32204         var newValue = value;
32205         var m = value % inc;
32206         if(m > 0){
32207             if(m > (inc/2)){
32208                 newValue = value + (inc-m);
32209             }else{
32210                 newValue = value - m;
32211             }
32212         }
32213         return Math.max(min, newValue);
32214     },
32215
32216     // private
32217     resizeElement : function(){
32218         var box = this.proxy.getBox();
32219         if(this.updateBox){
32220             this.el.setBox(box, false, this.animate, this.duration, null, this.easing);
32221         }else{
32222             this.el.setSize(box.width, box.height, this.animate, this.duration, null, this.easing);
32223         }
32224         this.updateChildSize();
32225         if(!this.dynamic){
32226             this.proxy.hide();
32227         }
32228         return box;
32229     },
32230
32231     // private
32232     constrain : function(v, diff, m, mx){
32233         if(v - diff < m){
32234             diff = v - m;
32235         }else if(v - diff > mx){
32236             diff = mx - v;
32237         }
32238         return diff;
32239     },
32240
32241     // private
32242     onMouseMove : function(e){
32243         
32244         if(this.enabled){
32245             try{// try catch so if something goes wrong the user doesn't get hung
32246
32247             if(this.resizeRegion && !this.resizeRegion.contains(e.getPoint())) {
32248                 return;
32249             }
32250
32251             //var curXY = this.startPoint;
32252             var curSize = this.curSize || this.startBox;
32253             var x = this.startBox.x, y = this.startBox.y;
32254             var ox = x, oy = y;
32255             var w = curSize.width, h = curSize.height;
32256             var ow = w, oh = h;
32257             var mw = this.minWidth, mh = this.minHeight;
32258             var mxw = this.maxWidth, mxh = this.maxHeight;
32259             var wi = this.widthIncrement;
32260             var hi = this.heightIncrement;
32261
32262             var eventXY = e.getXY();
32263             var diffX = -(this.startPoint[0] - Math.max(this.minX, eventXY[0]));
32264             var diffY = -(this.startPoint[1] - Math.max(this.minY, eventXY[1]));
32265
32266             var pos = this.activeHandle.position;
32267
32268             switch(pos){
32269                 case "east":
32270                     w += diffX;
32271                     w = Math.min(Math.max(mw, w), mxw);
32272                     break;
32273              
32274                 case "south":
32275                     h += diffY;
32276                     h = Math.min(Math.max(mh, h), mxh);
32277                     break;
32278                 case "southeast":
32279                     w += diffX;
32280                     h += diffY;
32281                     w = Math.min(Math.max(mw, w), mxw);
32282                     h = Math.min(Math.max(mh, h), mxh);
32283                     break;
32284                 case "north":
32285                     diffY = this.constrain(h, diffY, mh, mxh);
32286                     y += diffY;
32287                     h -= diffY;
32288                     break;
32289                 case "hdrag":
32290                     
32291                     if (wi) {
32292                         var adiffX = Math.abs(diffX);
32293                         var sub = (adiffX % wi); // how much 
32294                         if (sub > (wi/2)) { // far enough to snap
32295                             diffX = (diffX > 0) ? diffX-sub + wi : diffX+sub - wi;
32296                         } else {
32297                             // remove difference.. 
32298                             diffX = (diffX > 0) ? diffX-sub : diffX+sub;
32299                         }
32300                     }
32301                     x += diffX;
32302                     x = Math.max(this.minX, x);
32303                     break;
32304                 case "west":
32305                     diffX = this.constrain(w, diffX, mw, mxw);
32306                     x += diffX;
32307                     w -= diffX;
32308                     break;
32309                 case "northeast":
32310                     w += diffX;
32311                     w = Math.min(Math.max(mw, w), mxw);
32312                     diffY = this.constrain(h, diffY, mh, mxh);
32313                     y += diffY;
32314                     h -= diffY;
32315                     break;
32316                 case "northwest":
32317                     diffX = this.constrain(w, diffX, mw, mxw);
32318                     diffY = this.constrain(h, diffY, mh, mxh);
32319                     y += diffY;
32320                     h -= diffY;
32321                     x += diffX;
32322                     w -= diffX;
32323                     break;
32324                case "southwest":
32325                     diffX = this.constrain(w, diffX, mw, mxw);
32326                     h += diffY;
32327                     h = Math.min(Math.max(mh, h), mxh);
32328                     x += diffX;
32329                     w -= diffX;
32330                     break;
32331             }
32332
32333             var sw = this.snap(w, wi, mw);
32334             var sh = this.snap(h, hi, mh);
32335             if(sw != w || sh != h){
32336                 switch(pos){
32337                     case "northeast":
32338                         y -= sh - h;
32339                     break;
32340                     case "north":
32341                         y -= sh - h;
32342                         break;
32343                     case "southwest":
32344                         x -= sw - w;
32345                     break;
32346                     case "west":
32347                         x -= sw - w;
32348                         break;
32349                     case "northwest":
32350                         x -= sw - w;
32351                         y -= sh - h;
32352                     break;
32353                 }
32354                 w = sw;
32355                 h = sh;
32356             }
32357
32358             if(this.preserveRatio){
32359                 switch(pos){
32360                     case "southeast":
32361                     case "east":
32362                         h = oh * (w/ow);
32363                         h = Math.min(Math.max(mh, h), mxh);
32364                         w = ow * (h/oh);
32365                        break;
32366                     case "south":
32367                         w = ow * (h/oh);
32368                         w = Math.min(Math.max(mw, w), mxw);
32369                         h = oh * (w/ow);
32370                         break;
32371                     case "northeast":
32372                         w = ow * (h/oh);
32373                         w = Math.min(Math.max(mw, w), mxw);
32374                         h = oh * (w/ow);
32375                     break;
32376                     case "north":
32377                         var tw = w;
32378                         w = ow * (h/oh);
32379                         w = Math.min(Math.max(mw, w), mxw);
32380                         h = oh * (w/ow);
32381                         x += (tw - w) / 2;
32382                         break;
32383                     case "southwest":
32384                         h = oh * (w/ow);
32385                         h = Math.min(Math.max(mh, h), mxh);
32386                         var tw = w;
32387                         w = ow * (h/oh);
32388                         x += tw - w;
32389                         break;
32390                     case "west":
32391                         var th = h;
32392                         h = oh * (w/ow);
32393                         h = Math.min(Math.max(mh, h), mxh);
32394                         y += (th - h) / 2;
32395                         var tw = w;
32396                         w = ow * (h/oh);
32397                         x += tw - w;
32398                        break;
32399                     case "northwest":
32400                         var tw = w;
32401                         var th = h;
32402                         h = oh * (w/ow);
32403                         h = Math.min(Math.max(mh, h), mxh);
32404                         w = ow * (h/oh);
32405                         y += th - h;
32406                         x += tw - w;
32407                        break;
32408
32409                 }
32410             }
32411             if (pos == 'hdrag') {
32412                 w = ow;
32413             }
32414             this.proxy.setBounds(x, y, w, h);
32415             if(this.dynamic){
32416                 this.resizeElement();
32417             }
32418             }catch(e){}
32419         }
32420         this.fireEvent("resizing", this, x, y, w, h, e);
32421     },
32422
32423     // private
32424     handleOver : function(){
32425         if(this.enabled){
32426             this.el.addClass("x-resizable-over");
32427         }
32428     },
32429
32430     // private
32431     handleOut : function(){
32432         if(!this.resizing){
32433             this.el.removeClass("x-resizable-over");
32434         }
32435     },
32436
32437     /**
32438      * Returns the element this component is bound to.
32439      * @return {Roo.Element}
32440      */
32441     getEl : function(){
32442         return this.el;
32443     },
32444
32445     /**
32446      * Returns the resizeChild element (or null).
32447      * @return {Roo.Element}
32448      */
32449     getResizeChild : function(){
32450         return this.resizeChild;
32451     },
32452     groupHandler : function()
32453     {
32454         
32455     },
32456     /**
32457      * Destroys this resizable. If the element was wrapped and
32458      * removeEl is not true then the element remains.
32459      * @param {Boolean} removeEl (optional) true to remove the element from the DOM
32460      */
32461     destroy : function(removeEl){
32462         this.proxy.remove();
32463         if(this.overlay){
32464             this.overlay.removeAllListeners();
32465             this.overlay.remove();
32466         }
32467         var ps = Roo.Resizable.positions;
32468         for(var k in ps){
32469             if(typeof ps[k] != "function" && this[ps[k]]){
32470                 var h = this[ps[k]];
32471                 h.el.removeAllListeners();
32472                 h.el.remove();
32473             }
32474         }
32475         if(removeEl){
32476             this.el.update("");
32477             this.el.remove();
32478         }
32479     }
32480 });
32481
32482 // private
32483 // hash to map config positions to true positions
32484 Roo.Resizable.positions = {
32485     n: "north", s: "south", e: "east", w: "west", se: "southeast", sw: "southwest", nw: "northwest", ne: "northeast", 
32486     hd: "hdrag"
32487 };
32488
32489 // private
32490 Roo.Resizable.Handle = function(rz, pos, disableTrackOver, transparent){
32491     if(!this.tpl){
32492         // only initialize the template if resizable is used
32493         var tpl = Roo.DomHelper.createTemplate(
32494             {tag: "div", cls: "x-resizable-handle x-resizable-handle-{0}"}
32495         );
32496         tpl.compile();
32497         Roo.Resizable.Handle.prototype.tpl = tpl;
32498     }
32499     this.position = pos;
32500     this.rz = rz;
32501     // show north drag fro topdra
32502     var handlepos = pos == 'hdrag' ? 'north' : pos;
32503     
32504     this.el = this.tpl.append(rz.el.dom, [handlepos], true);
32505     if (pos == 'hdrag') {
32506         this.el.setStyle('cursor', 'pointer');
32507     }
32508     this.el.unselectable();
32509     if(transparent){
32510         this.el.setOpacity(0);
32511     }
32512     this.el.on("mousedown", this.onMouseDown, this);
32513     if(!disableTrackOver){
32514         this.el.on("mouseover", this.onMouseOver, this);
32515         this.el.on("mouseout", this.onMouseOut, this);
32516     }
32517 };
32518
32519 // private
32520 Roo.Resizable.Handle.prototype = {
32521     afterResize : function(rz){
32522         Roo.log('after?');
32523         // do nothing
32524     },
32525     // private
32526     onMouseDown : function(e){
32527         this.rz.onMouseDown(this, e);
32528     },
32529     // private
32530     onMouseOver : function(e){
32531         this.rz.handleOver(this, e);
32532     },
32533     // private
32534     onMouseOut : function(e){
32535         this.rz.handleOut(this, e);
32536     }
32537 };/*
32538  * Based on:
32539  * Ext JS Library 1.1.1
32540  * Copyright(c) 2006-2007, Ext JS, LLC.
32541  *
32542  * Originally Released Under LGPL - original licence link has changed is not relivant.
32543  *
32544  * Fork - LGPL
32545  * <script type="text/javascript">
32546  */
32547
32548 /**
32549  * @class Roo.Editor
32550  * @extends Roo.Component
32551  * A base editor field that handles displaying/hiding on demand and has some built-in sizing and event handling logic.
32552  * @constructor
32553  * Create a new Editor
32554  * @param {Roo.form.Field} field The Field object (or descendant)
32555  * @param {Object} config The config object
32556  */
32557 Roo.Editor = function(field, config){
32558     Roo.Editor.superclass.constructor.call(this, config);
32559     this.field = field;
32560     this.addEvents({
32561         /**
32562              * @event beforestartedit
32563              * Fires when editing is initiated, but before the value changes.  Editing can be canceled by returning
32564              * false from the handler of this event.
32565              * @param {Editor} this
32566              * @param {Roo.Element} boundEl The underlying element bound to this editor
32567              * @param {Mixed} value The field value being set
32568              */
32569         "beforestartedit" : true,
32570         /**
32571              * @event startedit
32572              * Fires when this editor is displayed
32573              * @param {Roo.Element} boundEl The underlying element bound to this editor
32574              * @param {Mixed} value The starting field value
32575              */
32576         "startedit" : true,
32577         /**
32578              * @event beforecomplete
32579              * Fires after a change has been made to the field, but before the change is reflected in the underlying
32580              * field.  Saving the change to the field can be canceled by returning false from the handler of this event.
32581              * Note that if the value has not changed and ignoreNoChange = true, the editing will still end but this
32582              * event will not fire since no edit actually occurred.
32583              * @param {Editor} this
32584              * @param {Mixed} value The current field value
32585              * @param {Mixed} startValue The original field value
32586              */
32587         "beforecomplete" : true,
32588         /**
32589              * @event complete
32590              * Fires after editing is complete and any changed value has been written to the underlying field.
32591              * @param {Editor} this
32592              * @param {Mixed} value The current field value
32593              * @param {Mixed} startValue The original field value
32594              */
32595         "complete" : true,
32596         /**
32597          * @event specialkey
32598          * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
32599          * {@link Roo.EventObject#getKey} to determine which key was pressed.
32600          * @param {Roo.form.Field} this
32601          * @param {Roo.EventObject} e The event object
32602          */
32603         "specialkey" : true
32604     });
32605 };
32606
32607 Roo.extend(Roo.Editor, Roo.Component, {
32608     /**
32609      * @cfg {Boolean/String} autosize
32610      * True for the editor to automatically adopt the size of the underlying field, "width" to adopt the width only,
32611      * or "height" to adopt the height only (defaults to false)
32612      */
32613     /**
32614      * @cfg {Boolean} revertInvalid
32615      * True to automatically revert the field value and cancel the edit when the user completes an edit and the field
32616      * validation fails (defaults to true)
32617      */
32618     /**
32619      * @cfg {Boolean} ignoreNoChange
32620      * True to skip the the edit completion process (no save, no events fired) if the user completes an edit and
32621      * the value has not changed (defaults to false).  Applies only to string values - edits for other data types
32622      * will never be ignored.
32623      */
32624     /**
32625      * @cfg {Boolean} hideEl
32626      * False to keep the bound element visible while the editor is displayed (defaults to true)
32627      */
32628     /**
32629      * @cfg {Mixed} value
32630      * The data value of the underlying field (defaults to "")
32631      */
32632     value : "",
32633     /**
32634      * @cfg {String} alignment
32635      * The position to align to (see {@link Roo.Element#alignTo} for more details, defaults to "c-c?").
32636      */
32637     alignment: "c-c?",
32638     /**
32639      * @cfg {Boolean/String} shadow "sides" for sides/bottom only, "frame" for 4-way shadow, and "drop"
32640      * for bottom-right shadow (defaults to "frame")
32641      */
32642     shadow : "frame",
32643     /**
32644      * @cfg {Boolean} constrain True to constrain the editor to the viewport
32645      */
32646     constrain : false,
32647     /**
32648      * @cfg {Boolean} completeOnEnter True to complete the edit when the enter key is pressed (defaults to false)
32649      */
32650     completeOnEnter : false,
32651     /**
32652      * @cfg {Boolean} cancelOnEsc True to cancel the edit when the escape key is pressed (defaults to false)
32653      */
32654     cancelOnEsc : false,
32655     /**
32656      * @cfg {Boolean} updateEl True to update the innerHTML of the bound element when the update completes (defaults to false)
32657      */
32658     updateEl : false,
32659
32660     // private
32661     onRender : function(ct, position){
32662         this.el = new Roo.Layer({
32663             shadow: this.shadow,
32664             cls: "x-editor",
32665             parentEl : ct,
32666             shim : this.shim,
32667             shadowOffset:4,
32668             id: this.id,
32669             constrain: this.constrain
32670         });
32671         this.el.setStyle("overflow", Roo.isGecko ? "auto" : "hidden");
32672         if(this.field.msgTarget != 'title'){
32673             this.field.msgTarget = 'qtip';
32674         }
32675         this.field.render(this.el);
32676         if(Roo.isGecko){
32677             this.field.el.dom.setAttribute('autocomplete', 'off');
32678         }
32679         this.field.on("specialkey", this.onSpecialKey, this);
32680         if(this.swallowKeys){
32681             this.field.el.swallowEvent(['keydown','keypress']);
32682         }
32683         this.field.show();
32684         this.field.on("blur", this.onBlur, this);
32685         if(this.field.grow){
32686             this.field.on("autosize", this.el.sync,  this.el, {delay:1});
32687         }
32688     },
32689
32690     onSpecialKey : function(field, e)
32691     {
32692         //Roo.log('editor onSpecialKey');
32693         if(this.completeOnEnter && e.getKey() == e.ENTER){
32694             e.stopEvent();
32695             this.completeEdit();
32696             return;
32697         }
32698         // do not fire special key otherwise it might hide close the editor...
32699         if(e.getKey() == e.ENTER){    
32700             return;
32701         }
32702         if(this.cancelOnEsc && e.getKey() == e.ESC){
32703             this.cancelEdit();
32704             return;
32705         } 
32706         this.fireEvent('specialkey', field, e);
32707     
32708     },
32709
32710     /**
32711      * Starts the editing process and shows the editor.
32712      * @param {String/HTMLElement/Element} el The element to edit
32713      * @param {String} value (optional) A value to initialize the editor with. If a value is not provided, it defaults
32714       * to the innerHTML of el.
32715      */
32716     startEdit : function(el, value){
32717         if(this.editing){
32718             this.completeEdit();
32719         }
32720         this.boundEl = Roo.get(el);
32721         var v = value !== undefined ? value : this.boundEl.dom.innerHTML;
32722         if(!this.rendered){
32723             this.render(this.parentEl || document.body);
32724         }
32725         if(this.fireEvent("beforestartedit", this, this.boundEl, v) === false){
32726             return;
32727         }
32728         this.startValue = v;
32729         this.field.setValue(v);
32730         if(this.autoSize){
32731             var sz = this.boundEl.getSize();
32732             switch(this.autoSize){
32733                 case "width":
32734                 this.setSize(sz.width,  "");
32735                 break;
32736                 case "height":
32737                 this.setSize("",  sz.height);
32738                 break;
32739                 default:
32740                 this.setSize(sz.width,  sz.height);
32741             }
32742         }
32743         this.el.alignTo(this.boundEl, this.alignment);
32744         this.editing = true;
32745         if(Roo.QuickTips){
32746             Roo.QuickTips.disable();
32747         }
32748         this.show();
32749     },
32750
32751     /**
32752      * Sets the height and width of this editor.
32753      * @param {Number} width The new width
32754      * @param {Number} height The new height
32755      */
32756     setSize : function(w, h){
32757         this.field.setSize(w, h);
32758         if(this.el){
32759             this.el.sync();
32760         }
32761     },
32762
32763     /**
32764      * Realigns the editor to the bound field based on the current alignment config value.
32765      */
32766     realign : function(){
32767         this.el.alignTo(this.boundEl, this.alignment);
32768     },
32769
32770     /**
32771      * Ends the editing process, persists the changed value to the underlying field, and hides the editor.
32772      * @param {Boolean} remainVisible Override the default behavior and keep the editor visible after edit (defaults to false)
32773      */
32774     completeEdit : function(remainVisible){
32775         if(!this.editing){
32776             return;
32777         }
32778         var v = this.getValue();
32779         if(this.revertInvalid !== false && !this.field.isValid()){
32780             v = this.startValue;
32781             this.cancelEdit(true);
32782         }
32783         if(String(v) === String(this.startValue) && this.ignoreNoChange){
32784             this.editing = false;
32785             this.hide();
32786             return;
32787         }
32788         if(this.fireEvent("beforecomplete", this, v, this.startValue) !== false){
32789             this.editing = false;
32790             if(this.updateEl && this.boundEl){
32791                 this.boundEl.update(v);
32792             }
32793             if(remainVisible !== true){
32794                 this.hide();
32795             }
32796             this.fireEvent("complete", this, v, this.startValue);
32797         }
32798     },
32799
32800     // private
32801     onShow : function(){
32802         this.el.show();
32803         if(this.hideEl !== false){
32804             this.boundEl.hide();
32805         }
32806         this.field.show();
32807         if(Roo.isIE && !this.fixIEFocus){ // IE has problems with focusing the first time
32808             this.fixIEFocus = true;
32809             this.deferredFocus.defer(50, this);
32810         }else{
32811             this.field.focus();
32812         }
32813         this.fireEvent("startedit", this.boundEl, this.startValue);
32814     },
32815
32816     deferredFocus : function(){
32817         if(this.editing){
32818             this.field.focus();
32819         }
32820     },
32821
32822     /**
32823      * Cancels the editing process and hides the editor without persisting any changes.  The field value will be
32824      * reverted to the original starting value.
32825      * @param {Boolean} remainVisible Override the default behavior and keep the editor visible after
32826      * cancel (defaults to false)
32827      */
32828     cancelEdit : function(remainVisible){
32829         if(this.editing){
32830             this.setValue(this.startValue);
32831             if(remainVisible !== true){
32832                 this.hide();
32833             }
32834         }
32835     },
32836
32837     // private
32838     onBlur : function(){
32839         if(this.allowBlur !== true && this.editing){
32840             this.completeEdit();
32841         }
32842     },
32843
32844     // private
32845     onHide : function(){
32846         if(this.editing){
32847             this.completeEdit();
32848             return;
32849         }
32850         this.field.blur();
32851         if(this.field.collapse){
32852             this.field.collapse();
32853         }
32854         this.el.hide();
32855         if(this.hideEl !== false){
32856             this.boundEl.show();
32857         }
32858         if(Roo.QuickTips){
32859             Roo.QuickTips.enable();
32860         }
32861     },
32862
32863     /**
32864      * Sets the data value of the editor
32865      * @param {Mixed} value Any valid value supported by the underlying field
32866      */
32867     setValue : function(v){
32868         this.field.setValue(v);
32869     },
32870
32871     /**
32872      * Gets the data value of the editor
32873      * @return {Mixed} The data value
32874      */
32875     getValue : function(){
32876         return this.field.getValue();
32877     }
32878 });/*
32879  * Based on:
32880  * Ext JS Library 1.1.1
32881  * Copyright(c) 2006-2007, Ext JS, LLC.
32882  *
32883  * Originally Released Under LGPL - original licence link has changed is not relivant.
32884  *
32885  * Fork - LGPL
32886  * <script type="text/javascript">
32887  */
32888  
32889 /**
32890  * @class Roo.BasicDialog
32891  * @extends Roo.util.Observable
32892  * Lightweight Dialog Class.  The code below shows the creation of a typical dialog using existing HTML markup:
32893  * <pre><code>
32894 var dlg = new Roo.BasicDialog("my-dlg", {
32895     height: 200,
32896     width: 300,
32897     minHeight: 100,
32898     minWidth: 150,
32899     modal: true,
32900     proxyDrag: true,
32901     shadow: true
32902 });
32903 dlg.addKeyListener(27, dlg.hide, dlg); // ESC can also close the dialog
32904 dlg.addButton('OK', dlg.hide, dlg);    // Could call a save function instead of hiding
32905 dlg.addButton('Cancel', dlg.hide, dlg);
32906 dlg.show();
32907 </code></pre>
32908   <b>A Dialog should always be a direct child of the body element.</b>
32909  * @cfg {Boolean/DomHelper} autoCreate True to auto create from scratch, or using a DomHelper Object (defaults to false)
32910  * @cfg {String} title Default text to display in the title bar (defaults to null)
32911  * @cfg {Number} width Width of the dialog in pixels (can also be set via CSS).  Determined by browser if unspecified.
32912  * @cfg {Number} height Height of the dialog in pixels (can also be set via CSS).  Determined by browser if unspecified.
32913  * @cfg {Number} x The default left page coordinate of the dialog (defaults to center screen)
32914  * @cfg {Number} y The default top page coordinate of the dialog (defaults to center screen)
32915  * @cfg {String/Element} animateTarget Id or element from which the dialog should animate while opening
32916  * (defaults to null with no animation)
32917  * @cfg {Boolean} resizable False to disable manual dialog resizing (defaults to true)
32918  * @cfg {String} resizeHandles Which resize handles to display - see the {@link Roo.Resizable} handles config
32919  * property for valid values (defaults to 'all')
32920  * @cfg {Number} minHeight The minimum allowable height for a resizable dialog (defaults to 80)
32921  * @cfg {Number} minWidth The minimum allowable width for a resizable dialog (defaults to 200)
32922  * @cfg {Boolean} modal True to show the dialog modally, preventing user interaction with the rest of the page (defaults to false)
32923  * @cfg {Boolean} autoScroll True to allow the dialog body contents to overflow and display scrollbars (defaults to false)
32924  * @cfg {Boolean} closable False to remove the built-in top-right corner close button (defaults to true)
32925  * @cfg {Boolean} collapsible False to remove the built-in top-right corner collapse button (defaults to true)
32926  * @cfg {Boolean} constraintoviewport True to keep the dialog constrained within the visible viewport boundaries (defaults to true)
32927  * @cfg {Boolean} syncHeightBeforeShow True to cause the dimensions to be recalculated before the dialog is shown (defaults to false)
32928  * @cfg {Boolean} draggable False to disable dragging of the dialog within the viewport (defaults to true)
32929  * @cfg {Boolean} autoTabs If true, all elements with class 'x-dlg-tab' will get automatically converted to tabs (defaults to false)
32930  * @cfg {String} tabTag The tag name of tab elements, used when autoTabs = true (defaults to 'div')
32931  * @cfg {Boolean} proxyDrag True to drag a lightweight proxy element rather than the dialog itself, used when
32932  * draggable = true (defaults to false)
32933  * @cfg {Boolean} fixedcenter True to ensure that anytime the dialog is shown or resized it gets centered (defaults to false)
32934  * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
32935  * shadow (defaults to false)
32936  * @cfg {Number} shadowOffset The number of pixels to offset the shadow if displayed (defaults to 5)
32937  * @cfg {String} buttonAlign Valid values are "left," "center" and "right" (defaults to "right")
32938  * @cfg {Number} minButtonWidth Minimum width of all dialog buttons (defaults to 75)
32939  * @cfg {Array} buttons Array of buttons
32940  * @cfg {Boolean} shim True to create an iframe shim that prevents selects from showing through (defaults to false)
32941  * @constructor
32942  * Create a new BasicDialog.
32943  * @param {String/HTMLElement/Roo.Element} el The container element or DOM node, or its id
32944  * @param {Object} config Configuration options
32945  */
32946 Roo.BasicDialog = function(el, config){
32947     this.el = Roo.get(el);
32948     var dh = Roo.DomHelper;
32949     if(!this.el && config && config.autoCreate){
32950         if(typeof config.autoCreate == "object"){
32951             if(!config.autoCreate.id){
32952                 config.autoCreate.id = el;
32953             }
32954             this.el = dh.append(document.body,
32955                         config.autoCreate, true);
32956         }else{
32957             this.el = dh.append(document.body,
32958                         {tag: "div", id: el, style:'visibility:hidden;'}, true);
32959         }
32960     }
32961     el = this.el;
32962     el.setDisplayed(true);
32963     el.hide = this.hideAction;
32964     this.id = el.id;
32965     el.addClass("x-dlg");
32966
32967     Roo.apply(this, config);
32968
32969     this.proxy = el.createProxy("x-dlg-proxy");
32970     this.proxy.hide = this.hideAction;
32971     this.proxy.setOpacity(.5);
32972     this.proxy.hide();
32973
32974     if(config.width){
32975         el.setWidth(config.width);
32976     }
32977     if(config.height){
32978         el.setHeight(config.height);
32979     }
32980     this.size = el.getSize();
32981     if(typeof config.x != "undefined" && typeof config.y != "undefined"){
32982         this.xy = [config.x,config.y];
32983     }else{
32984         this.xy = el.getCenterXY(true);
32985     }
32986     /** The header element @type Roo.Element */
32987     this.header = el.child("> .x-dlg-hd");
32988     /** The body element @type Roo.Element */
32989     this.body = el.child("> .x-dlg-bd");
32990     /** The footer element @type Roo.Element */
32991     this.footer = el.child("> .x-dlg-ft");
32992
32993     if(!this.header){
32994         this.header = el.createChild({tag: "div", cls:"x-dlg-hd", html: "&#160;"}, this.body ? this.body.dom : null);
32995     }
32996     if(!this.body){
32997         this.body = el.createChild({tag: "div", cls:"x-dlg-bd"});
32998     }
32999
33000     this.header.unselectable();
33001     if(this.title){
33002         this.header.update(this.title);
33003     }
33004     // this element allows the dialog to be focused for keyboard event
33005     this.focusEl = el.createChild({tag: "a", href:"#", cls:"x-dlg-focus", tabIndex:"-1"});
33006     this.focusEl.swallowEvent("click", true);
33007
33008     this.header.wrap({cls:"x-dlg-hd-right"}).wrap({cls:"x-dlg-hd-left"}, true);
33009
33010     // wrap the body and footer for special rendering
33011     this.bwrap = this.body.wrap({tag: "div", cls:"x-dlg-dlg-body"});
33012     if(this.footer){
33013         this.bwrap.dom.appendChild(this.footer.dom);
33014     }
33015
33016     this.bg = this.el.createChild({
33017         tag: "div", cls:"x-dlg-bg",
33018         html: '<div class="x-dlg-bg-left"><div class="x-dlg-bg-right"><div class="x-dlg-bg-center">&#160;</div></div></div>'
33019     });
33020     this.centerBg = this.bg.child("div.x-dlg-bg-center");
33021
33022
33023     if(this.autoScroll !== false && !this.autoTabs){
33024         this.body.setStyle("overflow", "auto");
33025     }
33026
33027     this.toolbox = this.el.createChild({cls: "x-dlg-toolbox"});
33028
33029     if(this.closable !== false){
33030         this.el.addClass("x-dlg-closable");
33031         this.close = this.toolbox.createChild({cls:"x-dlg-close"});
33032         this.close.on("click", this.closeClick, this);
33033         this.close.addClassOnOver("x-dlg-close-over");
33034     }
33035     if(this.collapsible !== false){
33036         this.collapseBtn = this.toolbox.createChild({cls:"x-dlg-collapse"});
33037         this.collapseBtn.on("click", this.collapseClick, this);
33038         this.collapseBtn.addClassOnOver("x-dlg-collapse-over");
33039         this.header.on("dblclick", this.collapseClick, this);
33040     }
33041     if(this.resizable !== false){
33042         this.el.addClass("x-dlg-resizable");
33043         this.resizer = new Roo.Resizable(el, {
33044             minWidth: this.minWidth || 80,
33045             minHeight:this.minHeight || 80,
33046             handles: this.resizeHandles || "all",
33047             pinned: true
33048         });
33049         this.resizer.on("beforeresize", this.beforeResize, this);
33050         this.resizer.on("resize", this.onResize, this);
33051     }
33052     if(this.draggable !== false){
33053         el.addClass("x-dlg-draggable");
33054         if (!this.proxyDrag) {
33055             var dd = new Roo.dd.DD(el.dom.id, "WindowDrag");
33056         }
33057         else {
33058             var dd = new Roo.dd.DDProxy(el.dom.id, "WindowDrag", {dragElId: this.proxy.id});
33059         }
33060         dd.setHandleElId(this.header.id);
33061         dd.endDrag = this.endMove.createDelegate(this);
33062         dd.startDrag = this.startMove.createDelegate(this);
33063         dd.onDrag = this.onDrag.createDelegate(this);
33064         dd.scroll = false;
33065         this.dd = dd;
33066     }
33067     if(this.modal){
33068         this.mask = dh.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
33069         this.mask.enableDisplayMode("block");
33070         this.mask.hide();
33071         this.el.addClass("x-dlg-modal");
33072     }
33073     if(this.shadow){
33074         this.shadow = new Roo.Shadow({
33075             mode : typeof this.shadow == "string" ? this.shadow : "sides",
33076             offset : this.shadowOffset
33077         });
33078     }else{
33079         this.shadowOffset = 0;
33080     }
33081     if(Roo.useShims && this.shim !== false){
33082         this.shim = this.el.createShim();
33083         this.shim.hide = this.hideAction;
33084         this.shim.hide();
33085     }else{
33086         this.shim = false;
33087     }
33088     if(this.autoTabs){
33089         this.initTabs();
33090     }
33091     if (this.buttons) { 
33092         var bts= this.buttons;
33093         this.buttons = [];
33094         Roo.each(bts, function(b) {
33095             this.addButton(b);
33096         }, this);
33097     }
33098     
33099     
33100     this.addEvents({
33101         /**
33102          * @event keydown
33103          * Fires when a key is pressed
33104          * @param {Roo.BasicDialog} this
33105          * @param {Roo.EventObject} e
33106          */
33107         "keydown" : true,
33108         /**
33109          * @event move
33110          * Fires when this dialog is moved by the user.
33111          * @param {Roo.BasicDialog} this
33112          * @param {Number} x The new page X
33113          * @param {Number} y The new page Y
33114          */
33115         "move" : true,
33116         /**
33117          * @event resize
33118          * Fires when this dialog is resized by the user.
33119          * @param {Roo.BasicDialog} this
33120          * @param {Number} width The new width
33121          * @param {Number} height The new height
33122          */
33123         "resize" : true,
33124         /**
33125          * @event beforehide
33126          * Fires before this dialog is hidden.
33127          * @param {Roo.BasicDialog} this
33128          */
33129         "beforehide" : true,
33130         /**
33131          * @event hide
33132          * Fires when this dialog is hidden.
33133          * @param {Roo.BasicDialog} this
33134          */
33135         "hide" : true,
33136         /**
33137          * @event beforeshow
33138          * Fires before this dialog is shown.
33139          * @param {Roo.BasicDialog} this
33140          */
33141         "beforeshow" : true,
33142         /**
33143          * @event show
33144          * Fires when this dialog is shown.
33145          * @param {Roo.BasicDialog} this
33146          */
33147         "show" : true
33148     });
33149     el.on("keydown", this.onKeyDown, this);
33150     el.on("mousedown", this.toFront, this);
33151     Roo.EventManager.onWindowResize(this.adjustViewport, this, true);
33152     this.el.hide();
33153     Roo.DialogManager.register(this);
33154     Roo.BasicDialog.superclass.constructor.call(this);
33155 };
33156
33157 Roo.extend(Roo.BasicDialog, Roo.util.Observable, {
33158     shadowOffset: Roo.isIE ? 6 : 5,
33159     minHeight: 80,
33160     minWidth: 200,
33161     minButtonWidth: 75,
33162     defaultButton: null,
33163     buttonAlign: "right",
33164     tabTag: 'div',
33165     firstShow: true,
33166
33167     /**
33168      * Sets the dialog title text
33169      * @param {String} text The title text to display
33170      * @return {Roo.BasicDialog} this
33171      */
33172     setTitle : function(text){
33173         this.header.update(text);
33174         return this;
33175     },
33176
33177     // private
33178     closeClick : function(){
33179         this.hide();
33180     },
33181
33182     // private
33183     collapseClick : function(){
33184         this[this.collapsed ? "expand" : "collapse"]();
33185     },
33186
33187     /**
33188      * Collapses the dialog to its minimized state (only the title bar is visible).
33189      * Equivalent to the user clicking the collapse dialog button.
33190      */
33191     collapse : function(){
33192         if(!this.collapsed){
33193             this.collapsed = true;
33194             this.el.addClass("x-dlg-collapsed");
33195             this.restoreHeight = this.el.getHeight();
33196             this.resizeTo(this.el.getWidth(), this.header.getHeight());
33197         }
33198     },
33199
33200     /**
33201      * Expands a collapsed dialog back to its normal state.  Equivalent to the user
33202      * clicking the expand dialog button.
33203      */
33204     expand : function(){
33205         if(this.collapsed){
33206             this.collapsed = false;
33207             this.el.removeClass("x-dlg-collapsed");
33208             this.resizeTo(this.el.getWidth(), this.restoreHeight);
33209         }
33210     },
33211
33212     /**
33213      * Reinitializes the tabs component, clearing out old tabs and finding new ones.
33214      * @return {Roo.TabPanel} The tabs component
33215      */
33216     initTabs : function(){
33217         var tabs = this.getTabs();
33218         while(tabs.getTab(0)){
33219             tabs.removeTab(0);
33220         }
33221         this.el.select(this.tabTag+'.x-dlg-tab').each(function(el){
33222             var dom = el.dom;
33223             tabs.addTab(Roo.id(dom), dom.title);
33224             dom.title = "";
33225         });
33226         tabs.activate(0);
33227         return tabs;
33228     },
33229
33230     // private
33231     beforeResize : function(){
33232         this.resizer.minHeight = Math.max(this.minHeight, this.getHeaderFooterHeight(true)+40);
33233     },
33234
33235     // private
33236     onResize : function(){
33237         this.refreshSize();
33238         this.syncBodyHeight();
33239         this.adjustAssets();
33240         this.focus();
33241         this.fireEvent("resize", this, this.size.width, this.size.height);
33242     },
33243
33244     // private
33245     onKeyDown : function(e){
33246         if(this.isVisible()){
33247             this.fireEvent("keydown", this, e);
33248         }
33249     },
33250
33251     /**
33252      * Resizes the dialog.
33253      * @param {Number} width
33254      * @param {Number} height
33255      * @return {Roo.BasicDialog} this
33256      */
33257     resizeTo : function(width, height){
33258         this.el.setSize(width, height);
33259         this.size = {width: width, height: height};
33260         this.syncBodyHeight();
33261         if(this.fixedcenter){
33262             this.center();
33263         }
33264         if(this.isVisible()){
33265             this.constrainXY();
33266             this.adjustAssets();
33267         }
33268         this.fireEvent("resize", this, width, height);
33269         return this;
33270     },
33271
33272
33273     /**
33274      * Resizes the dialog to fit the specified content size.
33275      * @param {Number} width
33276      * @param {Number} height
33277      * @return {Roo.BasicDialog} this
33278      */
33279     setContentSize : function(w, h){
33280         h += this.getHeaderFooterHeight() + this.body.getMargins("tb");
33281         w += this.body.getMargins("lr") + this.bwrap.getMargins("lr") + this.centerBg.getPadding("lr");
33282         //if(!this.el.isBorderBox()){
33283             h +=  this.body.getPadding("tb") + this.bwrap.getBorderWidth("tb") + this.body.getBorderWidth("tb") + this.el.getBorderWidth("tb");
33284             w += this.body.getPadding("lr") + this.bwrap.getBorderWidth("lr") + this.body.getBorderWidth("lr") + this.bwrap.getPadding("lr") + this.el.getBorderWidth("lr");
33285         //}
33286         if(this.tabs){
33287             h += this.tabs.stripWrap.getHeight() + this.tabs.bodyEl.getMargins("tb") + this.tabs.bodyEl.getPadding("tb");
33288             w += this.tabs.bodyEl.getMargins("lr") + this.tabs.bodyEl.getPadding("lr");
33289         }
33290         this.resizeTo(w, h);
33291         return this;
33292     },
33293
33294     /**
33295      * Adds a key listener for when this dialog is displayed.  This allows you to hook in a function that will be
33296      * executed in response to a particular key being pressed while the dialog is active.
33297      * @param {Number/Array/Object} key Either the numeric key code, array of key codes or an object with the following options:
33298      *                                  {key: (number or array), shift: (true/false), ctrl: (true/false), alt: (true/false)}
33299      * @param {Function} fn The function to call
33300      * @param {Object} scope (optional) The scope of the function
33301      * @return {Roo.BasicDialog} this
33302      */
33303     addKeyListener : function(key, fn, scope){
33304         var keyCode, shift, ctrl, alt;
33305         if(typeof key == "object" && !(key instanceof Array)){
33306             keyCode = key["key"];
33307             shift = key["shift"];
33308             ctrl = key["ctrl"];
33309             alt = key["alt"];
33310         }else{
33311             keyCode = key;
33312         }
33313         var handler = function(dlg, e){
33314             if((!shift || e.shiftKey) && (!ctrl || e.ctrlKey) &&  (!alt || e.altKey)){
33315                 var k = e.getKey();
33316                 if(keyCode instanceof Array){
33317                     for(var i = 0, len = keyCode.length; i < len; i++){
33318                         if(keyCode[i] == k){
33319                           fn.call(scope || window, dlg, k, e);
33320                           return;
33321                         }
33322                     }
33323                 }else{
33324                     if(k == keyCode){
33325                         fn.call(scope || window, dlg, k, e);
33326                     }
33327                 }
33328             }
33329         };
33330         this.on("keydown", handler);
33331         return this;
33332     },
33333
33334     /**
33335      * Returns the TabPanel component (creates it if it doesn't exist).
33336      * Note: If you wish to simply check for the existence of tabs without creating them,
33337      * check for a null 'tabs' property.
33338      * @return {Roo.TabPanel} The tabs component
33339      */
33340     getTabs : function(){
33341         if(!this.tabs){
33342             this.el.addClass("x-dlg-auto-tabs");
33343             this.body.addClass(this.tabPosition == "bottom" ? "x-tabs-bottom" : "x-tabs-top");
33344             this.tabs = new Roo.TabPanel(this.body.dom, this.tabPosition == "bottom");
33345         }
33346         return this.tabs;
33347     },
33348
33349     /**
33350      * Adds a button to the footer section of the dialog.
33351      * @param {String/Object} config A string becomes the button text, an object can either be a Button config
33352      * object or a valid Roo.DomHelper element config
33353      * @param {Function} handler The function called when the button is clicked
33354      * @param {Object} scope (optional) The scope of the handler function (accepts position as a property)
33355      * @return {Roo.Button} The new button
33356      */
33357     addButton : function(config, handler, scope){
33358         var dh = Roo.DomHelper;
33359         if(!this.footer){
33360             this.footer = dh.append(this.bwrap, {tag: "div", cls:"x-dlg-ft"}, true);
33361         }
33362         if(!this.btnContainer){
33363             var tb = this.footer.createChild({
33364
33365                 cls:"x-dlg-btns x-dlg-btns-"+this.buttonAlign,
33366                 html:'<table cellspacing="0"><tbody><tr></tr></tbody></table><div class="x-clear"></div>'
33367             }, null, true);
33368             this.btnContainer = tb.firstChild.firstChild.firstChild;
33369         }
33370         var bconfig = {
33371             handler: handler,
33372             scope: scope,
33373             minWidth: this.minButtonWidth,
33374             hideParent:true
33375         };
33376         if(typeof config == "string"){
33377             bconfig.text = config;
33378         }else{
33379             if(config.tag){
33380                 bconfig.dhconfig = config;
33381             }else{
33382                 Roo.apply(bconfig, config);
33383             }
33384         }
33385         var fc = false;
33386         if ((typeof(bconfig.position) != 'undefined') && bconfig.position < this.btnContainer.childNodes.length-1) {
33387             bconfig.position = Math.max(0, bconfig.position);
33388             fc = this.btnContainer.childNodes[bconfig.position];
33389         }
33390          
33391         var btn = new Roo.Button(
33392             fc ? 
33393                 this.btnContainer.insertBefore(document.createElement("td"),fc)
33394                 : this.btnContainer.appendChild(document.createElement("td")),
33395             //Roo.get(this.btnContainer).createChild( { tag: 'td'},  fc ),
33396             bconfig
33397         );
33398         this.syncBodyHeight();
33399         if(!this.buttons){
33400             /**
33401              * Array of all the buttons that have been added to this dialog via addButton
33402              * @type Array
33403              */
33404             this.buttons = [];
33405         }
33406         this.buttons.push(btn);
33407         return btn;
33408     },
33409
33410     /**
33411      * Sets the default button to be focused when the dialog is displayed.
33412      * @param {Roo.BasicDialog.Button} btn The button object returned by {@link #addButton}
33413      * @return {Roo.BasicDialog} this
33414      */
33415     setDefaultButton : function(btn){
33416         this.defaultButton = btn;
33417         return this;
33418     },
33419
33420     // private
33421     getHeaderFooterHeight : function(safe){
33422         var height = 0;
33423         if(this.header){
33424            height += this.header.getHeight();
33425         }
33426         if(this.footer){
33427            var fm = this.footer.getMargins();
33428             height += (this.footer.getHeight()+fm.top+fm.bottom);
33429         }
33430         height += this.bwrap.getPadding("tb")+this.bwrap.getBorderWidth("tb");
33431         height += this.centerBg.getPadding("tb");
33432         return height;
33433     },
33434
33435     // private
33436     syncBodyHeight : function()
33437     {
33438         var bd = this.body, // the text
33439             cb = this.centerBg, // wrapper around bottom.. but does not seem to be used..
33440             bw = this.bwrap;
33441         var height = this.size.height - this.getHeaderFooterHeight(false);
33442         bd.setHeight(height-bd.getMargins("tb"));
33443         var hh = this.header.getHeight();
33444         var h = this.size.height-hh;
33445         cb.setHeight(h);
33446         
33447         bw.setLeftTop(cb.getPadding("l"), hh+cb.getPadding("t"));
33448         bw.setHeight(h-cb.getPadding("tb"));
33449         
33450         bw.setWidth(this.el.getWidth(true)-cb.getPadding("lr"));
33451         bd.setWidth(bw.getWidth(true));
33452         if(this.tabs){
33453             this.tabs.syncHeight();
33454             if(Roo.isIE){
33455                 this.tabs.el.repaint();
33456             }
33457         }
33458     },
33459
33460     /**
33461      * Restores the previous state of the dialog if Roo.state is configured.
33462      * @return {Roo.BasicDialog} this
33463      */
33464     restoreState : function(){
33465         var box = Roo.state.Manager.get(this.stateId || (this.el.id + "-state"));
33466         if(box && box.width){
33467             this.xy = [box.x, box.y];
33468             this.resizeTo(box.width, box.height);
33469         }
33470         return this;
33471     },
33472
33473     // private
33474     beforeShow : function(){
33475         this.expand();
33476         if(this.fixedcenter){
33477             this.xy = this.el.getCenterXY(true);
33478         }
33479         if(this.modal){
33480             Roo.get(document.body).addClass("x-body-masked");
33481             this.mask.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
33482             this.mask.show();
33483         }
33484         this.constrainXY();
33485     },
33486
33487     // private
33488     animShow : function(){
33489         var b = Roo.get(this.animateTarget).getBox();
33490         this.proxy.setSize(b.width, b.height);
33491         this.proxy.setLocation(b.x, b.y);
33492         this.proxy.show();
33493         this.proxy.setBounds(this.xy[0], this.xy[1], this.size.width, this.size.height,
33494                     true, .35, this.showEl.createDelegate(this));
33495     },
33496
33497     /**
33498      * Shows the dialog.
33499      * @param {String/HTMLElement/Roo.Element} animateTarget (optional) Reset the animation target
33500      * @return {Roo.BasicDialog} this
33501      */
33502     show : function(animateTarget){
33503         if (this.fireEvent("beforeshow", this) === false){
33504             return;
33505         }
33506         if(this.syncHeightBeforeShow){
33507             this.syncBodyHeight();
33508         }else if(this.firstShow){
33509             this.firstShow = false;
33510             this.syncBodyHeight(); // sync the height on the first show instead of in the constructor
33511         }
33512         this.animateTarget = animateTarget || this.animateTarget;
33513         if(!this.el.isVisible()){
33514             this.beforeShow();
33515             if(this.animateTarget && Roo.get(this.animateTarget)){
33516                 this.animShow();
33517             }else{
33518                 this.showEl();
33519             }
33520         }
33521         return this;
33522     },
33523
33524     // private
33525     showEl : function(){
33526         this.proxy.hide();
33527         this.el.setXY(this.xy);
33528         this.el.show();
33529         this.adjustAssets(true);
33530         this.toFront();
33531         this.focus();
33532         // IE peekaboo bug - fix found by Dave Fenwick
33533         if(Roo.isIE){
33534             this.el.repaint();
33535         }
33536         this.fireEvent("show", this);
33537     },
33538
33539     /**
33540      * Focuses the dialog.  If a defaultButton is set, it will receive focus, otherwise the
33541      * dialog itself will receive focus.
33542      */
33543     focus : function(){
33544         if(this.defaultButton){
33545             this.defaultButton.focus();
33546         }else{
33547             this.focusEl.focus();
33548         }
33549     },
33550
33551     // private
33552     constrainXY : function(){
33553         if(this.constraintoviewport !== false){
33554             if(!this.viewSize){
33555                 if(this.container){
33556                     var s = this.container.getSize();
33557                     this.viewSize = [s.width, s.height];
33558                 }else{
33559                     this.viewSize = [Roo.lib.Dom.getViewWidth(),Roo.lib.Dom.getViewHeight()];
33560                 }
33561             }
33562             var s = Roo.get(this.container||document).getScroll();
33563
33564             var x = this.xy[0], y = this.xy[1];
33565             var w = this.size.width, h = this.size.height;
33566             var vw = this.viewSize[0], vh = this.viewSize[1];
33567             // only move it if it needs it
33568             var moved = false;
33569             // first validate right/bottom
33570             if(x + w > vw+s.left){
33571                 x = vw - w;
33572                 moved = true;
33573             }
33574             if(y + h > vh+s.top){
33575                 y = vh - h;
33576                 moved = true;
33577             }
33578             // then make sure top/left isn't negative
33579             if(x < s.left){
33580                 x = s.left;
33581                 moved = true;
33582             }
33583             if(y < s.top){
33584                 y = s.top;
33585                 moved = true;
33586             }
33587             if(moved){
33588                 // cache xy
33589                 this.xy = [x, y];
33590                 if(this.isVisible()){
33591                     this.el.setLocation(x, y);
33592                     this.adjustAssets();
33593                 }
33594             }
33595         }
33596     },
33597
33598     // private
33599     onDrag : function(){
33600         if(!this.proxyDrag){
33601             this.xy = this.el.getXY();
33602             this.adjustAssets();
33603         }
33604     },
33605
33606     // private
33607     adjustAssets : function(doShow){
33608         var x = this.xy[0], y = this.xy[1];
33609         var w = this.size.width, h = this.size.height;
33610         if(doShow === true){
33611             if(this.shadow){
33612                 this.shadow.show(this.el);
33613             }
33614             if(this.shim){
33615                 this.shim.show();
33616             }
33617         }
33618         if(this.shadow && this.shadow.isVisible()){
33619             this.shadow.show(this.el);
33620         }
33621         if(this.shim && this.shim.isVisible()){
33622             this.shim.setBounds(x, y, w, h);
33623         }
33624     },
33625
33626     // private
33627     adjustViewport : function(w, h){
33628         if(!w || !h){
33629             w = Roo.lib.Dom.getViewWidth();
33630             h = Roo.lib.Dom.getViewHeight();
33631         }
33632         // cache the size
33633         this.viewSize = [w, h];
33634         if(this.modal && this.mask.isVisible()){
33635             this.mask.setSize(w, h); // first make sure the mask isn't causing overflow
33636             this.mask.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
33637         }
33638         if(this.isVisible()){
33639             this.constrainXY();
33640         }
33641     },
33642
33643     /**
33644      * Destroys this dialog and all its supporting elements (including any tabs, shim,
33645      * shadow, proxy, mask, etc.)  Also removes all event listeners.
33646      * @param {Boolean} removeEl (optional) true to remove the element from the DOM
33647      */
33648     destroy : function(removeEl){
33649         if(this.isVisible()){
33650             this.animateTarget = null;
33651             this.hide();
33652         }
33653         Roo.EventManager.removeResizeListener(this.adjustViewport, this);
33654         if(this.tabs){
33655             this.tabs.destroy(removeEl);
33656         }
33657         Roo.destroy(
33658              this.shim,
33659              this.proxy,
33660              this.resizer,
33661              this.close,
33662              this.mask
33663         );
33664         if(this.dd){
33665             this.dd.unreg();
33666         }
33667         if(this.buttons){
33668            for(var i = 0, len = this.buttons.length; i < len; i++){
33669                this.buttons[i].destroy();
33670            }
33671         }
33672         this.el.removeAllListeners();
33673         if(removeEl === true){
33674             this.el.update("");
33675             this.el.remove();
33676         }
33677         Roo.DialogManager.unregister(this);
33678     },
33679
33680     // private
33681     startMove : function(){
33682         if(this.proxyDrag){
33683             this.proxy.show();
33684         }
33685         if(this.constraintoviewport !== false){
33686             this.dd.constrainTo(document.body, {right: this.shadowOffset, bottom: this.shadowOffset});
33687         }
33688     },
33689
33690     // private
33691     endMove : function(){
33692         if(!this.proxyDrag){
33693             Roo.dd.DD.prototype.endDrag.apply(this.dd, arguments);
33694         }else{
33695             Roo.dd.DDProxy.prototype.endDrag.apply(this.dd, arguments);
33696             this.proxy.hide();
33697         }
33698         this.refreshSize();
33699         this.adjustAssets();
33700         this.focus();
33701         this.fireEvent("move", this, this.xy[0], this.xy[1]);
33702     },
33703
33704     /**
33705      * Brings this dialog to the front of any other visible dialogs
33706      * @return {Roo.BasicDialog} this
33707      */
33708     toFront : function(){
33709         Roo.DialogManager.bringToFront(this);
33710         return this;
33711     },
33712
33713     /**
33714      * Sends this dialog to the back (under) of any other visible dialogs
33715      * @return {Roo.BasicDialog} this
33716      */
33717     toBack : function(){
33718         Roo.DialogManager.sendToBack(this);
33719         return this;
33720     },
33721
33722     /**
33723      * Centers this dialog in the viewport
33724      * @return {Roo.BasicDialog} this
33725      */
33726     center : function(){
33727         var xy = this.el.getCenterXY(true);
33728         this.moveTo(xy[0], xy[1]);
33729         return this;
33730     },
33731
33732     /**
33733      * Moves the dialog's top-left corner to the specified point
33734      * @param {Number} x
33735      * @param {Number} y
33736      * @return {Roo.BasicDialog} this
33737      */
33738     moveTo : function(x, y){
33739         this.xy = [x,y];
33740         if(this.isVisible()){
33741             this.el.setXY(this.xy);
33742             this.adjustAssets();
33743         }
33744         return this;
33745     },
33746
33747     /**
33748      * Aligns the dialog to the specified element
33749      * @param {String/HTMLElement/Roo.Element} element The element to align to.
33750      * @param {String} position The position to align to (see {@link Roo.Element#alignTo} for more details).
33751      * @param {Array} offsets (optional) Offset the positioning by [x, y]
33752      * @return {Roo.BasicDialog} this
33753      */
33754     alignTo : function(element, position, offsets){
33755         this.xy = this.el.getAlignToXY(element, position, offsets);
33756         if(this.isVisible()){
33757             this.el.setXY(this.xy);
33758             this.adjustAssets();
33759         }
33760         return this;
33761     },
33762
33763     /**
33764      * Anchors an element to another element and realigns it when the window is resized.
33765      * @param {String/HTMLElement/Roo.Element} element The element to align to.
33766      * @param {String} position The position to align to (see {@link Roo.Element#alignTo} for more details)
33767      * @param {Array} offsets (optional) Offset the positioning by [x, y]
33768      * @param {Boolean/Number} monitorScroll (optional) true to monitor body scroll and reposition. If this parameter
33769      * is a number, it is used as the buffer delay (defaults to 50ms).
33770      * @return {Roo.BasicDialog} this
33771      */
33772     anchorTo : function(el, alignment, offsets, monitorScroll){
33773         var action = function(){
33774             this.alignTo(el, alignment, offsets);
33775         };
33776         Roo.EventManager.onWindowResize(action, this);
33777         var tm = typeof monitorScroll;
33778         if(tm != 'undefined'){
33779             Roo.EventManager.on(window, 'scroll', action, this,
33780                 {buffer: tm == 'number' ? monitorScroll : 50});
33781         }
33782         action.call(this);
33783         return this;
33784     },
33785
33786     /**
33787      * Returns true if the dialog is visible
33788      * @return {Boolean}
33789      */
33790     isVisible : function(){
33791         return this.el.isVisible();
33792     },
33793
33794     // private
33795     animHide : function(callback){
33796         var b = Roo.get(this.animateTarget).getBox();
33797         this.proxy.show();
33798         this.proxy.setBounds(this.xy[0], this.xy[1], this.size.width, this.size.height);
33799         this.el.hide();
33800         this.proxy.setBounds(b.x, b.y, b.width, b.height, true, .35,
33801                     this.hideEl.createDelegate(this, [callback]));
33802     },
33803
33804     /**
33805      * Hides the dialog.
33806      * @param {Function} callback (optional) Function to call when the dialog is hidden
33807      * @return {Roo.BasicDialog} this
33808      */
33809     hide : function(callback){
33810         if (this.fireEvent("beforehide", this) === false){
33811             return;
33812         }
33813         if(this.shadow){
33814             this.shadow.hide();
33815         }
33816         if(this.shim) {
33817           this.shim.hide();
33818         }
33819         // sometimes animateTarget seems to get set.. causing problems...
33820         // this just double checks..
33821         if(this.animateTarget && Roo.get(this.animateTarget)) {
33822            this.animHide(callback);
33823         }else{
33824             this.el.hide();
33825             this.hideEl(callback);
33826         }
33827         return this;
33828     },
33829
33830     // private
33831     hideEl : function(callback){
33832         this.proxy.hide();
33833         if(this.modal){
33834             this.mask.hide();
33835             Roo.get(document.body).removeClass("x-body-masked");
33836         }
33837         this.fireEvent("hide", this);
33838         if(typeof callback == "function"){
33839             callback();
33840         }
33841     },
33842
33843     // private
33844     hideAction : function(){
33845         this.setLeft("-10000px");
33846         this.setTop("-10000px");
33847         this.setStyle("visibility", "hidden");
33848     },
33849
33850     // private
33851     refreshSize : function(){
33852         this.size = this.el.getSize();
33853         this.xy = this.el.getXY();
33854         Roo.state.Manager.set(this.stateId || this.el.id + "-state", this.el.getBox());
33855     },
33856
33857     // private
33858     // z-index is managed by the DialogManager and may be overwritten at any time
33859     setZIndex : function(index){
33860         if(this.modal){
33861             this.mask.setStyle("z-index", index);
33862         }
33863         if(this.shim){
33864             this.shim.setStyle("z-index", ++index);
33865         }
33866         if(this.shadow){
33867             this.shadow.setZIndex(++index);
33868         }
33869         this.el.setStyle("z-index", ++index);
33870         if(this.proxy){
33871             this.proxy.setStyle("z-index", ++index);
33872         }
33873         if(this.resizer){
33874             this.resizer.proxy.setStyle("z-index", ++index);
33875         }
33876
33877         this.lastZIndex = index;
33878     },
33879
33880     /**
33881      * Returns the element for this dialog
33882      * @return {Roo.Element} The underlying dialog Element
33883      */
33884     getEl : function(){
33885         return this.el;
33886     }
33887 });
33888
33889 /**
33890  * @class Roo.DialogManager
33891  * Provides global access to BasicDialogs that have been created and
33892  * support for z-indexing (layering) multiple open dialogs.
33893  */
33894 Roo.DialogManager = function(){
33895     var list = {};
33896     var accessList = [];
33897     var front = null;
33898
33899     // private
33900     var sortDialogs = function(d1, d2){
33901         return (!d1._lastAccess || d1._lastAccess < d2._lastAccess) ? -1 : 1;
33902     };
33903
33904     // private
33905     var orderDialogs = function(){
33906         accessList.sort(sortDialogs);
33907         var seed = Roo.DialogManager.zseed;
33908         for(var i = 0, len = accessList.length; i < len; i++){
33909             var dlg = accessList[i];
33910             if(dlg){
33911                 dlg.setZIndex(seed + (i*10));
33912             }
33913         }
33914     };
33915
33916     return {
33917         /**
33918          * The starting z-index for BasicDialogs (defaults to 9000)
33919          * @type Number The z-index value
33920          */
33921         zseed : 9000,
33922
33923         // private
33924         register : function(dlg){
33925             list[dlg.id] = dlg;
33926             accessList.push(dlg);
33927         },
33928
33929         // private
33930         unregister : function(dlg){
33931             delete list[dlg.id];
33932             var i=0;
33933             var len=0;
33934             if(!accessList.indexOf){
33935                 for(  i = 0, len = accessList.length; i < len; i++){
33936                     if(accessList[i] == dlg){
33937                         accessList.splice(i, 1);
33938                         return;
33939                     }
33940                 }
33941             }else{
33942                  i = accessList.indexOf(dlg);
33943                 if(i != -1){
33944                     accessList.splice(i, 1);
33945                 }
33946             }
33947         },
33948
33949         /**
33950          * Gets a registered dialog by id
33951          * @param {String/Object} id The id of the dialog or a dialog
33952          * @return {Roo.BasicDialog} this
33953          */
33954         get : function(id){
33955             return typeof id == "object" ? id : list[id];
33956         },
33957
33958         /**
33959          * Brings the specified dialog to the front
33960          * @param {String/Object} dlg The id of the dialog or a dialog
33961          * @return {Roo.BasicDialog} this
33962          */
33963         bringToFront : function(dlg){
33964             dlg = this.get(dlg);
33965             if(dlg != front){
33966                 front = dlg;
33967                 dlg._lastAccess = new Date().getTime();
33968                 orderDialogs();
33969             }
33970             return dlg;
33971         },
33972
33973         /**
33974          * Sends the specified dialog to the back
33975          * @param {String/Object} dlg The id of the dialog or a dialog
33976          * @return {Roo.BasicDialog} this
33977          */
33978         sendToBack : function(dlg){
33979             dlg = this.get(dlg);
33980             dlg._lastAccess = -(new Date().getTime());
33981             orderDialogs();
33982             return dlg;
33983         },
33984
33985         /**
33986          * Hides all dialogs
33987          */
33988         hideAll : function(){
33989             for(var id in list){
33990                 if(list[id] && typeof list[id] != "function" && list[id].isVisible()){
33991                     list[id].hide();
33992                 }
33993             }
33994         }
33995     };
33996 }();
33997
33998 /**
33999  * @class Roo.LayoutDialog
34000  * @extends Roo.BasicDialog
34001  * @children Roo.ContentPanel
34002  * @parent builder none
34003  * Dialog which provides adjustments for working with a layout in a Dialog.
34004  * Add your necessary layout config options to the dialog's config.<br>
34005  * Example usage (including a nested layout):
34006  * <pre><code>
34007 if(!dialog){
34008     dialog = new Roo.LayoutDialog("download-dlg", {
34009         modal: true,
34010         width:600,
34011         height:450,
34012         shadow:true,
34013         minWidth:500,
34014         minHeight:350,
34015         autoTabs:true,
34016         proxyDrag:true,
34017         // layout config merges with the dialog config
34018         center:{
34019             tabPosition: "top",
34020             alwaysShowTabs: true
34021         }
34022     });
34023     dialog.addKeyListener(27, dialog.hide, dialog);
34024     dialog.setDefaultButton(dialog.addButton("Close", dialog.hide, dialog));
34025     dialog.addButton("Build It!", this.getDownload, this);
34026
34027     // we can even add nested layouts
34028     var innerLayout = new Roo.BorderLayout("dl-inner", {
34029         east: {
34030             initialSize: 200,
34031             autoScroll:true,
34032             split:true
34033         },
34034         center: {
34035             autoScroll:true
34036         }
34037     });
34038     innerLayout.beginUpdate();
34039     innerLayout.add("east", new Roo.ContentPanel("dl-details"));
34040     innerLayout.add("center", new Roo.ContentPanel("selection-panel"));
34041     innerLayout.endUpdate(true);
34042
34043     var layout = dialog.getLayout();
34044     layout.beginUpdate();
34045     layout.add("center", new Roo.ContentPanel("standard-panel",
34046                         {title: "Download the Source", fitToFrame:true}));
34047     layout.add("center", new Roo.NestedLayoutPanel(innerLayout,
34048                {title: "Build your own roo.js"}));
34049     layout.getRegion("center").showPanel(sp);
34050     layout.endUpdate();
34051 }
34052 </code></pre>
34053     * @constructor
34054     * @param {String/HTMLElement/Roo.Element} el The id of or container element, or config
34055     * @param {Object} config configuration options
34056   */
34057 Roo.LayoutDialog = function(el, cfg){
34058     
34059     var config=  cfg;
34060     if (typeof(cfg) == 'undefined') {
34061         config = Roo.apply({}, el);
34062         // not sure why we use documentElement here.. - it should always be body.
34063         // IE7 borks horribly if we use documentElement.
34064         // webkit also does not like documentElement - it creates a body element...
34065         el = Roo.get( document.body || document.documentElement ).createChild();
34066         //config.autoCreate = true;
34067     }
34068     
34069     
34070     config.autoTabs = false;
34071     Roo.LayoutDialog.superclass.constructor.call(this, el, config);
34072     this.body.setStyle({overflow:"hidden", position:"relative"});
34073     this.layout = new Roo.BorderLayout(this.body.dom, config);
34074     this.layout.monitorWindowResize = false;
34075     this.el.addClass("x-dlg-auto-layout");
34076     // fix case when center region overwrites center function
34077     this.center = Roo.BasicDialog.prototype.center;
34078     this.on("show", this.layout.layout, this.layout, true);
34079     if (config.items) {
34080         var xitems = config.items;
34081         delete config.items;
34082         Roo.each(xitems, this.addxtype, this);
34083     }
34084     
34085     
34086 };
34087 Roo.extend(Roo.LayoutDialog, Roo.BasicDialog, {
34088     
34089     
34090     /**
34091      * @cfg {Roo.LayoutRegion} east  
34092      */
34093     /**
34094      * @cfg {Roo.LayoutRegion} west
34095      */
34096     /**
34097      * @cfg {Roo.LayoutRegion} south
34098      */
34099     /**
34100      * @cfg {Roo.LayoutRegion} north
34101      */
34102     /**
34103      * @cfg {Roo.LayoutRegion} center
34104      */
34105     /**
34106      * @cfg {Roo.Button} buttons[]  Bottom buttons..
34107      */
34108     
34109     
34110     /**
34111      * Ends update of the layout <strike>and resets display to none</strike>. Use standard beginUpdate/endUpdate on the layout.
34112      * @deprecated
34113      */
34114     endUpdate : function(){
34115         this.layout.endUpdate();
34116     },
34117
34118     /**
34119      * Begins an update of the layout <strike>and sets display to block and visibility to hidden</strike>. Use standard beginUpdate/endUpdate on the layout.
34120      *  @deprecated
34121      */
34122     beginUpdate : function(){
34123         this.layout.beginUpdate();
34124     },
34125
34126     /**
34127      * Get the BorderLayout for this dialog
34128      * @return {Roo.BorderLayout}
34129      */
34130     getLayout : function(){
34131         return this.layout;
34132     },
34133
34134     showEl : function(){
34135         Roo.LayoutDialog.superclass.showEl.apply(this, arguments);
34136         if(Roo.isIE7){
34137             this.layout.layout();
34138         }
34139     },
34140
34141     // private
34142     // Use the syncHeightBeforeShow config option to control this automatically
34143     syncBodyHeight : function(){
34144         Roo.LayoutDialog.superclass.syncBodyHeight.call(this);
34145         if(this.layout){this.layout.layout();}
34146     },
34147     
34148       /**
34149      * Add an xtype element (actually adds to the layout.)
34150      * @return {Object} xdata xtype object data.
34151      */
34152     
34153     addxtype : function(c) {
34154         return this.layout.addxtype(c);
34155     }
34156 });/*
34157  * Based on:
34158  * Ext JS Library 1.1.1
34159  * Copyright(c) 2006-2007, Ext JS, LLC.
34160  *
34161  * Originally Released Under LGPL - original licence link has changed is not relivant.
34162  *
34163  * Fork - LGPL
34164  * <script type="text/javascript">
34165  */
34166  
34167 /**
34168  * @class Roo.MessageBox
34169  * Utility class for generating different styles of message boxes.  The alias Roo.Msg can also be used.
34170  * Example usage:
34171  *<pre><code>
34172 // Basic alert:
34173 Roo.Msg.alert('Status', 'Changes saved successfully.');
34174
34175 // Prompt for user data:
34176 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
34177     if (btn == 'ok'){
34178         // process text value...
34179     }
34180 });
34181
34182 // Show a dialog using config options:
34183 Roo.Msg.show({
34184    title:'Save Changes?',
34185    msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
34186    buttons: Roo.Msg.YESNOCANCEL,
34187    fn: processResult,
34188    animEl: 'elId'
34189 });
34190 </code></pre>
34191  * @static
34192  */
34193 Roo.MessageBox = function(){
34194     var dlg, opt, mask, waitTimer;
34195     var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
34196     var buttons, activeTextEl, bwidth;
34197
34198     // private
34199     var handleButton = function(button){
34200         dlg.hide();
34201         Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
34202     };
34203
34204     // private
34205     var handleHide = function(){
34206         if(opt && opt.cls){
34207             dlg.el.removeClass(opt.cls);
34208         }
34209         if(waitTimer){
34210             Roo.TaskMgr.stop(waitTimer);
34211             waitTimer = null;
34212         }
34213     };
34214
34215     // private
34216     var updateButtons = function(b){
34217         var width = 0;
34218         if(!b){
34219             buttons["ok"].hide();
34220             buttons["cancel"].hide();
34221             buttons["yes"].hide();
34222             buttons["no"].hide();
34223             dlg.footer.dom.style.display = 'none';
34224             return width;
34225         }
34226         dlg.footer.dom.style.display = '';
34227         for(var k in buttons){
34228             if(typeof buttons[k] != "function"){
34229                 if(b[k]){
34230                     buttons[k].show();
34231                     buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.MessageBox.buttonText[k]);
34232                     width += buttons[k].el.getWidth()+15;
34233                 }else{
34234                     buttons[k].hide();
34235                 }
34236             }
34237         }
34238         return width;
34239     };
34240
34241     // private
34242     var handleEsc = function(d, k, e){
34243         if(opt && opt.closable !== false){
34244             dlg.hide();
34245         }
34246         if(e){
34247             e.stopEvent();
34248         }
34249     };
34250
34251     return {
34252         /**
34253          * Returns a reference to the underlying {@link Roo.BasicDialog} element
34254          * @return {Roo.BasicDialog} The BasicDialog element
34255          */
34256         getDialog : function(){
34257            if(!dlg){
34258                 dlg = new Roo.BasicDialog("x-msg-box", {
34259                     autoCreate : true,
34260                     shadow: true,
34261                     draggable: true,
34262                     resizable:false,
34263                     constraintoviewport:false,
34264                     fixedcenter:true,
34265                     collapsible : false,
34266                     shim:true,
34267                     modal: true,
34268                     width:400, height:100,
34269                     buttonAlign:"center",
34270                     closeClick : function(){
34271                         if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
34272                             handleButton("no");
34273                         }else{
34274                             handleButton("cancel");
34275                         }
34276                     }
34277                 });
34278                 dlg.on("hide", handleHide);
34279                 mask = dlg.mask;
34280                 dlg.addKeyListener(27, handleEsc);
34281                 buttons = {};
34282                 var bt = this.buttonText;
34283                 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
34284                 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
34285                 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
34286                 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
34287                 bodyEl = dlg.body.createChild({
34288
34289                     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>'
34290                 });
34291                 msgEl = bodyEl.dom.firstChild;
34292                 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
34293                 textboxEl.enableDisplayMode();
34294                 textboxEl.addKeyListener([10,13], function(){
34295                     if(dlg.isVisible() && opt && opt.buttons){
34296                         if(opt.buttons.ok){
34297                             handleButton("ok");
34298                         }else if(opt.buttons.yes){
34299                             handleButton("yes");
34300                         }
34301                     }
34302                 });
34303                 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
34304                 textareaEl.enableDisplayMode();
34305                 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
34306                 progressEl.enableDisplayMode();
34307                 var pf = progressEl.dom.firstChild;
34308                 if (pf) {
34309                     pp = Roo.get(pf.firstChild);
34310                     pp.setHeight(pf.offsetHeight);
34311                 }
34312                 
34313             }
34314             return dlg;
34315         },
34316
34317         /**
34318          * Updates the message box body text
34319          * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
34320          * the XHTML-compliant non-breaking space character '&amp;#160;')
34321          * @return {Roo.MessageBox} This message box
34322          */
34323         updateText : function(text){
34324             if(!dlg.isVisible() && !opt.width){
34325                 dlg.resizeTo(this.maxWidth, 100); // resize first so content is never clipped from previous shows
34326             }
34327             msgEl.innerHTML = text || '&#160;';
34328       
34329             var cw =  Math.max(msgEl.offsetWidth, msgEl.parentNode.scrollWidth);
34330             //Roo.log("guesed size: " + JSON.stringify([cw,msgEl.offsetWidth, msgEl.parentNode.scrollWidth]));
34331             var w = Math.max(
34332                     Math.min(opt.width || cw , this.maxWidth), 
34333                     Math.max(opt.minWidth || this.minWidth, bwidth)
34334             );
34335             if(opt.prompt){
34336                 activeTextEl.setWidth(w);
34337             }
34338             if(dlg.isVisible()){
34339                 dlg.fixedcenter = false;
34340             }
34341             // to big, make it scroll. = But as usual stupid IE does not support
34342             // !important..
34343             
34344             if ( bodyEl.getHeight() > (Roo.lib.Dom.getViewHeight() - 100)) {
34345                 bodyEl.setHeight ( Roo.lib.Dom.getViewHeight() - 100 );
34346                 bodyEl.dom.style.overflowY = 'auto' + ( Roo.isIE ? '' : ' !important');
34347             } else {
34348                 bodyEl.dom.style.height = '';
34349                 bodyEl.dom.style.overflowY = '';
34350             }
34351             if (cw > w) {
34352                 bodyEl.dom.style.get = 'auto' + ( Roo.isIE ? '' : ' !important');
34353             } else {
34354                 bodyEl.dom.style.overflowX = '';
34355             }
34356             
34357             dlg.setContentSize(w, bodyEl.getHeight());
34358             if(dlg.isVisible()){
34359                 dlg.fixedcenter = true;
34360             }
34361             return this;
34362         },
34363
34364         /**
34365          * Updates a progress-style message box's text and progress bar.  Only relevant on message boxes
34366          * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
34367          * @param {Number} value Any number between 0 and 1 (e.g., .5)
34368          * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
34369          * @return {Roo.MessageBox} This message box
34370          */
34371         updateProgress : function(value, text){
34372             if(text){
34373                 this.updateText(text);
34374             }
34375             if (pp) { // weird bug on my firefox - for some reason this is not defined
34376                 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
34377             }
34378             return this;
34379         },        
34380
34381         /**
34382          * Returns true if the message box is currently displayed
34383          * @return {Boolean} True if the message box is visible, else false
34384          */
34385         isVisible : function(){
34386             return dlg && dlg.isVisible();  
34387         },
34388
34389         /**
34390          * Hides the message box if it is displayed
34391          */
34392         hide : function(){
34393             if(this.isVisible()){
34394                 dlg.hide();
34395             }  
34396         },
34397
34398         /**
34399          * Displays a new message box, or reinitializes an existing message box, based on the config options
34400          * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
34401          * The following config object properties are supported:
34402          * <pre>
34403 Property    Type             Description
34404 ----------  ---------------  ------------------------------------------------------------------------------------
34405 animEl            String/Element   An id or Element from which the message box should animate as it opens and
34406                                    closes (defaults to undefined)
34407 buttons           Object/Boolean   A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
34408                                    cancel:'Bar'}), or false to not show any buttons (defaults to false)
34409 closable          Boolean          False to hide the top-right close button (defaults to true).  Note that
34410                                    progress and wait dialogs will ignore this property and always hide the
34411                                    close button as they can only be closed programmatically.
34412 cls               String           A custom CSS class to apply to the message box element
34413 defaultTextHeight Number           The default height in pixels of the message box's multiline textarea if
34414                                    displayed (defaults to 75)
34415 fn                Function         A callback function to execute after closing the dialog.  The arguments to the
34416                                    function will be btn (the name of the button that was clicked, if applicable,
34417                                    e.g. "ok"), and text (the value of the active text field, if applicable).
34418                                    Progress and wait dialogs will ignore this option since they do not respond to
34419                                    user actions and can only be closed programmatically, so any required function
34420                                    should be called by the same code after it closes the dialog.
34421 icon              String           A CSS class that provides a background image to be used as an icon for
34422                                    the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
34423 maxWidth          Number           The maximum width in pixels of the message box (defaults to 600)
34424 minWidth          Number           The minimum width in pixels of the message box (defaults to 100)
34425 modal             Boolean          False to allow user interaction with the page while the message box is
34426                                    displayed (defaults to true)
34427 msg               String           A string that will replace the existing message box body text (defaults
34428                                    to the XHTML-compliant non-breaking space character '&#160;')
34429 multiline         Boolean          True to prompt the user to enter multi-line text (defaults to false)
34430 progress          Boolean          True to display a progress bar (defaults to false)
34431 progressText      String           The text to display inside the progress bar if progress = true (defaults to '')
34432 prompt            Boolean          True to prompt the user to enter single-line text (defaults to false)
34433 proxyDrag         Boolean          True to display a lightweight proxy while dragging (defaults to false)
34434 title             String           The title text
34435 value             String           The string value to set into the active textbox element if displayed
34436 wait              Boolean          True to display a progress bar (defaults to false)
34437 width             Number           The width of the dialog in pixels
34438 </pre>
34439          *
34440          * Example usage:
34441          * <pre><code>
34442 Roo.Msg.show({
34443    title: 'Address',
34444    msg: 'Please enter your address:',
34445    width: 300,
34446    buttons: Roo.MessageBox.OKCANCEL,
34447    multiline: true,
34448    fn: saveAddress,
34449    animEl: 'addAddressBtn'
34450 });
34451 </code></pre>
34452          * @param {Object} config Configuration options
34453          * @return {Roo.MessageBox} This message box
34454          */
34455         show : function(options)
34456         {
34457             
34458             // this causes nightmares if you show one dialog after another
34459             // especially on callbacks..
34460              
34461             if(this.isVisible()){
34462                 
34463                 this.hide();
34464                 Roo.log("[Roo.Messagebox] Show called while message displayed:" );
34465                 Roo.log("Old Dialog Message:" +  msgEl.innerHTML );
34466                 Roo.log("New Dialog Message:" +  options.msg )
34467                 //this.alert("ERROR", "Multiple dialogs where displayed at the same time");
34468                 //throw "Roo.MessageBox ERROR : Multiple dialogs where displayed at the same time";
34469                 
34470             }
34471             var d = this.getDialog();
34472             opt = options;
34473             d.setTitle(opt.title || "&#160;");
34474             d.close.setDisplayed(opt.closable !== false);
34475             activeTextEl = textboxEl;
34476             opt.prompt = opt.prompt || (opt.multiline ? true : false);
34477             if(opt.prompt){
34478                 if(opt.multiline){
34479                     textboxEl.hide();
34480                     textareaEl.show();
34481                     textareaEl.setHeight(typeof opt.multiline == "number" ?
34482                         opt.multiline : this.defaultTextHeight);
34483                     activeTextEl = textareaEl;
34484                 }else{
34485                     textboxEl.show();
34486                     textareaEl.hide();
34487                 }
34488             }else{
34489                 textboxEl.hide();
34490                 textareaEl.hide();
34491             }
34492             progressEl.setDisplayed(opt.progress === true);
34493             this.updateProgress(0);
34494             activeTextEl.dom.value = opt.value || "";
34495             if(opt.prompt){
34496                 dlg.setDefaultButton(activeTextEl);
34497             }else{
34498                 var bs = opt.buttons;
34499                 var db = null;
34500                 if(bs && bs.ok){
34501                     db = buttons["ok"];
34502                 }else if(bs && bs.yes){
34503                     db = buttons["yes"];
34504                 }
34505                 dlg.setDefaultButton(db);
34506             }
34507             bwidth = updateButtons(opt.buttons);
34508             this.updateText(opt.msg);
34509             if(opt.cls){
34510                 d.el.addClass(opt.cls);
34511             }
34512             d.proxyDrag = opt.proxyDrag === true;
34513             d.modal = opt.modal !== false;
34514             d.mask = opt.modal !== false ? mask : false;
34515             if(!d.isVisible()){
34516                 // force it to the end of the z-index stack so it gets a cursor in FF
34517                 document.body.appendChild(dlg.el.dom);
34518                 d.animateTarget = null;
34519                 d.show(options.animEl);
34520             }
34521             return this;
34522         },
34523
34524         /**
34525          * Displays a message box with a progress bar.  This message box has no buttons and is not closeable by
34526          * the user.  You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
34527          * and closing the message box when the process is complete.
34528          * @param {String} title The title bar text
34529          * @param {String} msg The message box body text
34530          * @return {Roo.MessageBox} This message box
34531          */
34532         progress : function(title, msg){
34533             this.show({
34534                 title : title,
34535                 msg : msg,
34536                 buttons: false,
34537                 progress:true,
34538                 closable:false,
34539                 minWidth: this.minProgressWidth,
34540                 modal : true
34541             });
34542             return this;
34543         },
34544
34545         /**
34546          * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
34547          * If a callback function is passed it will be called after the user clicks the button, and the
34548          * id of the button that was clicked will be passed as the only parameter to the callback
34549          * (could also be the top-right close button).
34550          * @param {String} title The title bar text
34551          * @param {String} msg The message box body text
34552          * @param {Function} fn (optional) The callback function invoked after the message box is closed
34553          * @param {Object} scope (optional) The scope of the callback function
34554          * @return {Roo.MessageBox} This message box
34555          */
34556         alert : function(title, msg, fn, scope){
34557             this.show({
34558                 title : title,
34559                 msg : msg,
34560                 buttons: this.OK,
34561                 fn: fn,
34562                 scope : scope,
34563                 modal : true
34564             });
34565             return this;
34566         },
34567
34568         /**
34569          * Displays a message box with an infinitely auto-updating progress bar.  This can be used to block user
34570          * interaction while waiting for a long-running process to complete that does not have defined intervals.
34571          * You are responsible for closing the message box when the process is complete.
34572          * @param {String} msg The message box body text
34573          * @param {String} title (optional) The title bar text
34574          * @return {Roo.MessageBox} This message box
34575          */
34576         wait : function(msg, title){
34577             this.show({
34578                 title : title,
34579                 msg : msg,
34580                 buttons: false,
34581                 closable:false,
34582                 progress:true,
34583                 modal:true,
34584                 width:300,
34585                 wait:true
34586             });
34587             waitTimer = Roo.TaskMgr.start({
34588                 run: function(i){
34589                     Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
34590                 },
34591                 interval: 1000
34592             });
34593             return this;
34594         },
34595
34596         /**
34597          * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
34598          * If a callback function is passed it will be called after the user clicks either button, and the id of the
34599          * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
34600          * @param {String} title The title bar text
34601          * @param {String} msg The message box body text
34602          * @param {Function} fn (optional) The callback function invoked after the message box is closed
34603          * @param {Object} scope (optional) The scope of the callback function
34604          * @return {Roo.MessageBox} This message box
34605          */
34606         confirm : function(title, msg, fn, scope){
34607             this.show({
34608                 title : title,
34609                 msg : msg,
34610                 buttons: this.YESNO,
34611                 fn: fn,
34612                 scope : scope,
34613                 modal : true
34614             });
34615             return this;
34616         },
34617
34618         /**
34619          * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
34620          * JavaScript's Window.prompt).  The prompt can be a single-line or multi-line textbox.  If a callback function
34621          * is passed it will be called after the user clicks either button, and the id of the button that was clicked
34622          * (could also be the top-right close button) and the text that was entered will be passed as the two
34623          * parameters to the callback.
34624          * @param {String} title The title bar text
34625          * @param {String} msg The message box body text
34626          * @param {Function} fn (optional) The callback function invoked after the message box is closed
34627          * @param {Object} scope (optional) The scope of the callback function
34628          * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
34629          * property, or the height in pixels to create the textbox (defaults to false / single-line)
34630          * @return {Roo.MessageBox} This message box
34631          */
34632         prompt : function(title, msg, fn, scope, multiline){
34633             this.show({
34634                 title : title,
34635                 msg : msg,
34636                 buttons: this.OKCANCEL,
34637                 fn: fn,
34638                 minWidth:250,
34639                 scope : scope,
34640                 prompt:true,
34641                 multiline: multiline,
34642                 modal : true
34643             });
34644             return this;
34645         },
34646
34647         /**
34648          * Button config that displays a single OK button
34649          * @type Object
34650          */
34651         OK : {ok:true},
34652         /**
34653          * Button config that displays Yes and No buttons
34654          * @type Object
34655          */
34656         YESNO : {yes:true, no:true},
34657         /**
34658          * Button config that displays OK and Cancel buttons
34659          * @type Object
34660          */
34661         OKCANCEL : {ok:true, cancel:true},
34662         /**
34663          * Button config that displays Yes, No and Cancel buttons
34664          * @type Object
34665          */
34666         YESNOCANCEL : {yes:true, no:true, cancel:true},
34667
34668         /**
34669          * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
34670          * @type Number
34671          */
34672         defaultTextHeight : 75,
34673         /**
34674          * The maximum width in pixels of the message box (defaults to 600)
34675          * @type Number
34676          */
34677         maxWidth : 600,
34678         /**
34679          * The minimum width in pixels of the message box (defaults to 100)
34680          * @type Number
34681          */
34682         minWidth : 100,
34683         /**
34684          * The minimum width in pixels of the message box if it is a progress-style dialog.  This is useful
34685          * for setting a different minimum width than text-only dialogs may need (defaults to 250)
34686          * @type Number
34687          */
34688         minProgressWidth : 250,
34689         /**
34690          * An object containing the default button text strings that can be overriden for localized language support.
34691          * Supported properties are: ok, cancel, yes and no.
34692          * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
34693          * @type Object
34694          */
34695         buttonText : {
34696             ok : "OK",
34697             cancel : "Cancel",
34698             yes : "Yes",
34699             no : "No"
34700         }
34701     };
34702 }();
34703
34704 /**
34705  * Shorthand for {@link Roo.MessageBox}
34706  */
34707 Roo.Msg = Roo.MessageBox;/*
34708  * Based on:
34709  * Ext JS Library 1.1.1
34710  * Copyright(c) 2006-2007, Ext JS, LLC.
34711  *
34712  * Originally Released Under LGPL - original licence link has changed is not relivant.
34713  *
34714  * Fork - LGPL
34715  * <script type="text/javascript">
34716  */
34717 /**
34718  * @class Roo.QuickTips
34719  * Provides attractive and customizable tooltips for any element.
34720  * @static
34721  */
34722 Roo.QuickTips = function(){
34723     var el, tipBody, tipBodyText, tipTitle, tm, cfg, close, tagEls = {}, esc, removeCls = null, bdLeft, bdRight;
34724     var ce, bd, xy, dd;
34725     var visible = false, disabled = true, inited = false;
34726     var showProc = 1, hideProc = 1, dismissProc = 1, locks = [];
34727     
34728     var onOver = function(e){
34729         if(disabled){
34730             return;
34731         }
34732         var t = e.getTarget();
34733         if(!t || t.nodeType !== 1 || t == document || t == document.body){
34734             return;
34735         }
34736         if(ce && t == ce.el){
34737             clearTimeout(hideProc);
34738             return;
34739         }
34740         if(t && tagEls[t.id]){
34741             tagEls[t.id].el = t;
34742             showProc = show.defer(tm.showDelay, tm, [tagEls[t.id]]);
34743             return;
34744         }
34745         var ttp, et = Roo.fly(t);
34746         var ns = cfg.namespace;
34747         if(tm.interceptTitles && t.title){
34748             ttp = t.title;
34749             t.qtip = ttp;
34750             t.removeAttribute("title");
34751             e.preventDefault();
34752         }else{
34753             ttp = t.qtip || et.getAttributeNS(ns, cfg.attribute) || et.getAttributeNS(cfg.alt_namespace, cfg.attribute) ;
34754         }
34755         if(ttp){
34756             showProc = show.defer(tm.showDelay, tm, [{
34757                 el: t, 
34758                 text: ttp.replace(/\\n/g,'<br/>'),
34759                 width: et.getAttributeNS(ns, cfg.width),
34760                 autoHide: et.getAttributeNS(ns, cfg.hide) != "user",
34761                 title: et.getAttributeNS(ns, cfg.title),
34762                     cls: et.getAttributeNS(ns, cfg.cls)
34763             }]);
34764         }
34765     };
34766     
34767     var onOut = function(e){
34768         clearTimeout(showProc);
34769         var t = e.getTarget();
34770         if(t && ce && ce.el == t && (tm.autoHide && ce.autoHide !== false)){
34771             hideProc = setTimeout(hide, tm.hideDelay);
34772         }
34773     };
34774     
34775     var onMove = function(e){
34776         if(disabled){
34777             return;
34778         }
34779         xy = e.getXY();
34780         xy[1] += 18;
34781         if(tm.trackMouse && ce){
34782             el.setXY(xy);
34783         }
34784     };
34785     
34786     var onDown = function(e){
34787         clearTimeout(showProc);
34788         clearTimeout(hideProc);
34789         if(!e.within(el)){
34790             if(tm.hideOnClick){
34791                 hide();
34792                 tm.disable();
34793                 tm.enable.defer(100, tm);
34794             }
34795         }
34796     };
34797     
34798     var getPad = function(){
34799         return 2;//bdLeft.getPadding('l')+bdRight.getPadding('r');
34800     };
34801
34802     var show = function(o){
34803         if(disabled){
34804             return;
34805         }
34806         clearTimeout(dismissProc);
34807         ce = o;
34808         if(removeCls){ // in case manually hidden
34809             el.removeClass(removeCls);
34810             removeCls = null;
34811         }
34812         if(ce.cls){
34813             el.addClass(ce.cls);
34814             removeCls = ce.cls;
34815         }
34816         if(ce.title){
34817             tipTitle.update(ce.title);
34818             tipTitle.show();
34819         }else{
34820             tipTitle.update('');
34821             tipTitle.hide();
34822         }
34823         el.dom.style.width  = tm.maxWidth+'px';
34824         //tipBody.dom.style.width = '';
34825         tipBodyText.update(o.text);
34826         var p = getPad(), w = ce.width;
34827         if(!w){
34828             var td = tipBodyText.dom;
34829             var aw = Math.max(td.offsetWidth, td.clientWidth, td.scrollWidth);
34830             if(aw > tm.maxWidth){
34831                 w = tm.maxWidth;
34832             }else if(aw < tm.minWidth){
34833                 w = tm.minWidth;
34834             }else{
34835                 w = aw;
34836             }
34837         }
34838         //tipBody.setWidth(w);
34839         el.setWidth(parseInt(w, 10) + p);
34840         if(ce.autoHide === false){
34841             close.setDisplayed(true);
34842             if(dd){
34843                 dd.unlock();
34844             }
34845         }else{
34846             close.setDisplayed(false);
34847             if(dd){
34848                 dd.lock();
34849             }
34850         }
34851         if(xy){
34852             el.avoidY = xy[1]-18;
34853             el.setXY(xy);
34854         }
34855         if(tm.animate){
34856             el.setOpacity(.1);
34857             el.setStyle("visibility", "visible");
34858             el.fadeIn({callback: afterShow});
34859         }else{
34860             afterShow();
34861         }
34862     };
34863     
34864     var afterShow = function(){
34865         if(ce){
34866             el.show();
34867             esc.enable();
34868             if(tm.autoDismiss && ce.autoHide !== false){
34869                 dismissProc = setTimeout(hide, tm.autoDismissDelay);
34870             }
34871         }
34872     };
34873     
34874     var hide = function(noanim){
34875         clearTimeout(dismissProc);
34876         clearTimeout(hideProc);
34877         ce = null;
34878         if(el.isVisible()){
34879             esc.disable();
34880             if(noanim !== true && tm.animate){
34881                 el.fadeOut({callback: afterHide});
34882             }else{
34883                 afterHide();
34884             } 
34885         }
34886     };
34887     
34888     var afterHide = function(){
34889         el.hide();
34890         if(removeCls){
34891             el.removeClass(removeCls);
34892             removeCls = null;
34893         }
34894     };
34895     
34896     return {
34897         /**
34898         * @cfg {Number} minWidth
34899         * The minimum width of the quick tip (defaults to 40)
34900         */
34901        minWidth : 40,
34902         /**
34903         * @cfg {Number} maxWidth
34904         * The maximum width of the quick tip (defaults to 300)
34905         */
34906        maxWidth : 300,
34907         /**
34908         * @cfg {Boolean} interceptTitles
34909         * True to automatically use the element's DOM title value if available (defaults to false)
34910         */
34911        interceptTitles : false,
34912         /**
34913         * @cfg {Boolean} trackMouse
34914         * True to have the quick tip follow the mouse as it moves over the target element (defaults to false)
34915         */
34916        trackMouse : false,
34917         /**
34918         * @cfg {Boolean} hideOnClick
34919         * True to hide the quick tip if the user clicks anywhere in the document (defaults to true)
34920         */
34921        hideOnClick : true,
34922         /**
34923         * @cfg {Number} showDelay
34924         * Delay in milliseconds before the quick tip displays after the mouse enters the target element (defaults to 500)
34925         */
34926        showDelay : 500,
34927         /**
34928         * @cfg {Number} hideDelay
34929         * Delay in milliseconds before the quick tip hides when autoHide = true (defaults to 200)
34930         */
34931        hideDelay : 200,
34932         /**
34933         * @cfg {Boolean} autoHide
34934         * True to automatically hide the quick tip after the mouse exits the target element (defaults to true).
34935         * Used in conjunction with hideDelay.
34936         */
34937        autoHide : true,
34938         /**
34939         * @cfg {Boolean}
34940         * True to automatically hide the quick tip after a set period of time, regardless of the user's actions
34941         * (defaults to true).  Used in conjunction with autoDismissDelay.
34942         */
34943        autoDismiss : true,
34944         /**
34945         * @cfg {Number}
34946         * Delay in milliseconds before the quick tip hides when autoDismiss = true (defaults to 5000)
34947         */
34948        autoDismissDelay : 5000,
34949        /**
34950         * @cfg {Boolean} animate
34951         * True to turn on fade animation. Defaults to false (ClearType/scrollbar flicker issues in IE7).
34952         */
34953        animate : false,
34954
34955        /**
34956         * @cfg {String} title
34957         * Title text to display (defaults to '').  This can be any valid HTML markup.
34958         */
34959         title: '',
34960        /**
34961         * @cfg {String} text
34962         * Body text to display (defaults to '').  This can be any valid HTML markup.
34963         */
34964         text : '',
34965        /**
34966         * @cfg {String} cls
34967         * A CSS class to apply to the base quick tip element (defaults to '').
34968         */
34969         cls : '',
34970        /**
34971         * @cfg {Number} width
34972         * Width in pixels of the quick tip (defaults to auto).  Width will be ignored if it exceeds the bounds of
34973         * minWidth or maxWidth.
34974         */
34975         width : null,
34976
34977     /**
34978      * Initialize and enable QuickTips for first use.  This should be called once before the first attempt to access
34979      * or display QuickTips in a page.
34980      */
34981        init : function(){
34982           tm = Roo.QuickTips;
34983           cfg = tm.tagConfig;
34984           if(!inited){
34985               if(!Roo.isReady){ // allow calling of init() before onReady
34986                   Roo.onReady(Roo.QuickTips.init, Roo.QuickTips);
34987                   return;
34988               }
34989               el = new Roo.Layer({cls:"x-tip", shadow:"drop", shim: true, constrain:true, shadowOffset:4});
34990               el.fxDefaults = {stopFx: true};
34991               // maximum custom styling
34992               //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>');
34993               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>');              
34994               tipTitle = el.child('h3');
34995               tipTitle.enableDisplayMode("block");
34996               tipBody = el.child('div.x-tip-bd');
34997               tipBodyText = el.child('div.x-tip-bd-inner');
34998               //bdLeft = el.child('div.x-tip-bd-left');
34999               //bdRight = el.child('div.x-tip-bd-right');
35000               close = el.child('div.x-tip-close');
35001               close.enableDisplayMode("block");
35002               close.on("click", hide);
35003               var d = Roo.get(document);
35004               d.on("mousedown", onDown);
35005               d.on("mouseover", onOver);
35006               d.on("mouseout", onOut);
35007               d.on("mousemove", onMove);
35008               esc = d.addKeyListener(27, hide);
35009               esc.disable();
35010               if(Roo.dd.DD){
35011                   dd = el.initDD("default", null, {
35012                       onDrag : function(){
35013                           el.sync();  
35014                       }
35015                   });
35016                   dd.setHandleElId(tipTitle.id);
35017                   dd.lock();
35018               }
35019               inited = true;
35020           }
35021           this.enable(); 
35022        },
35023
35024     /**
35025      * Configures a new quick tip instance and assigns it to a target element.  The following config options
35026      * are supported:
35027      * <pre>
35028 Property    Type                   Description
35029 ----------  ---------------------  ------------------------------------------------------------------------
35030 target      Element/String/Array   An Element, id or array of ids that this quick tip should be tied to
35031      * </ul>
35032      * @param {Object} config The config object
35033      */
35034        register : function(config){
35035            var cs = config instanceof Array ? config : arguments;
35036            for(var i = 0, len = cs.length; i < len; i++) {
35037                var c = cs[i];
35038                var target = c.target;
35039                if(target){
35040                    if(target instanceof Array){
35041                        for(var j = 0, jlen = target.length; j < jlen; j++){
35042                            tagEls[target[j]] = c;
35043                        }
35044                    }else{
35045                        tagEls[typeof target == 'string' ? target : Roo.id(target)] = c;
35046                    }
35047                }
35048            }
35049        },
35050
35051     /**
35052      * Removes this quick tip from its element and destroys it.
35053      * @param {String/HTMLElement/Element} el The element from which the quick tip is to be removed.
35054      */
35055        unregister : function(el){
35056            delete tagEls[Roo.id(el)];
35057        },
35058
35059     /**
35060      * Enable this quick tip.
35061      */
35062        enable : function(){
35063            if(inited && disabled){
35064                locks.pop();
35065                if(locks.length < 1){
35066                    disabled = false;
35067                }
35068            }
35069        },
35070
35071     /**
35072      * Disable this quick tip.
35073      */
35074        disable : function(){
35075           disabled = true;
35076           clearTimeout(showProc);
35077           clearTimeout(hideProc);
35078           clearTimeout(dismissProc);
35079           if(ce){
35080               hide(true);
35081           }
35082           locks.push(1);
35083        },
35084
35085     /**
35086      * Returns true if the quick tip is enabled, else false.
35087      */
35088        isEnabled : function(){
35089             return !disabled;
35090        },
35091
35092         // private
35093        tagConfig : {
35094            namespace : "roo", // was ext?? this may break..
35095            alt_namespace : "ext",
35096            attribute : "qtip",
35097            width : "width",
35098            target : "target",
35099            title : "qtitle",
35100            hide : "hide",
35101            cls : "qclass"
35102        }
35103    };
35104 }();
35105
35106 // backwards compat
35107 Roo.QuickTips.tips = Roo.QuickTips.register;/*
35108  * Based on:
35109  * Ext JS Library 1.1.1
35110  * Copyright(c) 2006-2007, Ext JS, LLC.
35111  *
35112  * Originally Released Under LGPL - original licence link has changed is not relivant.
35113  *
35114  * Fork - LGPL
35115  * <script type="text/javascript">
35116  */
35117  
35118
35119 /**
35120  * @class Roo.tree.TreePanel
35121  * @extends Roo.data.Tree
35122  * @cfg {Roo.tree.TreeNode} root The root node
35123  * @cfg {Boolean} rootVisible false to hide the root node (defaults to true)
35124  * @cfg {Boolean} lines false to disable tree lines (defaults to true)
35125  * @cfg {Boolean} enableDD true to enable drag and drop
35126  * @cfg {Boolean} enableDrag true to enable just drag
35127  * @cfg {Boolean} enableDrop true to enable just drop
35128  * @cfg {Object} dragConfig Custom config to pass to the {@link Roo.tree.TreeDragZone} instance
35129  * @cfg {Object} dropConfig Custom config to pass to the {@link Roo.tree.TreeDropZone} instance
35130  * @cfg {String} ddGroup The DD group this TreePanel belongs to
35131  * @cfg {String} ddAppendOnly True if the tree should only allow append drops (use for trees which are sorted)
35132  * @cfg {Boolean} ddScroll true to enable YUI body scrolling
35133  * @cfg {Boolean} containerScroll true to register this container with ScrollManager
35134  * @cfg {Boolean} hlDrop false to disable node highlight on drop (defaults to the value of Roo.enableFx)
35135  * @cfg {String} hlColor The color of the node highlight (defaults to C3DAF9)
35136  * @cfg {Boolean} animate true to enable animated expand/collapse (defaults to the value of Roo.enableFx)
35137  * @cfg {Boolean} singleExpand true if only 1 node per branch may be expanded
35138  * @cfg {Boolean} selModel A tree selection model to use with this TreePanel (defaults to a {@link Roo.tree.DefaultSelectionModel})
35139  * @cfg {Roo.tree.TreeLoader} loader A TreeLoader for use with this TreePanel
35140  * @cfg {Roo.tree.TreeEditor} editor The TreeEditor to display when clicked.
35141  * @cfg {String} pathSeparator The token used to separate sub-paths in path strings (defaults to '/')
35142  * @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>
35143  * @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>
35144  * 
35145  * @constructor
35146  * @param {String/HTMLElement/Element} el The container element
35147  * @param {Object} config
35148  */
35149 Roo.tree.TreePanel = function(el, config){
35150     var root = false;
35151     var loader = false;
35152     if (config.root) {
35153         root = config.root;
35154         delete config.root;
35155     }
35156     if (config.loader) {
35157         loader = config.loader;
35158         delete config.loader;
35159     }
35160     
35161     Roo.apply(this, config);
35162     Roo.tree.TreePanel.superclass.constructor.call(this);
35163     this.el = Roo.get(el);
35164     this.el.addClass('x-tree');
35165     //console.log(root);
35166     if (root) {
35167         this.setRootNode( Roo.factory(root, Roo.tree));
35168     }
35169     if (loader) {
35170         this.loader = Roo.factory(loader, Roo.tree);
35171     }
35172    /**
35173     * Read-only. The id of the container element becomes this TreePanel's id.
35174     */
35175     this.id = this.el.id;
35176     this.addEvents({
35177         /**
35178         * @event beforeload
35179         * Fires before a node is loaded, return false to cancel
35180         * @param {Node} node The node being loaded
35181         */
35182         "beforeload" : true,
35183         /**
35184         * @event load
35185         * Fires when a node is loaded
35186         * @param {Node} node The node that was loaded
35187         */
35188         "load" : true,
35189         /**
35190         * @event textchange
35191         * Fires when the text for a node is changed
35192         * @param {Node} node The node
35193         * @param {String} text The new text
35194         * @param {String} oldText The old text
35195         */
35196         "textchange" : true,
35197         /**
35198         * @event beforeexpand
35199         * Fires before a node is expanded, return false to cancel.
35200         * @param {Node} node The node
35201         * @param {Boolean} deep
35202         * @param {Boolean} anim
35203         */
35204         "beforeexpand" : true,
35205         /**
35206         * @event beforecollapse
35207         * Fires before a node is collapsed, return false to cancel.
35208         * @param {Node} node The node
35209         * @param {Boolean} deep
35210         * @param {Boolean} anim
35211         */
35212         "beforecollapse" : true,
35213         /**
35214         * @event expand
35215         * Fires when a node is expanded
35216         * @param {Node} node The node
35217         */
35218         "expand" : true,
35219         /**
35220         * @event disabledchange
35221         * Fires when the disabled status of a node changes
35222         * @param {Node} node The node
35223         * @param {Boolean} disabled
35224         */
35225         "disabledchange" : true,
35226         /**
35227         * @event collapse
35228         * Fires when a node is collapsed
35229         * @param {Node} node The node
35230         */
35231         "collapse" : true,
35232         /**
35233         * @event beforeclick
35234         * Fires before click processing on a node. Return false to cancel the default action.
35235         * @param {Node} node The node
35236         * @param {Roo.EventObject} e The event object
35237         */
35238         "beforeclick":true,
35239         /**
35240         * @event checkchange
35241         * Fires when a node with a checkbox's checked property changes
35242         * @param {Node} this This node
35243         * @param {Boolean} checked
35244         */
35245         "checkchange":true,
35246         /**
35247         * @event click
35248         * Fires when a node is clicked
35249         * @param {Node} node The node
35250         * @param {Roo.EventObject} e The event object
35251         */
35252         "click":true,
35253         /**
35254         * @event dblclick
35255         * Fires when a node is double clicked
35256         * @param {Node} node The node
35257         * @param {Roo.EventObject} e The event object
35258         */
35259         "dblclick":true,
35260         /**
35261         * @event contextmenu
35262         * Fires when a node is right clicked
35263         * @param {Node} node The node
35264         * @param {Roo.EventObject} e The event object
35265         */
35266         "contextmenu":true,
35267         /**
35268         * @event beforechildrenrendered
35269         * Fires right before the child nodes for a node are rendered
35270         * @param {Node} node The node
35271         */
35272         "beforechildrenrendered":true,
35273         /**
35274         * @event startdrag
35275         * Fires when a node starts being dragged
35276         * @param {Roo.tree.TreePanel} this
35277         * @param {Roo.tree.TreeNode} node
35278         * @param {event} e The raw browser event
35279         */ 
35280        "startdrag" : true,
35281        /**
35282         * @event enddrag
35283         * Fires when a drag operation is complete
35284         * @param {Roo.tree.TreePanel} this
35285         * @param {Roo.tree.TreeNode} node
35286         * @param {event} e The raw browser event
35287         */
35288        "enddrag" : true,
35289        /**
35290         * @event dragdrop
35291         * Fires when a dragged node is dropped on a valid DD target
35292         * @param {Roo.tree.TreePanel} this
35293         * @param {Roo.tree.TreeNode} node
35294         * @param {DD} dd The dd it was dropped on
35295         * @param {event} e The raw browser event
35296         */
35297        "dragdrop" : true,
35298        /**
35299         * @event beforenodedrop
35300         * Fires when a DD object is dropped on a node in this tree for preprocessing. Return false to cancel the drop. The dropEvent
35301         * passed to handlers has the following properties:<br />
35302         * <ul style="padding:5px;padding-left:16px;">
35303         * <li>tree - The TreePanel</li>
35304         * <li>target - The node being targeted for the drop</li>
35305         * <li>data - The drag data from the drag source</li>
35306         * <li>point - The point of the drop - append, above or below</li>
35307         * <li>source - The drag source</li>
35308         * <li>rawEvent - Raw mouse event</li>
35309         * <li>dropNode - Drop node(s) provided by the source <b>OR</b> you can supply node(s)
35310         * to be inserted by setting them on this object.</li>
35311         * <li>cancel - Set this to true to cancel the drop.</li>
35312         * </ul>
35313         * @param {Object} dropEvent
35314         */
35315        "beforenodedrop" : true,
35316        /**
35317         * @event nodedrop
35318         * Fires after a DD object is dropped on a node in this tree. The dropEvent
35319         * passed to handlers has the following properties:<br />
35320         * <ul style="padding:5px;padding-left:16px;">
35321         * <li>tree - The TreePanel</li>
35322         * <li>target - The node being targeted for the drop</li>
35323         * <li>data - The drag data from the drag source</li>
35324         * <li>point - The point of the drop - append, above or below</li>
35325         * <li>source - The drag source</li>
35326         * <li>rawEvent - Raw mouse event</li>
35327         * <li>dropNode - Dropped node(s).</li>
35328         * </ul>
35329         * @param {Object} dropEvent
35330         */
35331        "nodedrop" : true,
35332         /**
35333         * @event nodedragover
35334         * Fires when a tree node is being targeted for a drag drop, return false to signal drop not allowed. The dragOverEvent
35335         * passed to handlers has the following properties:<br />
35336         * <ul style="padding:5px;padding-left:16px;">
35337         * <li>tree - The TreePanel</li>
35338         * <li>target - The node being targeted for the drop</li>
35339         * <li>data - The drag data from the drag source</li>
35340         * <li>point - The point of the drop - append, above or below</li>
35341         * <li>source - The drag source</li>
35342         * <li>rawEvent - Raw mouse event</li>
35343         * <li>dropNode - Drop node(s) provided by the source.</li>
35344         * <li>cancel - Set this to true to signal drop not allowed.</li>
35345         * </ul>
35346         * @param {Object} dragOverEvent
35347         */
35348        "nodedragover" : true,
35349        /**
35350         * @event appendnode
35351         * Fires when append node to the tree
35352         * @param {Roo.tree.TreePanel} this
35353         * @param {Roo.tree.TreeNode} node
35354         * @param {Number} index The index of the newly appended node
35355         */
35356        "appendnode" : true
35357         
35358     });
35359     if(this.singleExpand){
35360        this.on("beforeexpand", this.restrictExpand, this);
35361     }
35362     if (this.editor) {
35363         this.editor.tree = this;
35364         this.editor = Roo.factory(this.editor, Roo.tree);
35365     }
35366     
35367     if (this.selModel) {
35368         this.selModel = Roo.factory(this.selModel, Roo.tree);
35369     }
35370    
35371 };
35372 Roo.extend(Roo.tree.TreePanel, Roo.data.Tree, {
35373     rootVisible : true,
35374     animate: Roo.enableFx,
35375     lines : true,
35376     enableDD : false,
35377     hlDrop : Roo.enableFx,
35378   
35379     renderer: false,
35380     
35381     rendererTip: false,
35382     // private
35383     restrictExpand : function(node){
35384         var p = node.parentNode;
35385         if(p){
35386             if(p.expandedChild && p.expandedChild.parentNode == p){
35387                 p.expandedChild.collapse();
35388             }
35389             p.expandedChild = node;
35390         }
35391     },
35392
35393     // private override
35394     setRootNode : function(node){
35395         Roo.tree.TreePanel.superclass.setRootNode.call(this, node);
35396         if(!this.rootVisible){
35397             node.ui = new Roo.tree.RootTreeNodeUI(node);
35398         }
35399         return node;
35400     },
35401
35402     /**
35403      * Returns the container element for this TreePanel
35404      */
35405     getEl : function(){
35406         return this.el;
35407     },
35408
35409     /**
35410      * Returns the default TreeLoader for this TreePanel
35411      */
35412     getLoader : function(){
35413         return this.loader;
35414     },
35415
35416     /**
35417      * Expand all nodes
35418      */
35419     expandAll : function(){
35420         this.root.expand(true);
35421     },
35422
35423     /**
35424      * Collapse all nodes
35425      */
35426     collapseAll : function(){
35427         this.root.collapse(true);
35428     },
35429
35430     /**
35431      * Returns the selection model used by this TreePanel
35432      */
35433     getSelectionModel : function(){
35434         if(!this.selModel){
35435             this.selModel = new Roo.tree.DefaultSelectionModel();
35436         }
35437         return this.selModel;
35438     },
35439
35440     /**
35441      * Retrieve an array of checked nodes, or an array of a specific attribute of checked nodes (e.g. "id")
35442      * @param {String} attribute (optional) Defaults to null (return the actual nodes)
35443      * @param {TreeNode} startNode (optional) The node to start from, defaults to the root
35444      * @return {Array}
35445      */
35446     getChecked : function(a, startNode){
35447         startNode = startNode || this.root;
35448         var r = [];
35449         var f = function(){
35450             if(this.attributes.checked){
35451                 r.push(!a ? this : (a == 'id' ? this.id : this.attributes[a]));
35452             }
35453         }
35454         startNode.cascade(f);
35455         return r;
35456     },
35457
35458     /**
35459      * Expands a specified path in this TreePanel. A path can be retrieved from a node with {@link Roo.data.Node#getPath}
35460      * @param {String} path
35461      * @param {String} attr (optional) The attribute used in the path (see {@link Roo.data.Node#getPath} for more info)
35462      * @param {Function} callback (optional) The callback to call when the expand is complete. The callback will be called with
35463      * (bSuccess, oLastNode) where bSuccess is if the expand was successful and oLastNode is the last node that was expanded.
35464      */
35465     expandPath : function(path, attr, callback){
35466         attr = attr || "id";
35467         var keys = path.split(this.pathSeparator);
35468         var curNode = this.root;
35469         if(curNode.attributes[attr] != keys[1]){ // invalid root
35470             if(callback){
35471                 callback(false, null);
35472             }
35473             return;
35474         }
35475         var index = 1;
35476         var f = function(){
35477             if(++index == keys.length){
35478                 if(callback){
35479                     callback(true, curNode);
35480                 }
35481                 return;
35482             }
35483             var c = curNode.findChild(attr, keys[index]);
35484             if(!c){
35485                 if(callback){
35486                     callback(false, curNode);
35487                 }
35488                 return;
35489             }
35490             curNode = c;
35491             c.expand(false, false, f);
35492         };
35493         curNode.expand(false, false, f);
35494     },
35495
35496     /**
35497      * Selects the node in this tree at the specified path. A path can be retrieved from a node with {@link Roo.data.Node#getPath}
35498      * @param {String} path
35499      * @param {String} attr (optional) The attribute used in the path (see {@link Roo.data.Node#getPath} for more info)
35500      * @param {Function} callback (optional) The callback to call when the selection is complete. The callback will be called with
35501      * (bSuccess, oSelNode) where bSuccess is if the selection was successful and oSelNode is the selected node.
35502      */
35503     selectPath : function(path, attr, callback){
35504         attr = attr || "id";
35505         var keys = path.split(this.pathSeparator);
35506         var v = keys.pop();
35507         if(keys.length > 0){
35508             var f = function(success, node){
35509                 if(success && node){
35510                     var n = node.findChild(attr, v);
35511                     if(n){
35512                         n.select();
35513                         if(callback){
35514                             callback(true, n);
35515                         }
35516                     }else if(callback){
35517                         callback(false, n);
35518                     }
35519                 }else{
35520                     if(callback){
35521                         callback(false, n);
35522                     }
35523                 }
35524             };
35525             this.expandPath(keys.join(this.pathSeparator), attr, f);
35526         }else{
35527             this.root.select();
35528             if(callback){
35529                 callback(true, this.root);
35530             }
35531         }
35532     },
35533
35534     getTreeEl : function(){
35535         return this.el;
35536     },
35537
35538     /**
35539      * Trigger rendering of this TreePanel
35540      */
35541     render : function(){
35542         if (this.innerCt) {
35543             return this; // stop it rendering more than once!!
35544         }
35545         
35546         this.innerCt = this.el.createChild({tag:"ul",
35547                cls:"x-tree-root-ct " +
35548                (this.lines ? "x-tree-lines" : "x-tree-no-lines")});
35549
35550         if(this.containerScroll){
35551             Roo.dd.ScrollManager.register(this.el);
35552         }
35553         if((this.enableDD || this.enableDrop) && !this.dropZone){
35554            /**
35555             * The dropZone used by this tree if drop is enabled
35556             * @type Roo.tree.TreeDropZone
35557             */
35558              this.dropZone = new Roo.tree.TreeDropZone(this, this.dropConfig || {
35559                ddGroup: this.ddGroup || "TreeDD", appendOnly: this.ddAppendOnly === true
35560            });
35561         }
35562         if((this.enableDD || this.enableDrag) && !this.dragZone){
35563            /**
35564             * The dragZone used by this tree if drag is enabled
35565             * @type Roo.tree.TreeDragZone
35566             */
35567             this.dragZone = new Roo.tree.TreeDragZone(this, this.dragConfig || {
35568                ddGroup: this.ddGroup || "TreeDD",
35569                scroll: this.ddScroll
35570            });
35571         }
35572         this.getSelectionModel().init(this);
35573         if (!this.root) {
35574             Roo.log("ROOT not set in tree");
35575             return this;
35576         }
35577         this.root.render();
35578         if(!this.rootVisible){
35579             this.root.renderChildren();
35580         }
35581         return this;
35582     }
35583 });/*
35584  * Based on:
35585  * Ext JS Library 1.1.1
35586  * Copyright(c) 2006-2007, Ext JS, LLC.
35587  *
35588  * Originally Released Under LGPL - original licence link has changed is not relivant.
35589  *
35590  * Fork - LGPL
35591  * <script type="text/javascript">
35592  */
35593  
35594
35595 /**
35596  * @class Roo.tree.DefaultSelectionModel
35597  * @extends Roo.util.Observable
35598  * The default single selection for a TreePanel.
35599  * @param {Object} cfg Configuration
35600  */
35601 Roo.tree.DefaultSelectionModel = function(cfg){
35602    this.selNode = null;
35603    
35604    
35605    
35606    this.addEvents({
35607        /**
35608         * @event selectionchange
35609         * Fires when the selected node changes
35610         * @param {DefaultSelectionModel} this
35611         * @param {TreeNode} node the new selection
35612         */
35613        "selectionchange" : true,
35614
35615        /**
35616         * @event beforeselect
35617         * Fires before the selected node changes, return false to cancel the change
35618         * @param {DefaultSelectionModel} this
35619         * @param {TreeNode} node the new selection
35620         * @param {TreeNode} node the old selection
35621         */
35622        "beforeselect" : true
35623    });
35624    
35625     Roo.tree.DefaultSelectionModel.superclass.constructor.call(this,cfg);
35626 };
35627
35628 Roo.extend(Roo.tree.DefaultSelectionModel, Roo.util.Observable, {
35629     init : function(tree){
35630         this.tree = tree;
35631         tree.getTreeEl().on("keydown", this.onKeyDown, this);
35632         tree.on("click", this.onNodeClick, this);
35633     },
35634     
35635     onNodeClick : function(node, e){
35636         if (e.ctrlKey && this.selNode == node)  {
35637             this.unselect(node);
35638             return;
35639         }
35640         this.select(node);
35641     },
35642     
35643     /**
35644      * Select a node.
35645      * @param {TreeNode} node The node to select
35646      * @return {TreeNode} The selected node
35647      */
35648     select : function(node){
35649         var last = this.selNode;
35650         if(last != node && this.fireEvent('beforeselect', this, node, last) !== false){
35651             if(last){
35652                 last.ui.onSelectedChange(false);
35653             }
35654             this.selNode = node;
35655             node.ui.onSelectedChange(true);
35656             this.fireEvent("selectionchange", this, node, last);
35657         }
35658         return node;
35659     },
35660     
35661     /**
35662      * Deselect a node.
35663      * @param {TreeNode} node The node to unselect
35664      */
35665     unselect : function(node){
35666         if(this.selNode == node){
35667             this.clearSelections();
35668         }    
35669     },
35670     
35671     /**
35672      * Clear all selections
35673      */
35674     clearSelections : function(){
35675         var n = this.selNode;
35676         if(n){
35677             n.ui.onSelectedChange(false);
35678             this.selNode = null;
35679             this.fireEvent("selectionchange", this, null);
35680         }
35681         return n;
35682     },
35683     
35684     /**
35685      * Get the selected node
35686      * @return {TreeNode} The selected node
35687      */
35688     getSelectedNode : function(){
35689         return this.selNode;    
35690     },
35691     
35692     /**
35693      * Returns true if the node is selected
35694      * @param {TreeNode} node The node to check
35695      * @return {Boolean}
35696      */
35697     isSelected : function(node){
35698         return this.selNode == node;  
35699     },
35700
35701     /**
35702      * Selects the node above the selected node in the tree, intelligently walking the nodes
35703      * @return TreeNode The new selection
35704      */
35705     selectPrevious : function(){
35706         var s = this.selNode || this.lastSelNode;
35707         if(!s){
35708             return null;
35709         }
35710         var ps = s.previousSibling;
35711         if(ps){
35712             if(!ps.isExpanded() || ps.childNodes.length < 1){
35713                 return this.select(ps);
35714             } else{
35715                 var lc = ps.lastChild;
35716                 while(lc && lc.isExpanded() && lc.childNodes.length > 0){
35717                     lc = lc.lastChild;
35718                 }
35719                 return this.select(lc);
35720             }
35721         } else if(s.parentNode && (this.tree.rootVisible || !s.parentNode.isRoot)){
35722             return this.select(s.parentNode);
35723         }
35724         return null;
35725     },
35726
35727     /**
35728      * Selects the node above the selected node in the tree, intelligently walking the nodes
35729      * @return TreeNode The new selection
35730      */
35731     selectNext : function(){
35732         var s = this.selNode || this.lastSelNode;
35733         if(!s){
35734             return null;
35735         }
35736         if(s.firstChild && s.isExpanded()){
35737              return this.select(s.firstChild);
35738          }else if(s.nextSibling){
35739              return this.select(s.nextSibling);
35740          }else if(s.parentNode){
35741             var newS = null;
35742             s.parentNode.bubble(function(){
35743                 if(this.nextSibling){
35744                     newS = this.getOwnerTree().selModel.select(this.nextSibling);
35745                     return false;
35746                 }
35747             });
35748             return newS;
35749          }
35750         return null;
35751     },
35752
35753     onKeyDown : function(e){
35754         var s = this.selNode || this.lastSelNode;
35755         // undesirable, but required
35756         var sm = this;
35757         if(!s){
35758             return;
35759         }
35760         var k = e.getKey();
35761         switch(k){
35762              case e.DOWN:
35763                  e.stopEvent();
35764                  this.selectNext();
35765              break;
35766              case e.UP:
35767                  e.stopEvent();
35768                  this.selectPrevious();
35769              break;
35770              case e.RIGHT:
35771                  e.preventDefault();
35772                  if(s.hasChildNodes()){
35773                      if(!s.isExpanded()){
35774                          s.expand();
35775                      }else if(s.firstChild){
35776                          this.select(s.firstChild, e);
35777                      }
35778                  }
35779              break;
35780              case e.LEFT:
35781                  e.preventDefault();
35782                  if(s.hasChildNodes() && s.isExpanded()){
35783                      s.collapse();
35784                  }else if(s.parentNode && (this.tree.rootVisible || s.parentNode != this.tree.getRootNode())){
35785                      this.select(s.parentNode, e);
35786                  }
35787              break;
35788         };
35789     }
35790 });
35791
35792 /**
35793  * @class Roo.tree.MultiSelectionModel
35794  * @extends Roo.util.Observable
35795  * Multi selection for a TreePanel.
35796  * @param {Object} cfg Configuration
35797  */
35798 Roo.tree.MultiSelectionModel = function(){
35799    this.selNodes = [];
35800    this.selMap = {};
35801    this.addEvents({
35802        /**
35803         * @event selectionchange
35804         * Fires when the selected nodes change
35805         * @param {MultiSelectionModel} this
35806         * @param {Array} nodes Array of the selected nodes
35807         */
35808        "selectionchange" : true
35809    });
35810    Roo.tree.MultiSelectionModel.superclass.constructor.call(this,cfg);
35811    
35812 };
35813
35814 Roo.extend(Roo.tree.MultiSelectionModel, Roo.util.Observable, {
35815     init : function(tree){
35816         this.tree = tree;
35817         tree.getTreeEl().on("keydown", this.onKeyDown, this);
35818         tree.on("click", this.onNodeClick, this);
35819     },
35820     
35821     onNodeClick : function(node, e){
35822         this.select(node, e, e.ctrlKey);
35823     },
35824     
35825     /**
35826      * Select a node.
35827      * @param {TreeNode} node The node to select
35828      * @param {EventObject} e (optional) An event associated with the selection
35829      * @param {Boolean} keepExisting True to retain existing selections
35830      * @return {TreeNode} The selected node
35831      */
35832     select : function(node, e, keepExisting){
35833         if(keepExisting !== true){
35834             this.clearSelections(true);
35835         }
35836         if(this.isSelected(node)){
35837             this.lastSelNode = node;
35838             return node;
35839         }
35840         this.selNodes.push(node);
35841         this.selMap[node.id] = node;
35842         this.lastSelNode = node;
35843         node.ui.onSelectedChange(true);
35844         this.fireEvent("selectionchange", this, this.selNodes);
35845         return node;
35846     },
35847     
35848     /**
35849      * Deselect a node.
35850      * @param {TreeNode} node The node to unselect
35851      */
35852     unselect : function(node){
35853         if(this.selMap[node.id]){
35854             node.ui.onSelectedChange(false);
35855             var sn = this.selNodes;
35856             var index = -1;
35857             if(sn.indexOf){
35858                 index = sn.indexOf(node);
35859             }else{
35860                 for(var i = 0, len = sn.length; i < len; i++){
35861                     if(sn[i] == node){
35862                         index = i;
35863                         break;
35864                     }
35865                 }
35866             }
35867             if(index != -1){
35868                 this.selNodes.splice(index, 1);
35869             }
35870             delete this.selMap[node.id];
35871             this.fireEvent("selectionchange", this, this.selNodes);
35872         }
35873     },
35874     
35875     /**
35876      * Clear all selections
35877      */
35878     clearSelections : function(suppressEvent){
35879         var sn = this.selNodes;
35880         if(sn.length > 0){
35881             for(var i = 0, len = sn.length; i < len; i++){
35882                 sn[i].ui.onSelectedChange(false);
35883             }
35884             this.selNodes = [];
35885             this.selMap = {};
35886             if(suppressEvent !== true){
35887                 this.fireEvent("selectionchange", this, this.selNodes);
35888             }
35889         }
35890     },
35891     
35892     /**
35893      * Returns true if the node is selected
35894      * @param {TreeNode} node The node to check
35895      * @return {Boolean}
35896      */
35897     isSelected : function(node){
35898         return this.selMap[node.id] ? true : false;  
35899     },
35900     
35901     /**
35902      * Returns an array of the selected nodes
35903      * @return {Array}
35904      */
35905     getSelectedNodes : function(){
35906         return this.selNodes;    
35907     },
35908
35909     onKeyDown : Roo.tree.DefaultSelectionModel.prototype.onKeyDown,
35910
35911     selectNext : Roo.tree.DefaultSelectionModel.prototype.selectNext,
35912
35913     selectPrevious : Roo.tree.DefaultSelectionModel.prototype.selectPrevious
35914 });/*
35915  * Based on:
35916  * Ext JS Library 1.1.1
35917  * Copyright(c) 2006-2007, Ext JS, LLC.
35918  *
35919  * Originally Released Under LGPL - original licence link has changed is not relivant.
35920  *
35921  * Fork - LGPL
35922  * <script type="text/javascript">
35923  */
35924  
35925 /**
35926  * @class Roo.tree.TreeNode
35927  * @extends Roo.data.Node
35928  * @cfg {String} text The text for this node
35929  * @cfg {Boolean} expanded true to start the node expanded
35930  * @cfg {Boolean} allowDrag false to make this node undraggable if DD is on (defaults to true)
35931  * @cfg {Boolean} allowDrop false if this node cannot be drop on
35932  * @cfg {Boolean} disabled true to start the node disabled
35933  * @cfg {String} icon The path to an icon for the node. The preferred way to do this
35934  *    is to use the cls or iconCls attributes and add the icon via a CSS background image.
35935  * @cfg {String} cls A css class to be added to the node
35936  * @cfg {String} iconCls A css class to be added to the nodes icon element for applying css background images
35937  * @cfg {String} href URL of the link used for the node (defaults to #)
35938  * @cfg {String} hrefTarget target frame for the link
35939  * @cfg {String} qtip An Ext QuickTip for the node
35940  * @cfg {String} qtipCfg An Ext QuickTip config for the node (used instead of qtip)
35941  * @cfg {Boolean} singleClickExpand True for single click expand on this node
35942  * @cfg {Function} uiProvider A UI <b>class</b> to use for this node (defaults to Roo.tree.TreeNodeUI)
35943  * @cfg {Boolean} checked True to render a checked checkbox for this node, false to render an unchecked checkbox
35944  * (defaults to undefined with no checkbox rendered)
35945  * @constructor
35946  * @param {Object/String} attributes The attributes/config for the node or just a string with the text for the node
35947  */
35948 Roo.tree.TreeNode = function(attributes){
35949     attributes = attributes || {};
35950     if(typeof attributes == "string"){
35951         attributes = {text: attributes};
35952     }
35953     this.childrenRendered = false;
35954     this.rendered = false;
35955     Roo.tree.TreeNode.superclass.constructor.call(this, attributes);
35956     this.expanded = attributes.expanded === true;
35957     this.isTarget = attributes.isTarget !== false;
35958     this.draggable = attributes.draggable !== false && attributes.allowDrag !== false;
35959     this.allowChildren = attributes.allowChildren !== false && attributes.allowDrop !== false;
35960
35961     /**
35962      * Read-only. The text for this node. To change it use setText().
35963      * @type String
35964      */
35965     this.text = attributes.text;
35966     /**
35967      * True if this node is disabled.
35968      * @type Boolean
35969      */
35970     this.disabled = attributes.disabled === true;
35971
35972     this.addEvents({
35973         /**
35974         * @event textchange
35975         * Fires when the text for this node is changed
35976         * @param {Node} this This node
35977         * @param {String} text The new text
35978         * @param {String} oldText The old text
35979         */
35980         "textchange" : true,
35981         /**
35982         * @event beforeexpand
35983         * Fires before this node is expanded, return false to cancel.
35984         * @param {Node} this This node
35985         * @param {Boolean} deep
35986         * @param {Boolean} anim
35987         */
35988         "beforeexpand" : true,
35989         /**
35990         * @event beforecollapse
35991         * Fires before this node is collapsed, return false to cancel.
35992         * @param {Node} this This node
35993         * @param {Boolean} deep
35994         * @param {Boolean} anim
35995         */
35996         "beforecollapse" : true,
35997         /**
35998         * @event expand
35999         * Fires when this node is expanded
36000         * @param {Node} this This node
36001         */
36002         "expand" : true,
36003         /**
36004         * @event disabledchange
36005         * Fires when the disabled status of this node changes
36006         * @param {Node} this This node
36007         * @param {Boolean} disabled
36008         */
36009         "disabledchange" : true,
36010         /**
36011         * @event collapse
36012         * Fires when this node is collapsed
36013         * @param {Node} this This node
36014         */
36015         "collapse" : true,
36016         /**
36017         * @event beforeclick
36018         * Fires before click processing. Return false to cancel the default action.
36019         * @param {Node} this This node
36020         * @param {Roo.EventObject} e The event object
36021         */
36022         "beforeclick":true,
36023         /**
36024         * @event checkchange
36025         * Fires when a node with a checkbox's checked property changes
36026         * @param {Node} this This node
36027         * @param {Boolean} checked
36028         */
36029         "checkchange":true,
36030         /**
36031         * @event click
36032         * Fires when this node is clicked
36033         * @param {Node} this This node
36034         * @param {Roo.EventObject} e The event object
36035         */
36036         "click":true,
36037         /**
36038         * @event dblclick
36039         * Fires when this node is double clicked
36040         * @param {Node} this This node
36041         * @param {Roo.EventObject} e The event object
36042         */
36043         "dblclick":true,
36044         /**
36045         * @event contextmenu
36046         * Fires when this node is right clicked
36047         * @param {Node} this This node
36048         * @param {Roo.EventObject} e The event object
36049         */
36050         "contextmenu":true,
36051         /**
36052         * @event beforechildrenrendered
36053         * Fires right before the child nodes for this node are rendered
36054         * @param {Node} this This node
36055         */
36056         "beforechildrenrendered":true
36057     });
36058
36059     var uiClass = this.attributes.uiProvider || Roo.tree.TreeNodeUI;
36060
36061     /**
36062      * Read-only. The UI for this node
36063      * @type TreeNodeUI
36064      */
36065     this.ui = new uiClass(this);
36066     
36067     // finally support items[]
36068     if (typeof(this.attributes.items) == 'undefined' || !this.attributes.items) {
36069         return;
36070     }
36071     
36072     
36073     Roo.each(this.attributes.items, function(c) {
36074         this.appendChild(Roo.factory(c,Roo.Tree));
36075     }, this);
36076     delete this.attributes.items;
36077     
36078     
36079     
36080 };
36081 Roo.extend(Roo.tree.TreeNode, Roo.data.Node, {
36082     preventHScroll: true,
36083     /**
36084      * Returns true if this node is expanded
36085      * @return {Boolean}
36086      */
36087     isExpanded : function(){
36088         return this.expanded;
36089     },
36090
36091     /**
36092      * Returns the UI object for this node
36093      * @return {TreeNodeUI}
36094      */
36095     getUI : function(){
36096         return this.ui;
36097     },
36098
36099     // private override
36100     setFirstChild : function(node){
36101         var of = this.firstChild;
36102         Roo.tree.TreeNode.superclass.setFirstChild.call(this, node);
36103         if(this.childrenRendered && of && node != of){
36104             of.renderIndent(true, true);
36105         }
36106         if(this.rendered){
36107             this.renderIndent(true, true);
36108         }
36109     },
36110
36111     // private override
36112     setLastChild : function(node){
36113         var ol = this.lastChild;
36114         Roo.tree.TreeNode.superclass.setLastChild.call(this, node);
36115         if(this.childrenRendered && ol && node != ol){
36116             ol.renderIndent(true, true);
36117         }
36118         if(this.rendered){
36119             this.renderIndent(true, true);
36120         }
36121     },
36122
36123     // these methods are overridden to provide lazy rendering support
36124     // private override
36125     appendChild : function()
36126     {
36127         var node = Roo.tree.TreeNode.superclass.appendChild.apply(this, arguments);
36128         if(node && this.childrenRendered){
36129             node.render();
36130         }
36131         this.ui.updateExpandIcon();
36132         return node;
36133     },
36134
36135     // private override
36136     removeChild : function(node){
36137         this.ownerTree.getSelectionModel().unselect(node);
36138         Roo.tree.TreeNode.superclass.removeChild.apply(this, arguments);
36139         // if it's been rendered remove dom node
36140         if(this.childrenRendered){
36141             node.ui.remove();
36142         }
36143         if(this.childNodes.length < 1){
36144             this.collapse(false, false);
36145         }else{
36146             this.ui.updateExpandIcon();
36147         }
36148         if(!this.firstChild) {
36149             this.childrenRendered = false;
36150         }
36151         return node;
36152     },
36153
36154     // private override
36155     insertBefore : function(node, refNode){
36156         var newNode = Roo.tree.TreeNode.superclass.insertBefore.apply(this, arguments);
36157         if(newNode && refNode && this.childrenRendered){
36158             node.render();
36159         }
36160         this.ui.updateExpandIcon();
36161         return newNode;
36162     },
36163
36164     /**
36165      * Sets the text for this node
36166      * @param {String} text
36167      */
36168     setText : function(text){
36169         var oldText = this.text;
36170         this.text = text;
36171         this.attributes.text = text;
36172         if(this.rendered){ // event without subscribing
36173             this.ui.onTextChange(this, text, oldText);
36174         }
36175         this.fireEvent("textchange", this, text, oldText);
36176     },
36177
36178     /**
36179      * Triggers selection of this node
36180      */
36181     select : function(){
36182         this.getOwnerTree().getSelectionModel().select(this);
36183     },
36184
36185     /**
36186      * Triggers deselection of this node
36187      */
36188     unselect : function(){
36189         this.getOwnerTree().getSelectionModel().unselect(this);
36190     },
36191
36192     /**
36193      * Returns true if this node is selected
36194      * @return {Boolean}
36195      */
36196     isSelected : function(){
36197         return this.getOwnerTree().getSelectionModel().isSelected(this);
36198     },
36199
36200     /**
36201      * Expand this node.
36202      * @param {Boolean} deep (optional) True to expand all children as well
36203      * @param {Boolean} anim (optional) false to cancel the default animation
36204      * @param {Function} callback (optional) A callback to be called when
36205      * expanding this node completes (does not wait for deep expand to complete).
36206      * Called with 1 parameter, this node.
36207      */
36208     expand : function(deep, anim, callback){
36209         if(!this.expanded){
36210             if(this.fireEvent("beforeexpand", this, deep, anim) === false){
36211                 return;
36212             }
36213             if(!this.childrenRendered){
36214                 this.renderChildren();
36215             }
36216             this.expanded = true;
36217             
36218             if(!this.isHiddenRoot() && (this.getOwnerTree() && this.getOwnerTree().animate && anim !== false) || anim){
36219                 this.ui.animExpand(function(){
36220                     this.fireEvent("expand", this);
36221                     if(typeof callback == "function"){
36222                         callback(this);
36223                     }
36224                     if(deep === true){
36225                         this.expandChildNodes(true);
36226                     }
36227                 }.createDelegate(this));
36228                 return;
36229             }else{
36230                 this.ui.expand();
36231                 this.fireEvent("expand", this);
36232                 if(typeof callback == "function"){
36233                     callback(this);
36234                 }
36235             }
36236         }else{
36237            if(typeof callback == "function"){
36238                callback(this);
36239            }
36240         }
36241         if(deep === true){
36242             this.expandChildNodes(true);
36243         }
36244     },
36245
36246     isHiddenRoot : function(){
36247         return this.isRoot && !this.getOwnerTree().rootVisible;
36248     },
36249
36250     /**
36251      * Collapse this node.
36252      * @param {Boolean} deep (optional) True to collapse all children as well
36253      * @param {Boolean} anim (optional) false to cancel the default animation
36254      */
36255     collapse : function(deep, anim){
36256         if(this.expanded && !this.isHiddenRoot()){
36257             if(this.fireEvent("beforecollapse", this, deep, anim) === false){
36258                 return;
36259             }
36260             this.expanded = false;
36261             if((this.getOwnerTree().animate && anim !== false) || anim){
36262                 this.ui.animCollapse(function(){
36263                     this.fireEvent("collapse", this);
36264                     if(deep === true){
36265                         this.collapseChildNodes(true);
36266                     }
36267                 }.createDelegate(this));
36268                 return;
36269             }else{
36270                 this.ui.collapse();
36271                 this.fireEvent("collapse", this);
36272             }
36273         }
36274         if(deep === true){
36275             var cs = this.childNodes;
36276             for(var i = 0, len = cs.length; i < len; i++) {
36277                 cs[i].collapse(true, false);
36278             }
36279         }
36280     },
36281
36282     // private
36283     delayedExpand : function(delay){
36284         if(!this.expandProcId){
36285             this.expandProcId = this.expand.defer(delay, this);
36286         }
36287     },
36288
36289     // private
36290     cancelExpand : function(){
36291         if(this.expandProcId){
36292             clearTimeout(this.expandProcId);
36293         }
36294         this.expandProcId = false;
36295     },
36296
36297     /**
36298      * Toggles expanded/collapsed state of the node
36299      */
36300     toggle : function(){
36301         if(this.expanded){
36302             this.collapse();
36303         }else{
36304             this.expand();
36305         }
36306     },
36307
36308     /**
36309      * Ensures all parent nodes are expanded
36310      */
36311     ensureVisible : function(callback){
36312         var tree = this.getOwnerTree();
36313         tree.expandPath(this.parentNode.getPath(), false, function(){
36314             tree.getTreeEl().scrollChildIntoView(this.ui.anchor);
36315             Roo.callback(callback);
36316         }.createDelegate(this));
36317     },
36318
36319     /**
36320      * Expand all child nodes
36321      * @param {Boolean} deep (optional) true if the child nodes should also expand their child nodes
36322      */
36323     expandChildNodes : function(deep){
36324         var cs = this.childNodes;
36325         for(var i = 0, len = cs.length; i < len; i++) {
36326                 cs[i].expand(deep);
36327         }
36328     },
36329
36330     /**
36331      * Collapse all child nodes
36332      * @param {Boolean} deep (optional) true if the child nodes should also collapse their child nodes
36333      */
36334     collapseChildNodes : function(deep){
36335         var cs = this.childNodes;
36336         for(var i = 0, len = cs.length; i < len; i++) {
36337                 cs[i].collapse(deep);
36338         }
36339     },
36340
36341     /**
36342      * Disables this node
36343      */
36344     disable : function(){
36345         this.disabled = true;
36346         this.unselect();
36347         if(this.rendered && this.ui.onDisableChange){ // event without subscribing
36348             this.ui.onDisableChange(this, true);
36349         }
36350         this.fireEvent("disabledchange", this, true);
36351     },
36352
36353     /**
36354      * Enables this node
36355      */
36356     enable : function(){
36357         this.disabled = false;
36358         if(this.rendered && this.ui.onDisableChange){ // event without subscribing
36359             this.ui.onDisableChange(this, false);
36360         }
36361         this.fireEvent("disabledchange", this, false);
36362     },
36363
36364     // private
36365     renderChildren : function(suppressEvent){
36366         if(suppressEvent !== false){
36367             this.fireEvent("beforechildrenrendered", this);
36368         }
36369         var cs = this.childNodes;
36370         for(var i = 0, len = cs.length; i < len; i++){
36371             cs[i].render(true);
36372         }
36373         this.childrenRendered = true;
36374     },
36375
36376     // private
36377     sort : function(fn, scope){
36378         Roo.tree.TreeNode.superclass.sort.apply(this, arguments);
36379         if(this.childrenRendered){
36380             var cs = this.childNodes;
36381             for(var i = 0, len = cs.length; i < len; i++){
36382                 cs[i].render(true);
36383             }
36384         }
36385     },
36386
36387     // private
36388     render : function(bulkRender){
36389         this.ui.render(bulkRender);
36390         if(!this.rendered){
36391             this.rendered = true;
36392             if(this.expanded){
36393                 this.expanded = false;
36394                 this.expand(false, false);
36395             }
36396         }
36397     },
36398
36399     // private
36400     renderIndent : function(deep, refresh){
36401         if(refresh){
36402             this.ui.childIndent = null;
36403         }
36404         this.ui.renderIndent();
36405         if(deep === true && this.childrenRendered){
36406             var cs = this.childNodes;
36407             for(var i = 0, len = cs.length; i < len; i++){
36408                 cs[i].renderIndent(true, refresh);
36409             }
36410         }
36411     }
36412 });/*
36413  * Based on:
36414  * Ext JS Library 1.1.1
36415  * Copyright(c) 2006-2007, Ext JS, LLC.
36416  *
36417  * Originally Released Under LGPL - original licence link has changed is not relivant.
36418  *
36419  * Fork - LGPL
36420  * <script type="text/javascript">
36421  */
36422  
36423 /**
36424  * @class Roo.tree.AsyncTreeNode
36425  * @extends Roo.tree.TreeNode
36426  * @cfg {TreeLoader} loader A TreeLoader to be used by this node (defaults to the loader defined on the tree)
36427  * @constructor
36428  * @param {Object/String} attributes The attributes/config for the node or just a string with the text for the node 
36429  */
36430  Roo.tree.AsyncTreeNode = function(config){
36431     this.loaded = false;
36432     this.loading = false;
36433     Roo.tree.AsyncTreeNode.superclass.constructor.apply(this, arguments);
36434     /**
36435     * @event beforeload
36436     * Fires before this node is loaded, return false to cancel
36437     * @param {Node} this This node
36438     */
36439     this.addEvents({'beforeload':true, 'load': true});
36440     /**
36441     * @event load
36442     * Fires when this node is loaded
36443     * @param {Node} this This node
36444     */
36445     /**
36446      * The loader used by this node (defaults to using the tree's defined loader)
36447      * @type TreeLoader
36448      * @property loader
36449      */
36450 };
36451 Roo.extend(Roo.tree.AsyncTreeNode, Roo.tree.TreeNode, {
36452     expand : function(deep, anim, callback){
36453         if(this.loading){ // if an async load is already running, waiting til it's done
36454             var timer;
36455             var f = function(){
36456                 if(!this.loading){ // done loading
36457                     clearInterval(timer);
36458                     this.expand(deep, anim, callback);
36459                 }
36460             }.createDelegate(this);
36461             timer = setInterval(f, 200);
36462             return;
36463         }
36464         if(!this.loaded){
36465             if(this.fireEvent("beforeload", this) === false){
36466                 return;
36467             }
36468             this.loading = true;
36469             this.ui.beforeLoad(this);
36470             var loader = this.loader || this.attributes.loader || this.getOwnerTree().getLoader();
36471             if(loader){
36472                 loader.load(this, this.loadComplete.createDelegate(this, [deep, anim, callback]));
36473                 return;
36474             }
36475         }
36476         Roo.tree.AsyncTreeNode.superclass.expand.call(this, deep, anim, callback);
36477     },
36478     
36479     /**
36480      * Returns true if this node is currently loading
36481      * @return {Boolean}
36482      */
36483     isLoading : function(){
36484         return this.loading;  
36485     },
36486     
36487     loadComplete : function(deep, anim, callback){
36488         this.loading = false;
36489         this.loaded = true;
36490         this.ui.afterLoad(this);
36491         this.fireEvent("load", this);
36492         this.expand(deep, anim, callback);
36493     },
36494     
36495     /**
36496      * Returns true if this node has been loaded
36497      * @return {Boolean}
36498      */
36499     isLoaded : function(){
36500         return this.loaded;
36501     },
36502     
36503     hasChildNodes : function(){
36504         if(!this.isLeaf() && !this.loaded){
36505             return true;
36506         }else{
36507             return Roo.tree.AsyncTreeNode.superclass.hasChildNodes.call(this);
36508         }
36509     },
36510
36511     /**
36512      * Trigger a reload for this node
36513      * @param {Function} callback
36514      */
36515     reload : function(callback){
36516         this.collapse(false, false);
36517         while(this.firstChild){
36518             this.removeChild(this.firstChild);
36519         }
36520         this.childrenRendered = false;
36521         this.loaded = false;
36522         if(this.isHiddenRoot()){
36523             this.expanded = false;
36524         }
36525         this.expand(false, false, callback);
36526     }
36527 });/*
36528  * Based on:
36529  * Ext JS Library 1.1.1
36530  * Copyright(c) 2006-2007, Ext JS, LLC.
36531  *
36532  * Originally Released Under LGPL - original licence link has changed is not relivant.
36533  *
36534  * Fork - LGPL
36535  * <script type="text/javascript">
36536  */
36537  
36538 /**
36539  * @class Roo.tree.TreeNodeUI
36540  * @constructor
36541  * @param {Object} node The node to render
36542  * The TreeNode UI implementation is separate from the
36543  * tree implementation. Unless you are customizing the tree UI,
36544  * you should never have to use this directly.
36545  */
36546 Roo.tree.TreeNodeUI = function(node){
36547     this.node = node;
36548     this.rendered = false;
36549     this.animating = false;
36550     this.emptyIcon = Roo.BLANK_IMAGE_URL;
36551 };
36552
36553 Roo.tree.TreeNodeUI.prototype = {
36554     removeChild : function(node){
36555         if(this.rendered){
36556             this.ctNode.removeChild(node.ui.getEl());
36557         }
36558     },
36559
36560     beforeLoad : function(){
36561          this.addClass("x-tree-node-loading");
36562     },
36563
36564     afterLoad : function(){
36565          this.removeClass("x-tree-node-loading");
36566     },
36567
36568     onTextChange : function(node, text, oldText){
36569         if(this.rendered){
36570             this.textNode.innerHTML = text;
36571         }
36572     },
36573
36574     onDisableChange : function(node, state){
36575         this.disabled = state;
36576         if(state){
36577             this.addClass("x-tree-node-disabled");
36578         }else{
36579             this.removeClass("x-tree-node-disabled");
36580         }
36581     },
36582
36583     onSelectedChange : function(state){
36584         if(state){
36585             this.focus();
36586             this.addClass("x-tree-selected");
36587         }else{
36588             //this.blur();
36589             this.removeClass("x-tree-selected");
36590         }
36591     },
36592
36593     onMove : function(tree, node, oldParent, newParent, index, refNode){
36594         this.childIndent = null;
36595         if(this.rendered){
36596             var targetNode = newParent.ui.getContainer();
36597             if(!targetNode){//target not rendered
36598                 this.holder = document.createElement("div");
36599                 this.holder.appendChild(this.wrap);
36600                 return;
36601             }
36602             var insertBefore = refNode ? refNode.ui.getEl() : null;
36603             if(insertBefore){
36604                 targetNode.insertBefore(this.wrap, insertBefore);
36605             }else{
36606                 targetNode.appendChild(this.wrap);
36607             }
36608             this.node.renderIndent(true);
36609         }
36610     },
36611
36612     addClass : function(cls){
36613         if(this.elNode){
36614             Roo.fly(this.elNode).addClass(cls);
36615         }
36616     },
36617
36618     removeClass : function(cls){
36619         if(this.elNode){
36620             Roo.fly(this.elNode).removeClass(cls);
36621         }
36622     },
36623
36624     remove : function(){
36625         if(this.rendered){
36626             this.holder = document.createElement("div");
36627             this.holder.appendChild(this.wrap);
36628         }
36629     },
36630
36631     fireEvent : function(){
36632         return this.node.fireEvent.apply(this.node, arguments);
36633     },
36634
36635     initEvents : function(){
36636         this.node.on("move", this.onMove, this);
36637         var E = Roo.EventManager;
36638         var a = this.anchor;
36639
36640         var el = Roo.fly(a, '_treeui');
36641
36642         if(Roo.isOpera){ // opera render bug ignores the CSS
36643             el.setStyle("text-decoration", "none");
36644         }
36645
36646         el.on("click", this.onClick, this);
36647         el.on("dblclick", this.onDblClick, this);
36648
36649         if(this.checkbox){
36650             Roo.EventManager.on(this.checkbox,
36651                     Roo.isIE ? 'click' : 'change', this.onCheckChange, this);
36652         }
36653
36654         el.on("contextmenu", this.onContextMenu, this);
36655
36656         var icon = Roo.fly(this.iconNode);
36657         icon.on("click", this.onClick, this);
36658         icon.on("dblclick", this.onDblClick, this);
36659         icon.on("contextmenu", this.onContextMenu, this);
36660         E.on(this.ecNode, "click", this.ecClick, this, true);
36661
36662         if(this.node.disabled){
36663             this.addClass("x-tree-node-disabled");
36664         }
36665         if(this.node.hidden){
36666             this.addClass("x-tree-node-disabled");
36667         }
36668         var ot = this.node.getOwnerTree();
36669         var dd = ot ? (ot.enableDD || ot.enableDrag || ot.enableDrop) : false;
36670         if(dd && (!this.node.isRoot || ot.rootVisible)){
36671             Roo.dd.Registry.register(this.elNode, {
36672                 node: this.node,
36673                 handles: this.getDDHandles(),
36674                 isHandle: false
36675             });
36676         }
36677     },
36678
36679     getDDHandles : function(){
36680         return [this.iconNode, this.textNode];
36681     },
36682
36683     hide : function(){
36684         if(this.rendered){
36685             this.wrap.style.display = "none";
36686         }
36687     },
36688
36689     show : function(){
36690         if(this.rendered){
36691             this.wrap.style.display = "";
36692         }
36693     },
36694
36695     onContextMenu : function(e){
36696         if (this.node.hasListener("contextmenu") || this.node.getOwnerTree().hasListener("contextmenu")) {
36697             e.preventDefault();
36698             this.focus();
36699             this.fireEvent("contextmenu", this.node, e);
36700         }
36701     },
36702
36703     onClick : function(e){
36704         if(this.dropping){
36705             e.stopEvent();
36706             return;
36707         }
36708         if(this.fireEvent("beforeclick", this.node, e) !== false){
36709             if(!this.disabled && this.node.attributes.href){
36710                 this.fireEvent("click", this.node, e);
36711                 return;
36712             }
36713             e.preventDefault();
36714             if(this.disabled){
36715                 return;
36716             }
36717
36718             if(this.node.attributes.singleClickExpand && !this.animating && this.node.hasChildNodes()){
36719                 this.node.toggle();
36720             }
36721
36722             this.fireEvent("click", this.node, e);
36723         }else{
36724             e.stopEvent();
36725         }
36726     },
36727
36728     onDblClick : function(e){
36729         e.preventDefault();
36730         if(this.disabled){
36731             return;
36732         }
36733         if(this.checkbox){
36734             this.toggleCheck();
36735         }
36736         if(!this.animating && this.node.hasChildNodes()){
36737             this.node.toggle();
36738         }
36739         this.fireEvent("dblclick", this.node, e);
36740     },
36741
36742     onCheckChange : function(){
36743         var checked = this.checkbox.checked;
36744         this.node.attributes.checked = checked;
36745         this.fireEvent('checkchange', this.node, checked);
36746     },
36747
36748     ecClick : function(e){
36749         if(!this.animating && this.node.hasChildNodes()){
36750             this.node.toggle();
36751         }
36752     },
36753
36754     startDrop : function(){
36755         this.dropping = true;
36756     },
36757
36758     // delayed drop so the click event doesn't get fired on a drop
36759     endDrop : function(){
36760        setTimeout(function(){
36761            this.dropping = false;
36762        }.createDelegate(this), 50);
36763     },
36764
36765     expand : function(){
36766         this.updateExpandIcon();
36767         this.ctNode.style.display = "";
36768     },
36769
36770     focus : function(){
36771         if(!this.node.preventHScroll){
36772             try{this.anchor.focus();
36773             }catch(e){}
36774         }else if(!Roo.isIE){
36775             try{
36776                 var noscroll = this.node.getOwnerTree().getTreeEl().dom;
36777                 var l = noscroll.scrollLeft;
36778                 this.anchor.focus();
36779                 noscroll.scrollLeft = l;
36780             }catch(e){}
36781         }
36782     },
36783
36784     toggleCheck : function(value){
36785         var cb = this.checkbox;
36786         if(cb){
36787             cb.checked = (value === undefined ? !cb.checked : value);
36788         }
36789     },
36790
36791     blur : function(){
36792         try{
36793             this.anchor.blur();
36794         }catch(e){}
36795     },
36796
36797     animExpand : function(callback){
36798         var ct = Roo.get(this.ctNode);
36799         ct.stopFx();
36800         if(!this.node.hasChildNodes()){
36801             this.updateExpandIcon();
36802             this.ctNode.style.display = "";
36803             Roo.callback(callback);
36804             return;
36805         }
36806         this.animating = true;
36807         this.updateExpandIcon();
36808
36809         ct.slideIn('t', {
36810            callback : function(){
36811                this.animating = false;
36812                Roo.callback(callback);
36813             },
36814             scope: this,
36815             duration: this.node.ownerTree.duration || .25
36816         });
36817     },
36818
36819     highlight : function(){
36820         var tree = this.node.getOwnerTree();
36821         Roo.fly(this.wrap).highlight(
36822             tree.hlColor || "C3DAF9",
36823             {endColor: tree.hlBaseColor}
36824         );
36825     },
36826
36827     collapse : function(){
36828         this.updateExpandIcon();
36829         this.ctNode.style.display = "none";
36830     },
36831
36832     animCollapse : function(callback){
36833         var ct = Roo.get(this.ctNode);
36834         ct.enableDisplayMode('block');
36835         ct.stopFx();
36836
36837         this.animating = true;
36838         this.updateExpandIcon();
36839
36840         ct.slideOut('t', {
36841             callback : function(){
36842                this.animating = false;
36843                Roo.callback(callback);
36844             },
36845             scope: this,
36846             duration: this.node.ownerTree.duration || .25
36847         });
36848     },
36849
36850     getContainer : function(){
36851         return this.ctNode;
36852     },
36853
36854     getEl : function(){
36855         return this.wrap;
36856     },
36857
36858     appendDDGhost : function(ghostNode){
36859         ghostNode.appendChild(this.elNode.cloneNode(true));
36860     },
36861
36862     getDDRepairXY : function(){
36863         return Roo.lib.Dom.getXY(this.iconNode);
36864     },
36865
36866     onRender : function(){
36867         this.render();
36868     },
36869
36870     render : function(bulkRender){
36871         var n = this.node, a = n.attributes;
36872         var targetNode = n.parentNode ?
36873               n.parentNode.ui.getContainer() : n.ownerTree.innerCt.dom;
36874
36875         if(!this.rendered){
36876             this.rendered = true;
36877
36878             this.renderElements(n, a, targetNode, bulkRender);
36879
36880             if(a.qtip){
36881                if(this.textNode.setAttributeNS){
36882                    this.textNode.setAttributeNS("ext", "qtip", a.qtip);
36883                    if(a.qtipTitle){
36884                        this.textNode.setAttributeNS("ext", "qtitle", a.qtipTitle);
36885                    }
36886                }else{
36887                    this.textNode.setAttribute("ext:qtip", a.qtip);
36888                    if(a.qtipTitle){
36889                        this.textNode.setAttribute("ext:qtitle", a.qtipTitle);
36890                    }
36891                }
36892             }else if(a.qtipCfg){
36893                 a.qtipCfg.target = Roo.id(this.textNode);
36894                 Roo.QuickTips.register(a.qtipCfg);
36895             }
36896             this.initEvents();
36897             if(!this.node.expanded){
36898                 this.updateExpandIcon();
36899             }
36900         }else{
36901             if(bulkRender === true) {
36902                 targetNode.appendChild(this.wrap);
36903             }
36904         }
36905     },
36906
36907     renderElements : function(n, a, targetNode, bulkRender)
36908     {
36909         // add some indent caching, this helps performance when rendering a large tree
36910         this.indentMarkup = n.parentNode ? n.parentNode.ui.getChildIndent() : '';
36911         var t = n.getOwnerTree();
36912         var txt = t && t.renderer ? t.renderer(n.attributes) : Roo.util.Format.htmlEncode(n.text);
36913         if (typeof(n.attributes.html) != 'undefined') {
36914             txt = n.attributes.html;
36915         }
36916         var tip = t && t.rendererTip ? t.rendererTip(n.attributes) : txt;
36917         var cb = typeof a.checked == 'boolean';
36918         var href = a.href ? a.href : Roo.isGecko ? "" : "#";
36919         var buf = ['<li class="x-tree-node"><div class="x-tree-node-el ', a.cls,'">',
36920             '<span class="x-tree-node-indent">',this.indentMarkup,"</span>",
36921             '<img src="', this.emptyIcon, '" class="x-tree-ec-icon" />',
36922             '<img src="', a.icon || this.emptyIcon, '" class="x-tree-node-icon',(a.icon ? " x-tree-node-inline-icon" : ""),(a.iconCls ? " "+a.iconCls : ""),'" unselectable="on" />',
36923             cb ? ('<input class="x-tree-node-cb" type="checkbox" ' + (a.checked ? 'checked="checked" />' : ' />')) : '',
36924             '<a hidefocus="on" href="',href,'" tabIndex="1" ',
36925              a.hrefTarget ? ' target="'+a.hrefTarget+'"' : "", 
36926                 '><span unselectable="on" qtip="' , tip ,'">',txt,"</span></a></div>",
36927             '<ul class="x-tree-node-ct" style="display:none;"></ul>',
36928             "</li>"];
36929
36930         if(bulkRender !== true && n.nextSibling && n.nextSibling.ui.getEl()){
36931             this.wrap = Roo.DomHelper.insertHtml("beforeBegin",
36932                                 n.nextSibling.ui.getEl(), buf.join(""));
36933         }else{
36934             this.wrap = Roo.DomHelper.insertHtml("beforeEnd", targetNode, buf.join(""));
36935         }
36936
36937         this.elNode = this.wrap.childNodes[0];
36938         this.ctNode = this.wrap.childNodes[1];
36939         var cs = this.elNode.childNodes;
36940         this.indentNode = cs[0];
36941         this.ecNode = cs[1];
36942         this.iconNode = cs[2];
36943         var index = 3;
36944         if(cb){
36945             this.checkbox = cs[3];
36946             index++;
36947         }
36948         this.anchor = cs[index];
36949         this.textNode = cs[index].firstChild;
36950     },
36951
36952     getAnchor : function(){
36953         return this.anchor;
36954     },
36955
36956     getTextEl : function(){
36957         return this.textNode;
36958     },
36959
36960     getIconEl : function(){
36961         return this.iconNode;
36962     },
36963
36964     isChecked : function(){
36965         return this.checkbox ? this.checkbox.checked : false;
36966     },
36967
36968     updateExpandIcon : function(){
36969         if(this.rendered){
36970             var n = this.node, c1, c2;
36971             var cls = n.isLast() ? "x-tree-elbow-end" : "x-tree-elbow";
36972             var hasChild = n.hasChildNodes();
36973             if(hasChild){
36974                 if(n.expanded){
36975                     cls += "-minus";
36976                     c1 = "x-tree-node-collapsed";
36977                     c2 = "x-tree-node-expanded";
36978                 }else{
36979                     cls += "-plus";
36980                     c1 = "x-tree-node-expanded";
36981                     c2 = "x-tree-node-collapsed";
36982                 }
36983                 if(this.wasLeaf){
36984                     this.removeClass("x-tree-node-leaf");
36985                     this.wasLeaf = false;
36986                 }
36987                 if(this.c1 != c1 || this.c2 != c2){
36988                     Roo.fly(this.elNode).replaceClass(c1, c2);
36989                     this.c1 = c1; this.c2 = c2;
36990                 }
36991             }else{
36992                 // this changes non-leafs into leafs if they have no children.
36993                 // it's not very rational behaviour..
36994                 
36995                 if(!this.wasLeaf && this.node.leaf){
36996                     Roo.fly(this.elNode).replaceClass("x-tree-node-expanded", "x-tree-node-leaf");
36997                     delete this.c1;
36998                     delete this.c2;
36999                     this.wasLeaf = true;
37000                 }
37001             }
37002             var ecc = "x-tree-ec-icon "+cls;
37003             if(this.ecc != ecc){
37004                 this.ecNode.className = ecc;
37005                 this.ecc = ecc;
37006             }
37007         }
37008     },
37009
37010     getChildIndent : function(){
37011         if(!this.childIndent){
37012             var buf = [];
37013             var p = this.node;
37014             while(p){
37015                 if(!p.isRoot || (p.isRoot && p.ownerTree.rootVisible)){
37016                     if(!p.isLast()) {
37017                         buf.unshift('<img src="'+this.emptyIcon+'" class="x-tree-elbow-line" />');
37018                     } else {
37019                         buf.unshift('<img src="'+this.emptyIcon+'" class="x-tree-icon" />');
37020                     }
37021                 }
37022                 p = p.parentNode;
37023             }
37024             this.childIndent = buf.join("");
37025         }
37026         return this.childIndent;
37027     },
37028
37029     renderIndent : function(){
37030         if(this.rendered){
37031             var indent = "";
37032             var p = this.node.parentNode;
37033             if(p){
37034                 indent = p.ui.getChildIndent();
37035             }
37036             if(this.indentMarkup != indent){ // don't rerender if not required
37037                 this.indentNode.innerHTML = indent;
37038                 this.indentMarkup = indent;
37039             }
37040             this.updateExpandIcon();
37041         }
37042     }
37043 };
37044
37045 Roo.tree.RootTreeNodeUI = function(){
37046     Roo.tree.RootTreeNodeUI.superclass.constructor.apply(this, arguments);
37047 };
37048 Roo.extend(Roo.tree.RootTreeNodeUI, Roo.tree.TreeNodeUI, {
37049     render : function(){
37050         if(!this.rendered){
37051             var targetNode = this.node.ownerTree.innerCt.dom;
37052             this.node.expanded = true;
37053             targetNode.innerHTML = '<div class="x-tree-root-node"></div>';
37054             this.wrap = this.ctNode = targetNode.firstChild;
37055         }
37056     },
37057     collapse : function(){
37058     },
37059     expand : function(){
37060     }
37061 });/*
37062  * Based on:
37063  * Ext JS Library 1.1.1
37064  * Copyright(c) 2006-2007, Ext JS, LLC.
37065  *
37066  * Originally Released Under LGPL - original licence link has changed is not relivant.
37067  *
37068  * Fork - LGPL
37069  * <script type="text/javascript">
37070  */
37071 /**
37072  * @class Roo.tree.TreeLoader
37073  * @extends Roo.util.Observable
37074  * A TreeLoader provides for lazy loading of an {@link Roo.tree.TreeNode}'s child
37075  * nodes from a specified URL. The response must be a javascript Array definition
37076  * who's elements are node definition objects. eg:
37077  * <pre><code>
37078 {  success : true,
37079    data :      [
37080    
37081     { 'id': 1, 'text': 'A folder Node', 'leaf': false },
37082     { 'id': 2, 'text': 'A leaf Node', 'leaf': true }
37083     ]
37084 }
37085
37086
37087 </code></pre>
37088  * <br><br>
37089  * The old style respose with just an array is still supported, but not recommended.
37090  * <br><br>
37091  *
37092  * A server request is sent, and child nodes are loaded only when a node is expanded.
37093  * The loading node's id is passed to the server under the parameter name "node" to
37094  * enable the server to produce the correct child nodes.
37095  * <br><br>
37096  * To pass extra parameters, an event handler may be attached to the "beforeload"
37097  * event, and the parameters specified in the TreeLoader's baseParams property:
37098  * <pre><code>
37099     myTreeLoader.on("beforeload", function(treeLoader, node) {
37100         this.baseParams.category = node.attributes.category;
37101     }, this);
37102     
37103 </code></pre>
37104  *
37105  * This would pass an HTTP parameter called "category" to the server containing
37106  * the value of the Node's "category" attribute.
37107  * @constructor
37108  * Creates a new Treeloader.
37109  * @param {Object} config A config object containing config properties.
37110  */
37111 Roo.tree.TreeLoader = function(config){
37112     this.baseParams = {};
37113     this.requestMethod = "POST";
37114     Roo.apply(this, config);
37115
37116     this.addEvents({
37117     
37118         /**
37119          * @event beforeload
37120          * Fires before a network request is made to retrieve the Json text which specifies a node's children.
37121          * @param {Object} This TreeLoader object.
37122          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
37123          * @param {Object} callback The callback function specified in the {@link #load} call.
37124          */
37125         beforeload : true,
37126         /**
37127          * @event load
37128          * Fires when the node has been successfuly loaded.
37129          * @param {Object} This TreeLoader object.
37130          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
37131          * @param {Object} response The response object containing the data from the server.
37132          */
37133         load : true,
37134         /**
37135          * @event loadexception
37136          * Fires if the network request failed.
37137          * @param {Object} This TreeLoader object.
37138          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
37139          * @param {Object} response The response object containing the data from the server.
37140          */
37141         loadexception : true,
37142         /**
37143          * @event create
37144          * Fires before a node is created, enabling you to return custom Node types 
37145          * @param {Object} This TreeLoader object.
37146          * @param {Object} attr - the data returned from the AJAX call (modify it to suit)
37147          */
37148         create : true
37149     });
37150
37151     Roo.tree.TreeLoader.superclass.constructor.call(this);
37152 };
37153
37154 Roo.extend(Roo.tree.TreeLoader, Roo.util.Observable, {
37155     /**
37156     * @cfg {String} dataUrl The URL from which to request a Json string which
37157     * specifies an array of node definition object representing the child nodes
37158     * to be loaded.
37159     */
37160     /**
37161     * @cfg {String} requestMethod either GET or POST
37162     * defaults to POST (due to BC)
37163     * to be loaded.
37164     */
37165     /**
37166     * @cfg {Object} baseParams (optional) An object containing properties which
37167     * specify HTTP parameters to be passed to each request for child nodes.
37168     */
37169     /**
37170     * @cfg {Object} baseAttrs (optional) An object containing attributes to be added to all nodes
37171     * created by this loader. If the attributes sent by the server have an attribute in this object,
37172     * they take priority.
37173     */
37174     /**
37175     * @cfg {Object} uiProviders (optional) An object containing properties which
37176     * 
37177     * DEPRECATED - use 'create' event handler to modify attributes - which affect creation.
37178     * specify custom {@link Roo.tree.TreeNodeUI} implementations. If the optional
37179     * <i>uiProvider</i> attribute of a returned child node is a string rather
37180     * than a reference to a TreeNodeUI implementation, this that string value
37181     * is used as a property name in the uiProviders object. You can define the provider named
37182     * 'default' , and this will be used for all nodes (if no uiProvider is delivered by the node data)
37183     */
37184     uiProviders : {},
37185
37186     /**
37187     * @cfg {Boolean} clearOnLoad (optional) Default to true. Remove previously existing
37188     * child nodes before loading.
37189     */
37190     clearOnLoad : true,
37191
37192     /**
37193     * @cfg {String} root (optional) Default to false. Use this to read data from an object 
37194     * property on loading, rather than expecting an array. (eg. more compatible to a standard
37195     * Grid query { data : [ .....] }
37196     */
37197     
37198     root : false,
37199      /**
37200     * @cfg {String} queryParam (optional) 
37201     * Name of the query as it will be passed on the querystring (defaults to 'node')
37202     * eg. the request will be ?node=[id]
37203     */
37204     
37205     
37206     queryParam: false,
37207     
37208     /**
37209      * Load an {@link Roo.tree.TreeNode} from the URL specified in the constructor.
37210      * This is called automatically when a node is expanded, but may be used to reload
37211      * a node (or append new children if the {@link #clearOnLoad} option is false.)
37212      * @param {Roo.tree.TreeNode} node
37213      * @param {Function} callback
37214      */
37215     load : function(node, callback){
37216         if(this.clearOnLoad){
37217             while(node.firstChild){
37218                 node.removeChild(node.firstChild);
37219             }
37220         }
37221         if(node.attributes.children){ // preloaded json children
37222             var cs = node.attributes.children;
37223             for(var i = 0, len = cs.length; i < len; i++){
37224                 node.appendChild(this.createNode(cs[i]));
37225             }
37226             if(typeof callback == "function"){
37227                 callback();
37228             }
37229         }else if(this.dataUrl){
37230             this.requestData(node, callback);
37231         }
37232     },
37233
37234     getParams: function(node){
37235         var buf = [], bp = this.baseParams;
37236         for(var key in bp){
37237             if(typeof bp[key] != "function"){
37238                 buf.push(encodeURIComponent(key), "=", encodeURIComponent(bp[key]), "&");
37239             }
37240         }
37241         var n = this.queryParam === false ? 'node' : this.queryParam;
37242         buf.push(n + "=", encodeURIComponent(node.id));
37243         return buf.join("");
37244     },
37245
37246     requestData : function(node, callback){
37247         if(this.fireEvent("beforeload", this, node, callback) !== false){
37248             this.transId = Roo.Ajax.request({
37249                 method:this.requestMethod,
37250                 url: this.dataUrl||this.url,
37251                 success: this.handleResponse,
37252                 failure: this.handleFailure,
37253                 scope: this,
37254                 argument: {callback: callback, node: node},
37255                 params: this.getParams(node)
37256             });
37257         }else{
37258             // if the load is cancelled, make sure we notify
37259             // the node that we are done
37260             if(typeof callback == "function"){
37261                 callback();
37262             }
37263         }
37264     },
37265
37266     isLoading : function(){
37267         return this.transId ? true : false;
37268     },
37269
37270     abort : function(){
37271         if(this.isLoading()){
37272             Roo.Ajax.abort(this.transId);
37273         }
37274     },
37275
37276     // private
37277     createNode : function(attr)
37278     {
37279         // apply baseAttrs, nice idea Corey!
37280         if(this.baseAttrs){
37281             Roo.applyIf(attr, this.baseAttrs);
37282         }
37283         if(this.applyLoader !== false){
37284             attr.loader = this;
37285         }
37286         // uiProvider = depreciated..
37287         
37288         if(typeof(attr.uiProvider) == 'string'){
37289            attr.uiProvider = this.uiProviders[attr.uiProvider] || 
37290                 /**  eval:var:attr */ eval(attr.uiProvider);
37291         }
37292         if(typeof(this.uiProviders['default']) != 'undefined') {
37293             attr.uiProvider = this.uiProviders['default'];
37294         }
37295         
37296         this.fireEvent('create', this, attr);
37297         
37298         attr.leaf  = typeof(attr.leaf) == 'string' ? attr.leaf * 1 : attr.leaf;
37299         return(attr.leaf ?
37300                         new Roo.tree.TreeNode(attr) :
37301                         new Roo.tree.AsyncTreeNode(attr));
37302     },
37303
37304     processResponse : function(response, node, callback)
37305     {
37306         var json = response.responseText;
37307         try {
37308             
37309             var o = Roo.decode(json);
37310             
37311             if (this.root === false && typeof(o.success) != undefined) {
37312                 this.root = 'data'; // the default behaviour for list like data..
37313                 }
37314                 
37315             if (this.root !== false &&  !o.success) {
37316                 // it's a failure condition.
37317                 var a = response.argument;
37318                 this.fireEvent("loadexception", this, a.node, response);
37319                 Roo.log("Load failed - should have a handler really");
37320                 return;
37321             }
37322             
37323             
37324             
37325             if (this.root !== false) {
37326                  o = o[this.root];
37327             }
37328             
37329             for(var i = 0, len = o.length; i < len; i++){
37330                 var n = this.createNode(o[i]);
37331                 if(n){
37332                     node.appendChild(n);
37333                 }
37334             }
37335             if(typeof callback == "function"){
37336                 callback(this, node);
37337             }
37338         }catch(e){
37339             this.handleFailure(response);
37340         }
37341     },
37342
37343     handleResponse : function(response){
37344         this.transId = false;
37345         var a = response.argument;
37346         this.processResponse(response, a.node, a.callback);
37347         this.fireEvent("load", this, a.node, response);
37348     },
37349
37350     handleFailure : function(response)
37351     {
37352         // should handle failure better..
37353         this.transId = false;
37354         var a = response.argument;
37355         this.fireEvent("loadexception", this, a.node, response);
37356         if(typeof a.callback == "function"){
37357             a.callback(this, a.node);
37358         }
37359     }
37360 });/*
37361  * Based on:
37362  * Ext JS Library 1.1.1
37363  * Copyright(c) 2006-2007, Ext JS, LLC.
37364  *
37365  * Originally Released Under LGPL - original licence link has changed is not relivant.
37366  *
37367  * Fork - LGPL
37368  * <script type="text/javascript">
37369  */
37370
37371 /**
37372 * @class Roo.tree.TreeFilter
37373 * Note this class is experimental and doesn't update the indent (lines) or expand collapse icons of the nodes
37374 * @param {TreePanel} tree
37375 * @param {Object} config (optional)
37376  */
37377 Roo.tree.TreeFilter = function(tree, config){
37378     this.tree = tree;
37379     this.filtered = {};
37380     Roo.apply(this, config);
37381 };
37382
37383 Roo.tree.TreeFilter.prototype = {
37384     clearBlank:false,
37385     reverse:false,
37386     autoClear:false,
37387     remove:false,
37388
37389      /**
37390      * Filter the data by a specific attribute.
37391      * @param {String/RegExp} value Either string that the attribute value
37392      * should start with or a RegExp to test against the attribute
37393      * @param {String} attr (optional) The attribute passed in your node's attributes collection. Defaults to "text".
37394      * @param {TreeNode} startNode (optional) The node to start the filter at.
37395      */
37396     filter : function(value, attr, startNode){
37397         attr = attr || "text";
37398         var f;
37399         if(typeof value == "string"){
37400             var vlen = value.length;
37401             // auto clear empty filter
37402             if(vlen == 0 && this.clearBlank){
37403                 this.clear();
37404                 return;
37405             }
37406             value = value.toLowerCase();
37407             f = function(n){
37408                 return n.attributes[attr].substr(0, vlen).toLowerCase() == value;
37409             };
37410         }else if(value.exec){ // regex?
37411             f = function(n){
37412                 return value.test(n.attributes[attr]);
37413             };
37414         }else{
37415             throw 'Illegal filter type, must be string or regex';
37416         }
37417         this.filterBy(f, null, startNode);
37418         },
37419
37420     /**
37421      * Filter by a function. The passed function will be called with each
37422      * node in the tree (or from the startNode). If the function returns true, the node is kept
37423      * otherwise it is filtered. If a node is filtered, its children are also filtered.
37424      * @param {Function} fn The filter function
37425      * @param {Object} scope (optional) The scope of the function (defaults to the current node)
37426      */
37427     filterBy : function(fn, scope, startNode){
37428         startNode = startNode || this.tree.root;
37429         if(this.autoClear){
37430             this.clear();
37431         }
37432         var af = this.filtered, rv = this.reverse;
37433         var f = function(n){
37434             if(n == startNode){
37435                 return true;
37436             }
37437             if(af[n.id]){
37438                 return false;
37439             }
37440             var m = fn.call(scope || n, n);
37441             if(!m || rv){
37442                 af[n.id] = n;
37443                 n.ui.hide();
37444                 return false;
37445             }
37446             return true;
37447         };
37448         startNode.cascade(f);
37449         if(this.remove){
37450            for(var id in af){
37451                if(typeof id != "function"){
37452                    var n = af[id];
37453                    if(n && n.parentNode){
37454                        n.parentNode.removeChild(n);
37455                    }
37456                }
37457            }
37458         }
37459     },
37460
37461     /**
37462      * Clears the current filter. Note: with the "remove" option
37463      * set a filter cannot be cleared.
37464      */
37465     clear : function(){
37466         var t = this.tree;
37467         var af = this.filtered;
37468         for(var id in af){
37469             if(typeof id != "function"){
37470                 var n = af[id];
37471                 if(n){
37472                     n.ui.show();
37473                 }
37474             }
37475         }
37476         this.filtered = {};
37477     }
37478 };
37479 /*
37480  * Based on:
37481  * Ext JS Library 1.1.1
37482  * Copyright(c) 2006-2007, Ext JS, LLC.
37483  *
37484  * Originally Released Under LGPL - original licence link has changed is not relivant.
37485  *
37486  * Fork - LGPL
37487  * <script type="text/javascript">
37488  */
37489  
37490
37491 /**
37492  * @class Roo.tree.TreeSorter
37493  * Provides sorting of nodes in a TreePanel
37494  * 
37495  * @cfg {Boolean} folderSort True to sort leaf nodes under non leaf nodes
37496  * @cfg {String} property The named attribute on the node to sort by (defaults to text)
37497  * @cfg {String} dir The direction to sort (asc or desc) (defaults to asc)
37498  * @cfg {String} leafAttr The attribute used to determine leaf nodes in folder sort (defaults to "leaf")
37499  * @cfg {Boolean} caseSensitive true for case sensitive sort (defaults to false)
37500  * @cfg {Function} sortType A custom "casting" function used to convert node values before sorting
37501  * @constructor
37502  * @param {TreePanel} tree
37503  * @param {Object} config
37504  */
37505 Roo.tree.TreeSorter = function(tree, config){
37506     Roo.apply(this, config);
37507     tree.on("beforechildrenrendered", this.doSort, this);
37508     tree.on("append", this.updateSort, this);
37509     tree.on("insert", this.updateSort, this);
37510     
37511     var dsc = this.dir && this.dir.toLowerCase() == "desc";
37512     var p = this.property || "text";
37513     var sortType = this.sortType;
37514     var fs = this.folderSort;
37515     var cs = this.caseSensitive === true;
37516     var leafAttr = this.leafAttr || 'leaf';
37517
37518     this.sortFn = function(n1, n2){
37519         if(fs){
37520             if(n1.attributes[leafAttr] && !n2.attributes[leafAttr]){
37521                 return 1;
37522             }
37523             if(!n1.attributes[leafAttr] && n2.attributes[leafAttr]){
37524                 return -1;
37525             }
37526         }
37527         var v1 = sortType ? sortType(n1) : (cs ? n1.attributes[p] : n1.attributes[p].toUpperCase());
37528         var v2 = sortType ? sortType(n2) : (cs ? n2.attributes[p] : n2.attributes[p].toUpperCase());
37529         if(v1 < v2){
37530                         return dsc ? +1 : -1;
37531                 }else if(v1 > v2){
37532                         return dsc ? -1 : +1;
37533         }else{
37534                 return 0;
37535         }
37536     };
37537 };
37538
37539 Roo.tree.TreeSorter.prototype = {
37540     doSort : function(node){
37541         node.sort(this.sortFn);
37542     },
37543     
37544     compareNodes : function(n1, n2){
37545         return (n1.text.toUpperCase() > n2.text.toUpperCase() ? 1 : -1);
37546     },
37547     
37548     updateSort : function(tree, node){
37549         if(node.childrenRendered){
37550             this.doSort.defer(1, this, [node]);
37551         }
37552     }
37553 };/*
37554  * Based on:
37555  * Ext JS Library 1.1.1
37556  * Copyright(c) 2006-2007, Ext JS, LLC.
37557  *
37558  * Originally Released Under LGPL - original licence link has changed is not relivant.
37559  *
37560  * Fork - LGPL
37561  * <script type="text/javascript">
37562  */
37563
37564 if(Roo.dd.DropZone){
37565     
37566 Roo.tree.TreeDropZone = function(tree, config){
37567     this.allowParentInsert = false;
37568     this.allowContainerDrop = false;
37569     this.appendOnly = false;
37570     Roo.tree.TreeDropZone.superclass.constructor.call(this, tree.innerCt, config);
37571     this.tree = tree;
37572     this.lastInsertClass = "x-tree-no-status";
37573     this.dragOverData = {};
37574 };
37575
37576 Roo.extend(Roo.tree.TreeDropZone, Roo.dd.DropZone, {
37577     ddGroup : "TreeDD",
37578     scroll:  true,
37579     
37580     expandDelay : 1000,
37581     
37582     expandNode : function(node){
37583         if(node.hasChildNodes() && !node.isExpanded()){
37584             node.expand(false, null, this.triggerCacheRefresh.createDelegate(this));
37585         }
37586     },
37587     
37588     queueExpand : function(node){
37589         this.expandProcId = this.expandNode.defer(this.expandDelay, this, [node]);
37590     },
37591     
37592     cancelExpand : function(){
37593         if(this.expandProcId){
37594             clearTimeout(this.expandProcId);
37595             this.expandProcId = false;
37596         }
37597     },
37598     
37599     isValidDropPoint : function(n, pt, dd, e, data){
37600         if(!n || !data){ return false; }
37601         var targetNode = n.node;
37602         var dropNode = data.node;
37603         // default drop rules
37604         if(!(targetNode && targetNode.isTarget && pt)){
37605             return false;
37606         }
37607         if(pt == "append" && targetNode.allowChildren === false){
37608             return false;
37609         }
37610         if((pt == "above" || pt == "below") && (targetNode.parentNode && targetNode.parentNode.allowChildren === false)){
37611             return false;
37612         }
37613         if(dropNode && (targetNode == dropNode || dropNode.contains(targetNode))){
37614             return false;
37615         }
37616         // reuse the object
37617         var overEvent = this.dragOverData;
37618         overEvent.tree = this.tree;
37619         overEvent.target = targetNode;
37620         overEvent.data = data;
37621         overEvent.point = pt;
37622         overEvent.source = dd;
37623         overEvent.rawEvent = e;
37624         overEvent.dropNode = dropNode;
37625         overEvent.cancel = false;  
37626         var result = this.tree.fireEvent("nodedragover", overEvent);
37627         return overEvent.cancel === false && result !== false;
37628     },
37629     
37630     getDropPoint : function(e, n, dd)
37631     {
37632         var tn = n.node;
37633         if(tn.isRoot){
37634             return tn.allowChildren !== false ? "append" : false; // always append for root
37635         }
37636         var dragEl = n.ddel;
37637         var t = Roo.lib.Dom.getY(dragEl), b = t + dragEl.offsetHeight;
37638         var y = Roo.lib.Event.getPageY(e);
37639         //var noAppend = tn.allowChildren === false || tn.isLeaf();
37640         
37641         // we may drop nodes anywhere, as long as allowChildren has not been set to false..
37642         var noAppend = tn.allowChildren === false;
37643         if(this.appendOnly || tn.parentNode.allowChildren === false){
37644             return noAppend ? false : "append";
37645         }
37646         var noBelow = false;
37647         if(!this.allowParentInsert){
37648             noBelow = tn.hasChildNodes() && tn.isExpanded();
37649         }
37650         var q = (b - t) / (noAppend ? 2 : 3);
37651         if(y >= t && y < (t + q)){
37652             return "above";
37653         }else if(!noBelow && (noAppend || y >= b-q && y <= b)){
37654             return "below";
37655         }else{
37656             return "append";
37657         }
37658     },
37659     
37660     onNodeEnter : function(n, dd, e, data)
37661     {
37662         this.cancelExpand();
37663     },
37664     
37665     onNodeOver : function(n, dd, e, data)
37666     {
37667        
37668         var pt = this.getDropPoint(e, n, dd);
37669         var node = n.node;
37670         
37671         // auto node expand check
37672         if(!this.expandProcId && pt == "append" && node.hasChildNodes() && !n.node.isExpanded()){
37673             this.queueExpand(node);
37674         }else if(pt != "append"){
37675             this.cancelExpand();
37676         }
37677         
37678         // set the insert point style on the target node
37679         var returnCls = this.dropNotAllowed;
37680         if(this.isValidDropPoint(n, pt, dd, e, data)){
37681            if(pt){
37682                var el = n.ddel;
37683                var cls;
37684                if(pt == "above"){
37685                    returnCls = n.node.isFirst() ? "x-tree-drop-ok-above" : "x-tree-drop-ok-between";
37686                    cls = "x-tree-drag-insert-above";
37687                }else if(pt == "below"){
37688                    returnCls = n.node.isLast() ? "x-tree-drop-ok-below" : "x-tree-drop-ok-between";
37689                    cls = "x-tree-drag-insert-below";
37690                }else{
37691                    returnCls = "x-tree-drop-ok-append";
37692                    cls = "x-tree-drag-append";
37693                }
37694                if(this.lastInsertClass != cls){
37695                    Roo.fly(el).replaceClass(this.lastInsertClass, cls);
37696                    this.lastInsertClass = cls;
37697                }
37698            }
37699        }
37700        return returnCls;
37701     },
37702     
37703     onNodeOut : function(n, dd, e, data){
37704         
37705         this.cancelExpand();
37706         this.removeDropIndicators(n);
37707     },
37708     
37709     onNodeDrop : function(n, dd, e, data){
37710         var point = this.getDropPoint(e, n, dd);
37711         var targetNode = n.node;
37712         targetNode.ui.startDrop();
37713         if(!this.isValidDropPoint(n, point, dd, e, data)){
37714             targetNode.ui.endDrop();
37715             return false;
37716         }
37717         // first try to find the drop node
37718         var dropNode = data.node || (dd.getTreeNode ? dd.getTreeNode(data, targetNode, point, e) : null);
37719         var dropEvent = {
37720             tree : this.tree,
37721             target: targetNode,
37722             data: data,
37723             point: point,
37724             source: dd,
37725             rawEvent: e,
37726             dropNode: dropNode,
37727             cancel: !dropNode   
37728         };
37729         var retval = this.tree.fireEvent("beforenodedrop", dropEvent);
37730         if(retval === false || dropEvent.cancel === true || !dropEvent.dropNode){
37731             targetNode.ui.endDrop();
37732             return false;
37733         }
37734         // allow target changing
37735         targetNode = dropEvent.target;
37736         if(point == "append" && !targetNode.isExpanded()){
37737             targetNode.expand(false, null, function(){
37738                 this.completeDrop(dropEvent);
37739             }.createDelegate(this));
37740         }else{
37741             this.completeDrop(dropEvent);
37742         }
37743         return true;
37744     },
37745     
37746     completeDrop : function(de){
37747         var ns = de.dropNode, p = de.point, t = de.target;
37748         if(!(ns instanceof Array)){
37749             ns = [ns];
37750         }
37751         var n;
37752         for(var i = 0, len = ns.length; i < len; i++){
37753             n = ns[i];
37754             if(p == "above"){
37755                 t.parentNode.insertBefore(n, t);
37756             }else if(p == "below"){
37757                 t.parentNode.insertBefore(n, t.nextSibling);
37758             }else{
37759                 t.appendChild(n);
37760             }
37761         }
37762         n.ui.focus();
37763         if(this.tree.hlDrop){
37764             n.ui.highlight();
37765         }
37766         t.ui.endDrop();
37767         this.tree.fireEvent("nodedrop", de);
37768     },
37769     
37770     afterNodeMoved : function(dd, data, e, targetNode, dropNode){
37771         if(this.tree.hlDrop){
37772             dropNode.ui.focus();
37773             dropNode.ui.highlight();
37774         }
37775         this.tree.fireEvent("nodedrop", this.tree, targetNode, data, dd, e);
37776     },
37777     
37778     getTree : function(){
37779         return this.tree;
37780     },
37781     
37782     removeDropIndicators : function(n){
37783         if(n && n.ddel){
37784             var el = n.ddel;
37785             Roo.fly(el).removeClass([
37786                     "x-tree-drag-insert-above",
37787                     "x-tree-drag-insert-below",
37788                     "x-tree-drag-append"]);
37789             this.lastInsertClass = "_noclass";
37790         }
37791     },
37792     
37793     beforeDragDrop : function(target, e, id){
37794         this.cancelExpand();
37795         return true;
37796     },
37797     
37798     afterRepair : function(data){
37799         if(data && Roo.enableFx){
37800             data.node.ui.highlight();
37801         }
37802         this.hideProxy();
37803     } 
37804     
37805 });
37806
37807 }
37808 /*
37809  * Based on:
37810  * Ext JS Library 1.1.1
37811  * Copyright(c) 2006-2007, Ext JS, LLC.
37812  *
37813  * Originally Released Under LGPL - original licence link has changed is not relivant.
37814  *
37815  * Fork - LGPL
37816  * <script type="text/javascript">
37817  */
37818  
37819
37820 if(Roo.dd.DragZone){
37821 Roo.tree.TreeDragZone = function(tree, config){
37822     Roo.tree.TreeDragZone.superclass.constructor.call(this, tree.getTreeEl(), config);
37823     this.tree = tree;
37824 };
37825
37826 Roo.extend(Roo.tree.TreeDragZone, Roo.dd.DragZone, {
37827     ddGroup : "TreeDD",
37828    
37829     onBeforeDrag : function(data, e){
37830         var n = data.node;
37831         return n && n.draggable && !n.disabled;
37832     },
37833      
37834     
37835     onInitDrag : function(e){
37836         var data = this.dragData;
37837         this.tree.getSelectionModel().select(data.node);
37838         this.proxy.update("");
37839         data.node.ui.appendDDGhost(this.proxy.ghost.dom);
37840         this.tree.fireEvent("startdrag", this.tree, data.node, e);
37841     },
37842     
37843     getRepairXY : function(e, data){
37844         return data.node.ui.getDDRepairXY();
37845     },
37846     
37847     onEndDrag : function(data, e){
37848         this.tree.fireEvent("enddrag", this.tree, data.node, e);
37849         
37850         
37851     },
37852     
37853     onValidDrop : function(dd, e, id){
37854         this.tree.fireEvent("dragdrop", this.tree, this.dragData.node, dd, e);
37855         this.hideProxy();
37856     },
37857     
37858     beforeInvalidDrop : function(e, id){
37859         // this scrolls the original position back into view
37860         var sm = this.tree.getSelectionModel();
37861         sm.clearSelections();
37862         sm.select(this.dragData.node);
37863     }
37864 });
37865 }/*
37866  * Based on:
37867  * Ext JS Library 1.1.1
37868  * Copyright(c) 2006-2007, Ext JS, LLC.
37869  *
37870  * Originally Released Under LGPL - original licence link has changed is not relivant.
37871  *
37872  * Fork - LGPL
37873  * <script type="text/javascript">
37874  */
37875 /**
37876  * @class Roo.tree.TreeEditor
37877  * @extends Roo.Editor
37878  * Provides editor functionality for inline tree node editing.  Any valid {@link Roo.form.Field} can be used
37879  * as the editor field.
37880  * @constructor
37881  * @param {Object} config (used to be the tree panel.)
37882  * @param {Object} oldconfig DEPRECIATED Either a prebuilt {@link Roo.form.Field} instance or a Field config object
37883  * 
37884  * @cfg {Roo.tree.TreePanel} tree The tree to bind to.
37885  * @cfg {Roo.form.TextField} field [required] The field configuration
37886  *
37887  * 
37888  */
37889 Roo.tree.TreeEditor = function(config, oldconfig) { // was -- (tree, config){
37890     var tree = config;
37891     var field;
37892     if (oldconfig) { // old style..
37893         field = oldconfig.events ? oldconfig : new Roo.form.TextField(oldconfig);
37894     } else {
37895         // new style..
37896         tree = config.tree;
37897         config.field = config.field  || {};
37898         config.field.xtype = 'TextField';
37899         field = Roo.factory(config.field, Roo.form);
37900     }
37901     config = config || {};
37902     
37903     
37904     this.addEvents({
37905         /**
37906          * @event beforenodeedit
37907          * Fires when editing is initiated, but before the value changes.  Editing can be canceled by returning
37908          * false from the handler of this event.
37909          * @param {Editor} this
37910          * @param {Roo.tree.Node} node 
37911          */
37912         "beforenodeedit" : true
37913     });
37914     
37915     //Roo.log(config);
37916     Roo.tree.TreeEditor.superclass.constructor.call(this, field, config);
37917
37918     this.tree = tree;
37919
37920     tree.on('beforeclick', this.beforeNodeClick, this);
37921     tree.getTreeEl().on('mousedown', this.hide, this);
37922     this.on('complete', this.updateNode, this);
37923     this.on('beforestartedit', this.fitToTree, this);
37924     this.on('startedit', this.bindScroll, this, {delay:10});
37925     this.on('specialkey', this.onSpecialKey, this);
37926 };
37927
37928 Roo.extend(Roo.tree.TreeEditor, Roo.Editor, {
37929     /**
37930      * @cfg {String} alignment
37931      * The position to align to (see {@link Roo.Element#alignTo} for more details, defaults to "l-l").
37932      */
37933     alignment: "l-l",
37934     // inherit
37935     autoSize: false,
37936     /**
37937      * @cfg {Boolean} hideEl
37938      * True to hide the bound element while the editor is displayed (defaults to false)
37939      */
37940     hideEl : false,
37941     /**
37942      * @cfg {String} cls
37943      * CSS class to apply to the editor (defaults to "x-small-editor x-tree-editor")
37944      */
37945     cls: "x-small-editor x-tree-editor",
37946     /**
37947      * @cfg {Boolean} shim
37948      * True to shim the editor if selects/iframes could be displayed beneath it (defaults to false)
37949      */
37950     shim:false,
37951     // inherit
37952     shadow:"frame",
37953     /**
37954      * @cfg {Number} maxWidth
37955      * The maximum width in pixels of the editor field (defaults to 250).  Note that if the maxWidth would exceed
37956      * the containing tree element's size, it will be automatically limited for you to the container width, taking
37957      * scroll and client offsets into account prior to each edit.
37958      */
37959     maxWidth: 250,
37960
37961     editDelay : 350,
37962
37963     // private
37964     fitToTree : function(ed, el){
37965         var td = this.tree.getTreeEl().dom, nd = el.dom;
37966         if(td.scrollLeft >  nd.offsetLeft){ // ensure the node left point is visible
37967             td.scrollLeft = nd.offsetLeft;
37968         }
37969         var w = Math.min(
37970                 this.maxWidth,
37971                 (td.clientWidth > 20 ? td.clientWidth : td.offsetWidth) - Math.max(0, nd.offsetLeft-td.scrollLeft) - /*cushion*/5);
37972         this.setSize(w, '');
37973         
37974         return this.fireEvent('beforenodeedit', this, this.editNode);
37975         
37976     },
37977
37978     // private
37979     triggerEdit : function(node){
37980         this.completeEdit();
37981         this.editNode = node;
37982         this.startEdit(node.ui.textNode, node.text);
37983     },
37984
37985     // private
37986     bindScroll : function(){
37987         this.tree.getTreeEl().on('scroll', this.cancelEdit, this);
37988     },
37989
37990     // private
37991     beforeNodeClick : function(node, e){
37992         var sinceLast = (this.lastClick ? this.lastClick.getElapsed() : 0);
37993         this.lastClick = new Date();
37994         if(sinceLast > this.editDelay && this.tree.getSelectionModel().isSelected(node)){
37995             e.stopEvent();
37996             this.triggerEdit(node);
37997             return false;
37998         }
37999         return true;
38000     },
38001
38002     // private
38003     updateNode : function(ed, value){
38004         this.tree.getTreeEl().un('scroll', this.cancelEdit, this);
38005         this.editNode.setText(value);
38006     },
38007
38008     // private
38009     onHide : function(){
38010         Roo.tree.TreeEditor.superclass.onHide.call(this);
38011         if(this.editNode){
38012             this.editNode.ui.focus();
38013         }
38014     },
38015
38016     // private
38017     onSpecialKey : function(field, e){
38018         var k = e.getKey();
38019         if(k == e.ESC){
38020             e.stopEvent();
38021             this.cancelEdit();
38022         }else if(k == e.ENTER && !e.hasModifier()){
38023             e.stopEvent();
38024             this.completeEdit();
38025         }
38026     }
38027 });//<Script type="text/javascript">
38028 /*
38029  * Based on:
38030  * Ext JS Library 1.1.1
38031  * Copyright(c) 2006-2007, Ext JS, LLC.
38032  *
38033  * Originally Released Under LGPL - original licence link has changed is not relivant.
38034  *
38035  * Fork - LGPL
38036  * <script type="text/javascript">
38037  */
38038  
38039 /**
38040  * Not documented??? - probably should be...
38041  */
38042
38043 Roo.tree.ColumnNodeUI = Roo.extend(Roo.tree.TreeNodeUI, {
38044     //focus: Roo.emptyFn, // prevent odd scrolling behavior
38045     
38046     renderElements : function(n, a, targetNode, bulkRender){
38047         //consel.log("renderElements?");
38048         this.indentMarkup = n.parentNode ? n.parentNode.ui.getChildIndent() : '';
38049
38050         var t = n.getOwnerTree();
38051         var tid = Pman.Tab.Document_TypesTree.tree.el.id;
38052         
38053         var cols = t.columns;
38054         var bw = t.borderWidth;
38055         var c = cols[0];
38056         var href = a.href ? a.href : Roo.isGecko ? "" : "#";
38057          var cb = typeof a.checked == "boolean";
38058         var tx = String.format('{0}',n.text || (c.renderer ? c.renderer(a[c.dataIndex], n, a) : a[c.dataIndex]));
38059         var colcls = 'x-t-' + tid + '-c0';
38060         var buf = [
38061             '<li class="x-tree-node">',
38062             
38063                 
38064                 '<div class="x-tree-node-el ', a.cls,'">',
38065                     // extran...
38066                     '<div class="x-tree-col ', colcls, '" style="width:', c.width-bw, 'px;">',
38067                 
38068                 
38069                         '<span class="x-tree-node-indent">',this.indentMarkup,'</span>',
38070                         '<img src="', this.emptyIcon, '" class="x-tree-ec-icon  " />',
38071                         '<img src="', a.icon || this.emptyIcon, '" class="x-tree-node-icon',
38072                            (a.icon ? ' x-tree-node-inline-icon' : ''),
38073                            (a.iconCls ? ' '+a.iconCls : ''),
38074                            '" unselectable="on" />',
38075                         (cb ? ('<input class="x-tree-node-cb" type="checkbox" ' + 
38076                              (a.checked ? 'checked="checked" />' : ' />')) : ''),
38077                              
38078                         '<a class="x-tree-node-anchor" hidefocus="on" href="',href,'" tabIndex="1" ',
38079                             (a.hrefTarget ? ' target="' +a.hrefTarget + '"' : ''), '>',
38080                             '<span unselectable="on" qtip="' + tx + '">',
38081                              tx,
38082                              '</span></a>' ,
38083                     '</div>',
38084                      '<a class="x-tree-node-anchor" hidefocus="on" href="',href,'" tabIndex="1" ',
38085                             (a.hrefTarget ? ' target="' +a.hrefTarget + '"' : ''), '>'
38086                  ];
38087         for(var i = 1, len = cols.length; i < len; i++){
38088             c = cols[i];
38089             colcls = 'x-t-' + tid + '-c' +i;
38090             tx = String.format('{0}', (c.renderer ? c.renderer(a[c.dataIndex], n, a) : a[c.dataIndex]));
38091             buf.push('<div class="x-tree-col ', colcls, ' ' ,(c.cls?c.cls:''),'" style="width:',c.width-bw,'px;">',
38092                         '<div class="x-tree-col-text" qtip="' + tx +'">',tx,"</div>",
38093                       "</div>");
38094          }
38095          
38096          buf.push(
38097             '</a>',
38098             '<div class="x-clear"></div></div>',
38099             '<ul class="x-tree-node-ct" style="display:none;"></ul>',
38100             "</li>");
38101         
38102         if(bulkRender !== true && n.nextSibling && n.nextSibling.ui.getEl()){
38103             this.wrap = Roo.DomHelper.insertHtml("beforeBegin",
38104                                 n.nextSibling.ui.getEl(), buf.join(""));
38105         }else{
38106             this.wrap = Roo.DomHelper.insertHtml("beforeEnd", targetNode, buf.join(""));
38107         }
38108         var el = this.wrap.firstChild;
38109         this.elRow = el;
38110         this.elNode = el.firstChild;
38111         this.ranchor = el.childNodes[1];
38112         this.ctNode = this.wrap.childNodes[1];
38113         var cs = el.firstChild.childNodes;
38114         this.indentNode = cs[0];
38115         this.ecNode = cs[1];
38116         this.iconNode = cs[2];
38117         var index = 3;
38118         if(cb){
38119             this.checkbox = cs[3];
38120             index++;
38121         }
38122         this.anchor = cs[index];
38123         
38124         this.textNode = cs[index].firstChild;
38125         
38126         //el.on("click", this.onClick, this);
38127         //el.on("dblclick", this.onDblClick, this);
38128         
38129         
38130        // console.log(this);
38131     },
38132     initEvents : function(){
38133         Roo.tree.ColumnNodeUI.superclass.initEvents.call(this);
38134         
38135             
38136         var a = this.ranchor;
38137
38138         var el = Roo.get(a);
38139
38140         if(Roo.isOpera){ // opera render bug ignores the CSS
38141             el.setStyle("text-decoration", "none");
38142         }
38143
38144         el.on("click", this.onClick, this);
38145         el.on("dblclick", this.onDblClick, this);
38146         el.on("contextmenu", this.onContextMenu, this);
38147         
38148     },
38149     
38150     /*onSelectedChange : function(state){
38151         if(state){
38152             this.focus();
38153             this.addClass("x-tree-selected");
38154         }else{
38155             //this.blur();
38156             this.removeClass("x-tree-selected");
38157         }
38158     },*/
38159     addClass : function(cls){
38160         if(this.elRow){
38161             Roo.fly(this.elRow).addClass(cls);
38162         }
38163         
38164     },
38165     
38166     
38167     removeClass : function(cls){
38168         if(this.elRow){
38169             Roo.fly(this.elRow).removeClass(cls);
38170         }
38171     }
38172
38173     
38174     
38175 });//<Script type="text/javascript">
38176
38177 /*
38178  * Based on:
38179  * Ext JS Library 1.1.1
38180  * Copyright(c) 2006-2007, Ext JS, LLC.
38181  *
38182  * Originally Released Under LGPL - original licence link has changed is not relivant.
38183  *
38184  * Fork - LGPL
38185  * <script type="text/javascript">
38186  */
38187  
38188
38189 /**
38190  * @class Roo.tree.ColumnTree
38191  * @extends Roo.tree.TreePanel
38192  * @cfg {Object} columns  Including width, header, renderer, cls, dataIndex 
38193  * @cfg {int} borderWidth  compined right/left border allowance
38194  * @constructor
38195  * @param {String/HTMLElement/Element} el The container element
38196  * @param {Object} config
38197  */
38198 Roo.tree.ColumnTree =  function(el, config)
38199 {
38200    Roo.tree.ColumnTree.superclass.constructor.call(this, el , config);
38201    this.addEvents({
38202         /**
38203         * @event resize
38204         * Fire this event on a container when it resizes
38205         * @param {int} w Width
38206         * @param {int} h Height
38207         */
38208        "resize" : true
38209     });
38210     this.on('resize', this.onResize, this);
38211 };
38212
38213 Roo.extend(Roo.tree.ColumnTree, Roo.tree.TreePanel, {
38214     //lines:false,
38215     
38216     
38217     borderWidth: Roo.isBorderBox ? 0 : 2, 
38218     headEls : false,
38219     
38220     render : function(){
38221         // add the header.....
38222        
38223         Roo.tree.ColumnTree.superclass.render.apply(this);
38224         
38225         this.el.addClass('x-column-tree');
38226         
38227         this.headers = this.el.createChild(
38228             {cls:'x-tree-headers'},this.innerCt.dom);
38229    
38230         var cols = this.columns, c;
38231         var totalWidth = 0;
38232         this.headEls = [];
38233         var  len = cols.length;
38234         for(var i = 0; i < len; i++){
38235              c = cols[i];
38236              totalWidth += c.width;
38237             this.headEls.push(this.headers.createChild({
38238                  cls:'x-tree-hd ' + (c.cls?c.cls+'-hd':''),
38239                  cn: {
38240                      cls:'x-tree-hd-text',
38241                      html: c.header
38242                  },
38243                  style:'width:'+(c.width-this.borderWidth)+'px;'
38244              }));
38245         }
38246         this.headers.createChild({cls:'x-clear'});
38247         // prevent floats from wrapping when clipped
38248         this.headers.setWidth(totalWidth);
38249         //this.innerCt.setWidth(totalWidth);
38250         this.innerCt.setStyle({ overflow: 'auto' });
38251         this.onResize(this.width, this.height);
38252              
38253         
38254     },
38255     onResize : function(w,h)
38256     {
38257         this.height = h;
38258         this.width = w;
38259         // resize cols..
38260         this.innerCt.setWidth(this.width);
38261         this.innerCt.setHeight(this.height-20);
38262         
38263         // headers...
38264         var cols = this.columns, c;
38265         var totalWidth = 0;
38266         var expEl = false;
38267         var len = cols.length;
38268         for(var i = 0; i < len; i++){
38269             c = cols[i];
38270             if (this.autoExpandColumn !== false && c.dataIndex == this.autoExpandColumn) {
38271                 // it's the expander..
38272                 expEl  = this.headEls[i];
38273                 continue;
38274             }
38275             totalWidth += c.width;
38276             
38277         }
38278         if (expEl) {
38279             expEl.setWidth(  ((w - totalWidth)-this.borderWidth - 20));
38280         }
38281         this.headers.setWidth(w-20);
38282
38283         
38284         
38285         
38286     }
38287 });
38288 /*
38289  * Based on:
38290  * Ext JS Library 1.1.1
38291  * Copyright(c) 2006-2007, Ext JS, LLC.
38292  *
38293  * Originally Released Under LGPL - original licence link has changed is not relivant.
38294  *
38295  * Fork - LGPL
38296  * <script type="text/javascript">
38297  */
38298  
38299 /**
38300  * @class Roo.menu.Menu
38301  * @extends Roo.util.Observable
38302  * @children Roo.menu.BaseItem
38303  * A menu object.  This is the container to which you add all other menu items.  Menu can also serve a as a base class
38304  * when you want a specialzed menu based off of another component (like {@link Roo.menu.DateMenu} for example).
38305  * @constructor
38306  * Creates a new Menu
38307  * @param {Object} config Configuration options
38308  */
38309 Roo.menu.Menu = function(config){
38310     
38311     Roo.menu.Menu.superclass.constructor.call(this, config);
38312     
38313     this.id = this.id || Roo.id();
38314     this.addEvents({
38315         /**
38316          * @event beforeshow
38317          * Fires before this menu is displayed
38318          * @param {Roo.menu.Menu} this
38319          */
38320         beforeshow : true,
38321         /**
38322          * @event beforehide
38323          * Fires before this menu is hidden
38324          * @param {Roo.menu.Menu} this
38325          */
38326         beforehide : true,
38327         /**
38328          * @event show
38329          * Fires after this menu is displayed
38330          * @param {Roo.menu.Menu} this
38331          */
38332         show : true,
38333         /**
38334          * @event hide
38335          * Fires after this menu is hidden
38336          * @param {Roo.menu.Menu} this
38337          */
38338         hide : true,
38339         /**
38340          * @event click
38341          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
38342          * @param {Roo.menu.Menu} this
38343          * @param {Roo.menu.Item} menuItem The menu item that was clicked
38344          * @param {Roo.EventObject} e
38345          */
38346         click : true,
38347         /**
38348          * @event mouseover
38349          * Fires when the mouse is hovering over this menu
38350          * @param {Roo.menu.Menu} this
38351          * @param {Roo.EventObject} e
38352          * @param {Roo.menu.Item} menuItem The menu item that was clicked
38353          */
38354         mouseover : true,
38355         /**
38356          * @event mouseout
38357          * Fires when the mouse exits this menu
38358          * @param {Roo.menu.Menu} this
38359          * @param {Roo.EventObject} e
38360          * @param {Roo.menu.Item} menuItem The menu item that was clicked
38361          */
38362         mouseout : true,
38363         /**
38364          * @event itemclick
38365          * Fires when a menu item contained in this menu is clicked
38366          * @param {Roo.menu.BaseItem} baseItem The BaseItem that was clicked
38367          * @param {Roo.EventObject} e
38368          */
38369         itemclick: true
38370     });
38371     if (this.registerMenu) {
38372         Roo.menu.MenuMgr.register(this);
38373     }
38374     
38375     var mis = this.items;
38376     this.items = new Roo.util.MixedCollection();
38377     if(mis){
38378         this.add.apply(this, mis);
38379     }
38380 };
38381
38382 Roo.extend(Roo.menu.Menu, Roo.util.Observable, {
38383     /**
38384      * @cfg {Number} minWidth The minimum width of the menu in pixels (defaults to 120)
38385      */
38386     minWidth : 120,
38387     /**
38388      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop"
38389      * for bottom-right shadow (defaults to "sides")
38390      */
38391     shadow : "sides",
38392     /**
38393      * @cfg {String} subMenuAlign The {@link Roo.Element#alignTo} anchor position value to use for submenus of
38394      * this menu (defaults to "tl-tr?")
38395      */
38396     subMenuAlign : "tl-tr?",
38397     /**
38398      * @cfg {String} defaultAlign The default {@link Roo.Element#alignTo) anchor position value for this menu
38399      * relative to its element of origin (defaults to "tl-bl?")
38400      */
38401     defaultAlign : "tl-bl?",
38402     /**
38403      * @cfg {Boolean} allowOtherMenus True to allow multiple menus to be displayed at the same time (defaults to false)
38404      */
38405     allowOtherMenus : false,
38406     /**
38407      * @cfg {Boolean} registerMenu True (default) - means that clicking on screen etc. hides it.
38408      */
38409     registerMenu : true,
38410
38411     hidden:true,
38412
38413     // private
38414     render : function(){
38415         if(this.el){
38416             return;
38417         }
38418         var el = this.el = new Roo.Layer({
38419             cls: "x-menu",
38420             shadow:this.shadow,
38421             constrain: false,
38422             parentEl: this.parentEl || document.body,
38423             zindex:15000
38424         });
38425
38426         this.keyNav = new Roo.menu.MenuNav(this);
38427
38428         if(this.plain){
38429             el.addClass("x-menu-plain");
38430         }
38431         if(this.cls){
38432             el.addClass(this.cls);
38433         }
38434         // generic focus element
38435         this.focusEl = el.createChild({
38436             tag: "a", cls: "x-menu-focus", href: "#", onclick: "return false;", tabIndex:"-1"
38437         });
38438         var ul = el.createChild({tag: "ul", cls: "x-menu-list"});
38439         //disabling touch- as it's causing issues ..
38440         //ul.on(Roo.isTouch ? 'touchstart' : 'click'   , this.onClick, this);
38441         ul.on('click'   , this.onClick, this);
38442         
38443         
38444         ul.on("mouseover", this.onMouseOver, this);
38445         ul.on("mouseout", this.onMouseOut, this);
38446         this.items.each(function(item){
38447             if (item.hidden) {
38448                 return;
38449             }
38450             
38451             var li = document.createElement("li");
38452             li.className = "x-menu-list-item";
38453             ul.dom.appendChild(li);
38454             item.render(li, this);
38455         }, this);
38456         this.ul = ul;
38457         this.autoWidth();
38458     },
38459
38460     // private
38461     autoWidth : function(){
38462         var el = this.el, ul = this.ul;
38463         if(!el){
38464             return;
38465         }
38466         var w = this.width;
38467         if(w){
38468             el.setWidth(w);
38469         }else if(Roo.isIE){
38470             el.setWidth(this.minWidth);
38471             var t = el.dom.offsetWidth; // force recalc
38472             el.setWidth(ul.getWidth()+el.getFrameWidth("lr"));
38473         }
38474     },
38475
38476     // private
38477     delayAutoWidth : function(){
38478         if(this.rendered){
38479             if(!this.awTask){
38480                 this.awTask = new Roo.util.DelayedTask(this.autoWidth, this);
38481             }
38482             this.awTask.delay(20);
38483         }
38484     },
38485
38486     // private
38487     findTargetItem : function(e){
38488         var t = e.getTarget(".x-menu-list-item", this.ul,  true);
38489         if(t && t.menuItemId){
38490             return this.items.get(t.menuItemId);
38491         }
38492     },
38493
38494     // private
38495     onClick : function(e){
38496         Roo.log("menu.onClick");
38497         var t = this.findTargetItem(e);
38498         if(!t){
38499             return;
38500         }
38501         Roo.log(e);
38502         if (Roo.isTouch && e.type == 'touchstart' && t.menu  && !t.disabled) {
38503             if(t == this.activeItem && t.shouldDeactivate(e)){
38504                 this.activeItem.deactivate();
38505                 delete this.activeItem;
38506                 return;
38507             }
38508             if(t.canActivate){
38509                 this.setActiveItem(t, true);
38510             }
38511             return;
38512             
38513             
38514         }
38515         
38516         t.onClick(e);
38517         this.fireEvent("click", this, t, e);
38518     },
38519
38520     // private
38521     setActiveItem : function(item, autoExpand){
38522         if(item != this.activeItem){
38523             if(this.activeItem){
38524                 this.activeItem.deactivate();
38525             }
38526             this.activeItem = item;
38527             item.activate(autoExpand);
38528         }else if(autoExpand){
38529             item.expandMenu();
38530         }
38531     },
38532
38533     // private
38534     tryActivate : function(start, step){
38535         var items = this.items;
38536         for(var i = start, len = items.length; i >= 0 && i < len; i+= step){
38537             var item = items.get(i);
38538             if(!item.disabled && item.canActivate){
38539                 this.setActiveItem(item, false);
38540                 return item;
38541             }
38542         }
38543         return false;
38544     },
38545
38546     // private
38547     onMouseOver : function(e){
38548         var t;
38549         if(t = this.findTargetItem(e)){
38550             if(t.canActivate && !t.disabled){
38551                 this.setActiveItem(t, true);
38552             }
38553         }
38554         this.fireEvent("mouseover", this, e, t);
38555     },
38556
38557     // private
38558     onMouseOut : function(e){
38559         var t;
38560         if(t = this.findTargetItem(e)){
38561             if(t == this.activeItem && t.shouldDeactivate(e)){
38562                 this.activeItem.deactivate();
38563                 delete this.activeItem;
38564             }
38565         }
38566         this.fireEvent("mouseout", this, e, t);
38567     },
38568
38569     /**
38570      * Read-only.  Returns true if the menu is currently displayed, else false.
38571      * @type Boolean
38572      */
38573     isVisible : function(){
38574         return this.el && !this.hidden;
38575     },
38576
38577     /**
38578      * Displays this menu relative to another element
38579      * @param {String/HTMLElement/Roo.Element} element The element to align to
38580      * @param {String} position (optional) The {@link Roo.Element#alignTo} anchor position to use in aligning to
38581      * the element (defaults to this.defaultAlign)
38582      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
38583      */
38584     show : function(el, pos, parentMenu){
38585         this.parentMenu = parentMenu;
38586         if(!this.el){
38587             this.render();
38588         }
38589         this.fireEvent("beforeshow", this);
38590         this.showAt(this.el.getAlignToXY(el, pos || this.defaultAlign), parentMenu, false);
38591     },
38592
38593     /**
38594      * Displays this menu at a specific xy position
38595      * @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)
38596      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
38597      */
38598     showAt : function(xy, parentMenu, /* private: */_e){
38599         this.parentMenu = parentMenu;
38600         if(!this.el){
38601             this.render();
38602         }
38603         if(_e !== false){
38604             this.fireEvent("beforeshow", this);
38605             xy = this.el.adjustForConstraints(xy);
38606         }
38607         this.el.setXY(xy);
38608         this.el.show();
38609         this.hidden = false;
38610         this.focus();
38611         this.fireEvent("show", this);
38612     },
38613
38614     focus : function(){
38615         if(!this.hidden){
38616             this.doFocus.defer(50, this);
38617         }
38618     },
38619
38620     doFocus : function(){
38621         if(!this.hidden){
38622             this.focusEl.focus();
38623         }
38624     },
38625
38626     /**
38627      * Hides this menu and optionally all parent menus
38628      * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
38629      */
38630     hide : function(deep){
38631         if(this.el && this.isVisible()){
38632             this.fireEvent("beforehide", this);
38633             if(this.activeItem){
38634                 this.activeItem.deactivate();
38635                 this.activeItem = null;
38636             }
38637             this.el.hide();
38638             this.hidden = true;
38639             this.fireEvent("hide", this);
38640         }
38641         if(deep === true && this.parentMenu){
38642             this.parentMenu.hide(true);
38643         }
38644     },
38645
38646     /**
38647      * Addds one or more items of any type supported by the Menu class, or that can be converted into menu items.
38648      * Any of the following are valid:
38649      * <ul>
38650      * <li>Any menu item object based on {@link Roo.menu.Item}</li>
38651      * <li>An HTMLElement object which will be converted to a menu item</li>
38652      * <li>A menu item config object that will be created as a new menu item</li>
38653      * <li>A string, which can either be '-' or 'separator' to add a menu separator, otherwise
38654      * it will be converted into a {@link Roo.menu.TextItem} and added</li>
38655      * </ul>
38656      * Usage:
38657      * <pre><code>
38658 // Create the menu
38659 var menu = new Roo.menu.Menu();
38660
38661 // Create a menu item to add by reference
38662 var menuItem = new Roo.menu.Item({ text: 'New Item!' });
38663
38664 // Add a bunch of items at once using different methods.
38665 // Only the last item added will be returned.
38666 var item = menu.add(
38667     menuItem,                // add existing item by ref
38668     'Dynamic Item',          // new TextItem
38669     '-',                     // new separator
38670     { text: 'Config Item' }  // new item by config
38671 );
38672 </code></pre>
38673      * @param {Mixed} args One or more menu items, menu item configs or other objects that can be converted to menu items
38674      * @return {Roo.menu.Item} The menu item that was added, or the last one if multiple items were added
38675      */
38676     add : function(){
38677         var a = arguments, l = a.length, item;
38678         for(var i = 0; i < l; i++){
38679             var el = a[i];
38680             if ((typeof(el) == "object") && el.xtype && el.xns) {
38681                 el = Roo.factory(el, Roo.menu);
38682             }
38683             
38684             if(el.render){ // some kind of Item
38685                 item = this.addItem(el);
38686             }else if(typeof el == "string"){ // string
38687                 if(el == "separator" || el == "-"){
38688                     item = this.addSeparator();
38689                 }else{
38690                     item = this.addText(el);
38691                 }
38692             }else if(el.tagName || el.el){ // element
38693                 item = this.addElement(el);
38694             }else if(typeof el == "object"){ // must be menu item config?
38695                 item = this.addMenuItem(el);
38696             }
38697         }
38698         return item;
38699     },
38700
38701     /**
38702      * Returns this menu's underlying {@link Roo.Element} object
38703      * @return {Roo.Element} The element
38704      */
38705     getEl : function(){
38706         if(!this.el){
38707             this.render();
38708         }
38709         return this.el;
38710     },
38711
38712     /**
38713      * Adds a separator bar to the menu
38714      * @return {Roo.menu.Item} The menu item that was added
38715      */
38716     addSeparator : function(){
38717         return this.addItem(new Roo.menu.Separator());
38718     },
38719
38720     /**
38721      * Adds an {@link Roo.Element} object to the menu
38722      * @param {String/HTMLElement/Roo.Element} el The element or DOM node to add, or its id
38723      * @return {Roo.menu.Item} The menu item that was added
38724      */
38725     addElement : function(el){
38726         return this.addItem(new Roo.menu.BaseItem(el));
38727     },
38728
38729     /**
38730      * Adds an existing object based on {@link Roo.menu.Item} to the menu
38731      * @param {Roo.menu.Item} item The menu item to add
38732      * @return {Roo.menu.Item} The menu item that was added
38733      */
38734     addItem : function(item){
38735         this.items.add(item);
38736         if(this.ul){
38737             var li = document.createElement("li");
38738             li.className = "x-menu-list-item";
38739             this.ul.dom.appendChild(li);
38740             item.render(li, this);
38741             this.delayAutoWidth();
38742         }
38743         return item;
38744     },
38745
38746     /**
38747      * Creates a new {@link Roo.menu.Item} based an the supplied config object and adds it to the menu
38748      * @param {Object} config A MenuItem config object
38749      * @return {Roo.menu.Item} The menu item that was added
38750      */
38751     addMenuItem : function(config){
38752         if(!(config instanceof Roo.menu.Item)){
38753             if(typeof config.checked == "boolean"){ // must be check menu item config?
38754                 config = new Roo.menu.CheckItem(config);
38755             }else{
38756                 config = new Roo.menu.Item(config);
38757             }
38758         }
38759         return this.addItem(config);
38760     },
38761
38762     /**
38763      * Creates a new {@link Roo.menu.TextItem} with the supplied text and adds it to the menu
38764      * @param {String} text The text to display in the menu item
38765      * @return {Roo.menu.Item} The menu item that was added
38766      */
38767     addText : function(text){
38768         return this.addItem(new Roo.menu.TextItem({ text : text }));
38769     },
38770
38771     /**
38772      * Inserts an existing object based on {@link Roo.menu.Item} to the menu at a specified index
38773      * @param {Number} index The index in the menu's list of current items where the new item should be inserted
38774      * @param {Roo.menu.Item} item The menu item to add
38775      * @return {Roo.menu.Item} The menu item that was added
38776      */
38777     insert : function(index, item){
38778         this.items.insert(index, item);
38779         if(this.ul){
38780             var li = document.createElement("li");
38781             li.className = "x-menu-list-item";
38782             this.ul.dom.insertBefore(li, this.ul.dom.childNodes[index]);
38783             item.render(li, this);
38784             this.delayAutoWidth();
38785         }
38786         return item;
38787     },
38788
38789     /**
38790      * Removes an {@link Roo.menu.Item} from the menu and destroys the object
38791      * @param {Roo.menu.Item} item The menu item to remove
38792      */
38793     remove : function(item){
38794         this.items.removeKey(item.id);
38795         item.destroy();
38796     },
38797
38798     /**
38799      * Removes and destroys all items in the menu
38800      */
38801     removeAll : function(){
38802         var f;
38803         while(f = this.items.first()){
38804             this.remove(f);
38805         }
38806     }
38807 });
38808
38809 // MenuNav is a private utility class used internally by the Menu
38810 Roo.menu.MenuNav = function(menu){
38811     Roo.menu.MenuNav.superclass.constructor.call(this, menu.el);
38812     this.scope = this.menu = menu;
38813 };
38814
38815 Roo.extend(Roo.menu.MenuNav, Roo.KeyNav, {
38816     doRelay : function(e, h){
38817         var k = e.getKey();
38818         if(!this.menu.activeItem && e.isNavKeyPress() && k != e.SPACE && k != e.RETURN){
38819             this.menu.tryActivate(0, 1);
38820             return false;
38821         }
38822         return h.call(this.scope || this, e, this.menu);
38823     },
38824
38825     up : function(e, m){
38826         if(!m.tryActivate(m.items.indexOf(m.activeItem)-1, -1)){
38827             m.tryActivate(m.items.length-1, -1);
38828         }
38829     },
38830
38831     down : function(e, m){
38832         if(!m.tryActivate(m.items.indexOf(m.activeItem)+1, 1)){
38833             m.tryActivate(0, 1);
38834         }
38835     },
38836
38837     right : function(e, m){
38838         if(m.activeItem){
38839             m.activeItem.expandMenu(true);
38840         }
38841     },
38842
38843     left : function(e, m){
38844         m.hide();
38845         if(m.parentMenu && m.parentMenu.activeItem){
38846             m.parentMenu.activeItem.activate();
38847         }
38848     },
38849
38850     enter : function(e, m){
38851         if(m.activeItem){
38852             e.stopPropagation();
38853             m.activeItem.onClick(e);
38854             m.fireEvent("click", this, m.activeItem);
38855             return true;
38856         }
38857     }
38858 });/*
38859  * Based on:
38860  * Ext JS Library 1.1.1
38861  * Copyright(c) 2006-2007, Ext JS, LLC.
38862  *
38863  * Originally Released Under LGPL - original licence link has changed is not relivant.
38864  *
38865  * Fork - LGPL
38866  * <script type="text/javascript">
38867  */
38868  
38869 /**
38870  * @class Roo.menu.MenuMgr
38871  * Provides a common registry of all menu items on a page so that they can be easily accessed by id.
38872  * @static
38873  */
38874 Roo.menu.MenuMgr = function(){
38875    var menus, active, groups = {}, attached = false, lastShow = new Date();
38876
38877    // private - called when first menu is created
38878    function init(){
38879        menus = {};
38880        active = new Roo.util.MixedCollection();
38881        Roo.get(document).addKeyListener(27, function(){
38882            if(active.length > 0){
38883                hideAll();
38884            }
38885        });
38886    }
38887
38888    // private
38889    function hideAll(){
38890        if(active && active.length > 0){
38891            var c = active.clone();
38892            c.each(function(m){
38893                m.hide();
38894            });
38895        }
38896    }
38897
38898    // private
38899    function onHide(m){
38900        active.remove(m);
38901        if(active.length < 1){
38902            Roo.get(document).un("mousedown", onMouseDown);
38903            attached = false;
38904        }
38905    }
38906
38907    // private
38908    function onShow(m){
38909        var last = active.last();
38910        lastShow = new Date();
38911        active.add(m);
38912        if(!attached){
38913            Roo.get(document).on("mousedown", onMouseDown);
38914            attached = true;
38915        }
38916        if(m.parentMenu){
38917           m.getEl().setZIndex(parseInt(m.parentMenu.getEl().getStyle("z-index"), 10) + 3);
38918           m.parentMenu.activeChild = m;
38919        }else if(last && last.isVisible()){
38920           m.getEl().setZIndex(parseInt(last.getEl().getStyle("z-index"), 10) + 3);
38921        }
38922    }
38923
38924    // private
38925    function onBeforeHide(m){
38926        if(m.activeChild){
38927            m.activeChild.hide();
38928        }
38929        if(m.autoHideTimer){
38930            clearTimeout(m.autoHideTimer);
38931            delete m.autoHideTimer;
38932        }
38933    }
38934
38935    // private
38936    function onBeforeShow(m){
38937        var pm = m.parentMenu;
38938        if(!pm && !m.allowOtherMenus){
38939            hideAll();
38940        }else if(pm && pm.activeChild && active != m){
38941            pm.activeChild.hide();
38942        }
38943    }
38944
38945    // private
38946    function onMouseDown(e){
38947        if(lastShow.getElapsed() > 50 && active.length > 0 && !e.getTarget(".x-menu")){
38948            hideAll();
38949        }
38950    }
38951
38952    // private
38953    function onBeforeCheck(mi, state){
38954        if(state){
38955            var g = groups[mi.group];
38956            for(var i = 0, l = g.length; i < l; i++){
38957                if(g[i] != mi){
38958                    g[i].setChecked(false);
38959                }
38960            }
38961        }
38962    }
38963
38964    return {
38965
38966        /**
38967         * Hides all menus that are currently visible
38968         */
38969        hideAll : function(){
38970             hideAll();  
38971        },
38972
38973        // private
38974        register : function(menu){
38975            if(!menus){
38976                init();
38977            }
38978            menus[menu.id] = menu;
38979            menu.on("beforehide", onBeforeHide);
38980            menu.on("hide", onHide);
38981            menu.on("beforeshow", onBeforeShow);
38982            menu.on("show", onShow);
38983            var g = menu.group;
38984            if(g && menu.events["checkchange"]){
38985                if(!groups[g]){
38986                    groups[g] = [];
38987                }
38988                groups[g].push(menu);
38989                menu.on("checkchange", onCheck);
38990            }
38991        },
38992
38993         /**
38994          * Returns a {@link Roo.menu.Menu} object
38995          * @param {String/Object} menu The string menu id, an existing menu object reference, or a Menu config that will
38996          * be used to generate and return a new Menu instance.
38997          */
38998        get : function(menu){
38999            if(typeof menu == "string"){ // menu id
39000                return menus[menu];
39001            }else if(menu.events){  // menu instance
39002                return menu;
39003            }else if(typeof menu.length == 'number'){ // array of menu items?
39004                return new Roo.menu.Menu({items:menu});
39005            }else{ // otherwise, must be a config
39006                return new Roo.menu.Menu(menu);
39007            }
39008        },
39009
39010        // private
39011        unregister : function(menu){
39012            delete menus[menu.id];
39013            menu.un("beforehide", onBeforeHide);
39014            menu.un("hide", onHide);
39015            menu.un("beforeshow", onBeforeShow);
39016            menu.un("show", onShow);
39017            var g = menu.group;
39018            if(g && menu.events["checkchange"]){
39019                groups[g].remove(menu);
39020                menu.un("checkchange", onCheck);
39021            }
39022        },
39023
39024        // private
39025        registerCheckable : function(menuItem){
39026            var g = menuItem.group;
39027            if(g){
39028                if(!groups[g]){
39029                    groups[g] = [];
39030                }
39031                groups[g].push(menuItem);
39032                menuItem.on("beforecheckchange", onBeforeCheck);
39033            }
39034        },
39035
39036        // private
39037        unregisterCheckable : function(menuItem){
39038            var g = menuItem.group;
39039            if(g){
39040                groups[g].remove(menuItem);
39041                menuItem.un("beforecheckchange", onBeforeCheck);
39042            }
39043        }
39044    };
39045 }();/*
39046  * Based on:
39047  * Ext JS Library 1.1.1
39048  * Copyright(c) 2006-2007, Ext JS, LLC.
39049  *
39050  * Originally Released Under LGPL - original licence link has changed is not relivant.
39051  *
39052  * Fork - LGPL
39053  * <script type="text/javascript">
39054  */
39055  
39056
39057 /**
39058  * @class Roo.menu.BaseItem
39059  * @extends Roo.Component
39060  * @abstract
39061  * The base class for all items that render into menus.  BaseItem provides default rendering, activated state
39062  * management and base configuration options shared by all menu components.
39063  * @constructor
39064  * Creates a new BaseItem
39065  * @param {Object} config Configuration options
39066  */
39067 Roo.menu.BaseItem = function(config){
39068     Roo.menu.BaseItem.superclass.constructor.call(this, config);
39069
39070     this.addEvents({
39071         /**
39072          * @event click
39073          * Fires when this item is clicked
39074          * @param {Roo.menu.BaseItem} this
39075          * @param {Roo.EventObject} e
39076          */
39077         click: true,
39078         /**
39079          * @event activate
39080          * Fires when this item is activated
39081          * @param {Roo.menu.BaseItem} this
39082          */
39083         activate : true,
39084         /**
39085          * @event deactivate
39086          * Fires when this item is deactivated
39087          * @param {Roo.menu.BaseItem} this
39088          */
39089         deactivate : true
39090     });
39091
39092     if(this.handler){
39093         this.on("click", this.handler, this.scope, true);
39094     }
39095 };
39096
39097 Roo.extend(Roo.menu.BaseItem, Roo.Component, {
39098     /**
39099      * @cfg {Function} handler
39100      * A function that will handle the click event of this menu item (defaults to undefined)
39101      */
39102     /**
39103      * @cfg {Boolean} canActivate True if this item can be visually activated (defaults to false)
39104      */
39105     canActivate : false,
39106     
39107      /**
39108      * @cfg {Boolean} hidden True to prevent creation of this menu item (defaults to false)
39109      */
39110     hidden: false,
39111     
39112     /**
39113      * @cfg {String} activeClass The CSS class to use when the item becomes activated (defaults to "x-menu-item-active")
39114      */
39115     activeClass : "x-menu-item-active",
39116     /**
39117      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to true)
39118      */
39119     hideOnClick : true,
39120     /**
39121      * @cfg {Number} hideDelay Length of time in milliseconds to wait before hiding after a click (defaults to 100)
39122      */
39123     hideDelay : 100,
39124
39125     // private
39126     ctype: "Roo.menu.BaseItem",
39127
39128     // private
39129     actionMode : "container",
39130
39131     // private
39132     render : function(container, parentMenu){
39133         this.parentMenu = parentMenu;
39134         Roo.menu.BaseItem.superclass.render.call(this, container);
39135         this.container.menuItemId = this.id;
39136     },
39137
39138     // private
39139     onRender : function(container, position){
39140         this.el = Roo.get(this.el);
39141         container.dom.appendChild(this.el.dom);
39142     },
39143
39144     // private
39145     onClick : function(e){
39146         if(!this.disabled && this.fireEvent("click", this, e) !== false
39147                 && this.parentMenu.fireEvent("itemclick", this, e) !== false){
39148             this.handleClick(e);
39149         }else{
39150             e.stopEvent();
39151         }
39152     },
39153
39154     // private
39155     activate : function(){
39156         if(this.disabled){
39157             return false;
39158         }
39159         var li = this.container;
39160         li.addClass(this.activeClass);
39161         this.region = li.getRegion().adjust(2, 2, -2, -2);
39162         this.fireEvent("activate", this);
39163         return true;
39164     },
39165
39166     // private
39167     deactivate : function(){
39168         this.container.removeClass(this.activeClass);
39169         this.fireEvent("deactivate", this);
39170     },
39171
39172     // private
39173     shouldDeactivate : function(e){
39174         return !this.region || !this.region.contains(e.getPoint());
39175     },
39176
39177     // private
39178     handleClick : function(e){
39179         if(this.hideOnClick){
39180             this.parentMenu.hide.defer(this.hideDelay, this.parentMenu, [true]);
39181         }
39182     },
39183
39184     // private
39185     expandMenu : function(autoActivate){
39186         // do nothing
39187     },
39188
39189     // private
39190     hideMenu : function(){
39191         // do nothing
39192     }
39193 });/*
39194  * Based on:
39195  * Ext JS Library 1.1.1
39196  * Copyright(c) 2006-2007, Ext JS, LLC.
39197  *
39198  * Originally Released Under LGPL - original licence link has changed is not relivant.
39199  *
39200  * Fork - LGPL
39201  * <script type="text/javascript">
39202  */
39203  
39204 /**
39205  * @class Roo.menu.Adapter
39206  * @extends Roo.menu.BaseItem
39207  * @abstract
39208  * 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.
39209  * It provides basic rendering, activation management and enable/disable logic required to work in menus.
39210  * @constructor
39211  * Creates a new Adapter
39212  * @param {Object} config Configuration options
39213  */
39214 Roo.menu.Adapter = function(component, config){
39215     Roo.menu.Adapter.superclass.constructor.call(this, config);
39216     this.component = component;
39217 };
39218 Roo.extend(Roo.menu.Adapter, Roo.menu.BaseItem, {
39219     // private
39220     canActivate : true,
39221
39222     // private
39223     onRender : function(container, position){
39224         this.component.render(container);
39225         this.el = this.component.getEl();
39226     },
39227
39228     // private
39229     activate : function(){
39230         if(this.disabled){
39231             return false;
39232         }
39233         this.component.focus();
39234         this.fireEvent("activate", this);
39235         return true;
39236     },
39237
39238     // private
39239     deactivate : function(){
39240         this.fireEvent("deactivate", this);
39241     },
39242
39243     // private
39244     disable : function(){
39245         this.component.disable();
39246         Roo.menu.Adapter.superclass.disable.call(this);
39247     },
39248
39249     // private
39250     enable : function(){
39251         this.component.enable();
39252         Roo.menu.Adapter.superclass.enable.call(this);
39253     }
39254 });/*
39255  * Based on:
39256  * Ext JS Library 1.1.1
39257  * Copyright(c) 2006-2007, Ext JS, LLC.
39258  *
39259  * Originally Released Under LGPL - original licence link has changed is not relivant.
39260  *
39261  * Fork - LGPL
39262  * <script type="text/javascript">
39263  */
39264
39265 /**
39266  * @class Roo.menu.TextItem
39267  * @extends Roo.menu.BaseItem
39268  * Adds a static text string to a menu, usually used as either a heading or group separator.
39269  * Note: old style constructor with text is still supported.
39270  * 
39271  * @constructor
39272  * Creates a new TextItem
39273  * @param {Object} cfg Configuration
39274  */
39275 Roo.menu.TextItem = function(cfg){
39276     if (typeof(cfg) == 'string') {
39277         this.text = cfg;
39278     } else {
39279         Roo.apply(this,cfg);
39280     }
39281     
39282     Roo.menu.TextItem.superclass.constructor.call(this);
39283 };
39284
39285 Roo.extend(Roo.menu.TextItem, Roo.menu.BaseItem, {
39286     /**
39287      * @cfg {String} text Text to show on item.
39288      */
39289     text : '',
39290     
39291     /**
39292      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to false)
39293      */
39294     hideOnClick : false,
39295     /**
39296      * @cfg {String} itemCls The default CSS class to use for text items (defaults to "x-menu-text")
39297      */
39298     itemCls : "x-menu-text",
39299
39300     // private
39301     onRender : function(){
39302         var s = document.createElement("span");
39303         s.className = this.itemCls;
39304         s.innerHTML = this.text;
39305         this.el = s;
39306         Roo.menu.TextItem.superclass.onRender.apply(this, arguments);
39307     }
39308 });/*
39309  * Based on:
39310  * Ext JS Library 1.1.1
39311  * Copyright(c) 2006-2007, Ext JS, LLC.
39312  *
39313  * Originally Released Under LGPL - original licence link has changed is not relivant.
39314  *
39315  * Fork - LGPL
39316  * <script type="text/javascript">
39317  */
39318
39319 /**
39320  * @class Roo.menu.Separator
39321  * @extends Roo.menu.BaseItem
39322  * Adds a separator bar to a menu, used to divide logical groups of menu items. Generally you will
39323  * add one of these by using "-" in you call to add() or in your items config rather than creating one directly.
39324  * @constructor
39325  * @param {Object} config Configuration options
39326  */
39327 Roo.menu.Separator = function(config){
39328     Roo.menu.Separator.superclass.constructor.call(this, config);
39329 };
39330
39331 Roo.extend(Roo.menu.Separator, Roo.menu.BaseItem, {
39332     /**
39333      * @cfg {String} itemCls The default CSS class to use for separators (defaults to "x-menu-sep")
39334      */
39335     itemCls : "x-menu-sep",
39336     /**
39337      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to false)
39338      */
39339     hideOnClick : false,
39340
39341     // private
39342     onRender : function(li){
39343         var s = document.createElement("span");
39344         s.className = this.itemCls;
39345         s.innerHTML = "&#160;";
39346         this.el = s;
39347         li.addClass("x-menu-sep-li");
39348         Roo.menu.Separator.superclass.onRender.apply(this, arguments);
39349     }
39350 });/*
39351  * Based on:
39352  * Ext JS Library 1.1.1
39353  * Copyright(c) 2006-2007, Ext JS, LLC.
39354  *
39355  * Originally Released Under LGPL - original licence link has changed is not relivant.
39356  *
39357  * Fork - LGPL
39358  * <script type="text/javascript">
39359  */
39360 /**
39361  * @class Roo.menu.Item
39362  * @extends Roo.menu.BaseItem
39363  * A base class for all menu items that require menu-related functionality (like sub-menus) and are not static
39364  * display items.  Item extends the base functionality of {@link Roo.menu.BaseItem} by adding menu-specific
39365  * activation and click handling.
39366  * @constructor
39367  * Creates a new Item
39368  * @param {Object} config Configuration options
39369  */
39370 Roo.menu.Item = function(config){
39371     Roo.menu.Item.superclass.constructor.call(this, config);
39372     if(this.menu){
39373         this.menu = Roo.menu.MenuMgr.get(this.menu);
39374     }
39375 };
39376 Roo.extend(Roo.menu.Item, Roo.menu.BaseItem, {
39377     /**
39378      * @cfg {Roo.menu.Menu} menu
39379      * A Sub menu
39380      */
39381     /**
39382      * @cfg {String} text
39383      * The text to show on the menu item.
39384      */
39385     text: '',
39386      /**
39387      * @cfg {String} HTML to render in menu
39388      * The text to show on the menu item (HTML version).
39389      */
39390     html: '',
39391     /**
39392      * @cfg {String} icon
39393      * The path to an icon to display in this menu item (defaults to Roo.BLANK_IMAGE_URL)
39394      */
39395     icon: undefined,
39396     /**
39397      * @cfg {String} itemCls The default CSS class to use for menu items (defaults to "x-menu-item")
39398      */
39399     itemCls : "x-menu-item",
39400     /**
39401      * @cfg {Boolean} canActivate True if this item can be visually activated (defaults to true)
39402      */
39403     canActivate : true,
39404     /**
39405      * @cfg {Number} showDelay Length of time in milliseconds to wait before showing this item (defaults to 200)
39406      */
39407     showDelay: 200,
39408     // doc'd in BaseItem
39409     hideDelay: 200,
39410
39411     // private
39412     ctype: "Roo.menu.Item",
39413     
39414     // private
39415     onRender : function(container, position){
39416         var el = document.createElement("a");
39417         el.hideFocus = true;
39418         el.unselectable = "on";
39419         el.href = this.href || "#";
39420         if(this.hrefTarget){
39421             el.target = this.hrefTarget;
39422         }
39423         el.className = this.itemCls + (this.menu ?  " x-menu-item-arrow" : "") + (this.cls ?  " " + this.cls : "");
39424         
39425         var html = this.html.length ? this.html  : String.format('{0}',this.text);
39426         
39427         el.innerHTML = String.format(
39428                 '<img src="{0}" class="x-menu-item-icon {1}" />' + html,
39429                 this.icon || Roo.BLANK_IMAGE_URL, this.iconCls || '');
39430         this.el = el;
39431         Roo.menu.Item.superclass.onRender.call(this, container, position);
39432     },
39433
39434     /**
39435      * Sets the text to display in this menu item
39436      * @param {String} text The text to display
39437      * @param {Boolean} isHTML true to indicate text is pure html.
39438      */
39439     setText : function(text, isHTML){
39440         if (isHTML) {
39441             this.html = text;
39442         } else {
39443             this.text = text;
39444             this.html = '';
39445         }
39446         if(this.rendered){
39447             var html = this.html.length ? this.html  : String.format('{0}',this.text);
39448      
39449             this.el.update(String.format(
39450                 '<img src="{0}" class="x-menu-item-icon {2}">' + html,
39451                 this.icon || Roo.BLANK_IMAGE_URL, this.text, this.iconCls || ''));
39452             this.parentMenu.autoWidth();
39453         }
39454     },
39455
39456     // private
39457     handleClick : function(e){
39458         if(!this.href){ // if no link defined, stop the event automatically
39459             e.stopEvent();
39460         }
39461         Roo.menu.Item.superclass.handleClick.apply(this, arguments);
39462     },
39463
39464     // private
39465     activate : function(autoExpand){
39466         if(Roo.menu.Item.superclass.activate.apply(this, arguments)){
39467             this.focus();
39468             if(autoExpand){
39469                 this.expandMenu();
39470             }
39471         }
39472         return true;
39473     },
39474
39475     // private
39476     shouldDeactivate : function(e){
39477         if(Roo.menu.Item.superclass.shouldDeactivate.call(this, e)){
39478             if(this.menu && this.menu.isVisible()){
39479                 return !this.menu.getEl().getRegion().contains(e.getPoint());
39480             }
39481             return true;
39482         }
39483         return false;
39484     },
39485
39486     // private
39487     deactivate : function(){
39488         Roo.menu.Item.superclass.deactivate.apply(this, arguments);
39489         this.hideMenu();
39490     },
39491
39492     // private
39493     expandMenu : function(autoActivate){
39494         if(!this.disabled && this.menu){
39495             clearTimeout(this.hideTimer);
39496             delete this.hideTimer;
39497             if(!this.menu.isVisible() && !this.showTimer){
39498                 this.showTimer = this.deferExpand.defer(this.showDelay, this, [autoActivate]);
39499             }else if (this.menu.isVisible() && autoActivate){
39500                 this.menu.tryActivate(0, 1);
39501             }
39502         }
39503     },
39504
39505     // private
39506     deferExpand : function(autoActivate){
39507         delete this.showTimer;
39508         this.menu.show(this.container, this.parentMenu.subMenuAlign || "tl-tr?", this.parentMenu);
39509         if(autoActivate){
39510             this.menu.tryActivate(0, 1);
39511         }
39512     },
39513
39514     // private
39515     hideMenu : function(){
39516         clearTimeout(this.showTimer);
39517         delete this.showTimer;
39518         if(!this.hideTimer && this.menu && this.menu.isVisible()){
39519             this.hideTimer = this.deferHide.defer(this.hideDelay, this);
39520         }
39521     },
39522
39523     // private
39524     deferHide : function(){
39525         delete this.hideTimer;
39526         this.menu.hide();
39527     }
39528 });/*
39529  * Based on:
39530  * Ext JS Library 1.1.1
39531  * Copyright(c) 2006-2007, Ext JS, LLC.
39532  *
39533  * Originally Released Under LGPL - original licence link has changed is not relivant.
39534  *
39535  * Fork - LGPL
39536  * <script type="text/javascript">
39537  */
39538  
39539 /**
39540  * @class Roo.menu.CheckItem
39541  * @extends Roo.menu.Item
39542  * Adds a menu item that contains a checkbox by default, but can also be part of a radio group.
39543  * @constructor
39544  * Creates a new CheckItem
39545  * @param {Object} config Configuration options
39546  */
39547 Roo.menu.CheckItem = function(config){
39548     Roo.menu.CheckItem.superclass.constructor.call(this, config);
39549     this.addEvents({
39550         /**
39551          * @event beforecheckchange
39552          * Fires before the checked value is set, providing an opportunity to cancel if needed
39553          * @param {Roo.menu.CheckItem} this
39554          * @param {Boolean} checked The new checked value that will be set
39555          */
39556         "beforecheckchange" : true,
39557         /**
39558          * @event checkchange
39559          * Fires after the checked value has been set
39560          * @param {Roo.menu.CheckItem} this
39561          * @param {Boolean} checked The checked value that was set
39562          */
39563         "checkchange" : true
39564     });
39565     if(this.checkHandler){
39566         this.on('checkchange', this.checkHandler, this.scope);
39567     }
39568 };
39569 Roo.extend(Roo.menu.CheckItem, Roo.menu.Item, {
39570     /**
39571      * @cfg {String} group
39572      * All check items with the same group name will automatically be grouped into a single-select
39573      * radio button group (defaults to '')
39574      */
39575     /**
39576      * @cfg {String} itemCls The default CSS class to use for check items (defaults to "x-menu-item x-menu-check-item")
39577      */
39578     itemCls : "x-menu-item x-menu-check-item",
39579     /**
39580      * @cfg {String} groupClass The default CSS class to use for radio group check items (defaults to "x-menu-group-item")
39581      */
39582     groupClass : "x-menu-group-item",
39583
39584     /**
39585      * @cfg {Boolean} checked True to initialize this checkbox as checked (defaults to false).  Note that
39586      * if this checkbox is part of a radio group (group = true) only the last item in the group that is
39587      * initialized with checked = true will be rendered as checked.
39588      */
39589     checked: false,
39590
39591     // private
39592     ctype: "Roo.menu.CheckItem",
39593
39594     // private
39595     onRender : function(c){
39596         Roo.menu.CheckItem.superclass.onRender.apply(this, arguments);
39597         if(this.group){
39598             this.el.addClass(this.groupClass);
39599         }
39600         Roo.menu.MenuMgr.registerCheckable(this);
39601         if(this.checked){
39602             this.checked = false;
39603             this.setChecked(true, true);
39604         }
39605     },
39606
39607     // private
39608     destroy : function(){
39609         if(this.rendered){
39610             Roo.menu.MenuMgr.unregisterCheckable(this);
39611         }
39612         Roo.menu.CheckItem.superclass.destroy.apply(this, arguments);
39613     },
39614
39615     /**
39616      * Set the checked state of this item
39617      * @param {Boolean} checked The new checked value
39618      * @param {Boolean} suppressEvent (optional) True to prevent the checkchange event from firing (defaults to false)
39619      */
39620     setChecked : function(state, suppressEvent){
39621         if(this.checked != state && this.fireEvent("beforecheckchange", this, state) !== false){
39622             if(this.container){
39623                 this.container[state ? "addClass" : "removeClass"]("x-menu-item-checked");
39624             }
39625             this.checked = state;
39626             if(suppressEvent !== true){
39627                 this.fireEvent("checkchange", this, state);
39628             }
39629         }
39630     },
39631
39632     // private
39633     handleClick : function(e){
39634        if(!this.disabled && !(this.checked && this.group)){// disable unselect on radio item
39635            this.setChecked(!this.checked);
39636        }
39637        Roo.menu.CheckItem.superclass.handleClick.apply(this, arguments);
39638     }
39639 });/*
39640  * Based on:
39641  * Ext JS Library 1.1.1
39642  * Copyright(c) 2006-2007, Ext JS, LLC.
39643  *
39644  * Originally Released Under LGPL - original licence link has changed is not relivant.
39645  *
39646  * Fork - LGPL
39647  * <script type="text/javascript">
39648  */
39649  
39650 /**
39651  * @class Roo.menu.DateItem
39652  * @extends Roo.menu.Adapter
39653  * A menu item that wraps the {@link Roo.DatPicker} component.
39654  * @constructor
39655  * Creates a new DateItem
39656  * @param {Object} config Configuration options
39657  */
39658 Roo.menu.DateItem = function(config){
39659     Roo.menu.DateItem.superclass.constructor.call(this, new Roo.DatePicker(config), config);
39660     /** The Roo.DatePicker object @type Roo.DatePicker */
39661     this.picker = this.component;
39662     this.addEvents({select: true});
39663     
39664     this.picker.on("render", function(picker){
39665         picker.getEl().swallowEvent("click");
39666         picker.container.addClass("x-menu-date-item");
39667     });
39668
39669     this.picker.on("select", this.onSelect, this);
39670 };
39671
39672 Roo.extend(Roo.menu.DateItem, Roo.menu.Adapter, {
39673     // private
39674     onSelect : function(picker, date){
39675         this.fireEvent("select", this, date, picker);
39676         Roo.menu.DateItem.superclass.handleClick.call(this);
39677     }
39678 });/*
39679  * Based on:
39680  * Ext JS Library 1.1.1
39681  * Copyright(c) 2006-2007, Ext JS, LLC.
39682  *
39683  * Originally Released Under LGPL - original licence link has changed is not relivant.
39684  *
39685  * Fork - LGPL
39686  * <script type="text/javascript">
39687  */
39688  
39689 /**
39690  * @class Roo.menu.ColorItem
39691  * @extends Roo.menu.Adapter
39692  * A menu item that wraps the {@link Roo.ColorPalette} component.
39693  * @constructor
39694  * Creates a new ColorItem
39695  * @param {Object} config Configuration options
39696  */
39697 Roo.menu.ColorItem = function(config){
39698     Roo.menu.ColorItem.superclass.constructor.call(this, new Roo.ColorPalette(config), config);
39699     /** The Roo.ColorPalette object @type Roo.ColorPalette */
39700     this.palette = this.component;
39701     this.relayEvents(this.palette, ["select"]);
39702     if(this.selectHandler){
39703         this.on('select', this.selectHandler, this.scope);
39704     }
39705 };
39706 Roo.extend(Roo.menu.ColorItem, Roo.menu.Adapter);/*
39707  * Based on:
39708  * Ext JS Library 1.1.1
39709  * Copyright(c) 2006-2007, Ext JS, LLC.
39710  *
39711  * Originally Released Under LGPL - original licence link has changed is not relivant.
39712  *
39713  * Fork - LGPL
39714  * <script type="text/javascript">
39715  */
39716  
39717
39718 /**
39719  * @class Roo.menu.DateMenu
39720  * @extends Roo.menu.Menu
39721  * A menu containing a {@link Roo.menu.DateItem} component (which provides a date picker).
39722  * @constructor
39723  * Creates a new DateMenu
39724  * @param {Object} config Configuration options
39725  */
39726 Roo.menu.DateMenu = function(config){
39727     Roo.menu.DateMenu.superclass.constructor.call(this, config);
39728     this.plain = true;
39729     var di = new Roo.menu.DateItem(config);
39730     this.add(di);
39731     /**
39732      * The {@link Roo.DatePicker} instance for this DateMenu
39733      * @type DatePicker
39734      */
39735     this.picker = di.picker;
39736     /**
39737      * @event select
39738      * @param {DatePicker} picker
39739      * @param {Date} date
39740      */
39741     this.relayEvents(di, ["select"]);
39742     this.on('beforeshow', function(){
39743         if(this.picker){
39744             this.picker.hideMonthPicker(false);
39745         }
39746     }, this);
39747 };
39748 Roo.extend(Roo.menu.DateMenu, Roo.menu.Menu, {
39749     cls:'x-date-menu'
39750 });/*
39751  * Based on:
39752  * Ext JS Library 1.1.1
39753  * Copyright(c) 2006-2007, Ext JS, LLC.
39754  *
39755  * Originally Released Under LGPL - original licence link has changed is not relivant.
39756  *
39757  * Fork - LGPL
39758  * <script type="text/javascript">
39759  */
39760  
39761
39762 /**
39763  * @class Roo.menu.ColorMenu
39764  * @extends Roo.menu.Menu
39765  * A menu containing a {@link Roo.menu.ColorItem} component (which provides a basic color picker).
39766  * @constructor
39767  * Creates a new ColorMenu
39768  * @param {Object} config Configuration options
39769  */
39770 Roo.menu.ColorMenu = function(config){
39771     Roo.menu.ColorMenu.superclass.constructor.call(this, config);
39772     this.plain = true;
39773     var ci = new Roo.menu.ColorItem(config);
39774     this.add(ci);
39775     /**
39776      * The {@link Roo.ColorPalette} instance for this ColorMenu
39777      * @type ColorPalette
39778      */
39779     this.palette = ci.palette;
39780     /**
39781      * @event select
39782      * @param {ColorPalette} palette
39783      * @param {String} color
39784      */
39785     this.relayEvents(ci, ["select"]);
39786 };
39787 Roo.extend(Roo.menu.ColorMenu, Roo.menu.Menu);/*
39788  * Based on:
39789  * Ext JS Library 1.1.1
39790  * Copyright(c) 2006-2007, Ext JS, LLC.
39791  *
39792  * Originally Released Under LGPL - original licence link has changed is not relivant.
39793  *
39794  * Fork - LGPL
39795  * <script type="text/javascript">
39796  */
39797  
39798 /**
39799  * @class Roo.form.TextItem
39800  * @extends Roo.BoxComponent
39801  * Base class for form fields that provides default event handling, sizing, value handling and other functionality.
39802  * @constructor
39803  * Creates a new TextItem
39804  * @param {Object} config Configuration options
39805  */
39806 Roo.form.TextItem = function(config){
39807     Roo.form.TextItem.superclass.constructor.call(this, config);
39808 };
39809
39810 Roo.extend(Roo.form.TextItem, Roo.BoxComponent,  {
39811     
39812     /**
39813      * @cfg {String} tag the tag for this item (default div)
39814      */
39815     tag : 'div',
39816     /**
39817      * @cfg {String} html the content for this item
39818      */
39819     html : '',
39820     
39821     getAutoCreate : function()
39822     {
39823         var cfg = {
39824             id: this.id,
39825             tag: this.tag,
39826             html: this.html,
39827             cls: 'x-form-item'
39828         };
39829         
39830         return cfg;
39831         
39832     },
39833     
39834     onRender : function(ct, position)
39835     {
39836         Roo.form.TextItem.superclass.onRender.call(this, ct, position);
39837         
39838         if(!this.el){
39839             var cfg = this.getAutoCreate();
39840             if(!cfg.name){
39841                 cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
39842             }
39843             if (!cfg.name.length) {
39844                 delete cfg.name;
39845             }
39846             this.el = ct.createChild(cfg, position);
39847         }
39848     },
39849     /*
39850      * setHTML
39851      * @param {String} html update the Contents of the element.
39852      */
39853     setHTML : function(html)
39854     {
39855         this.fieldEl.dom.innerHTML = html;
39856     }
39857     
39858 });/*
39859  * Based on:
39860  * Ext JS Library 1.1.1
39861  * Copyright(c) 2006-2007, Ext JS, LLC.
39862  *
39863  * Originally Released Under LGPL - original licence link has changed is not relivant.
39864  *
39865  * Fork - LGPL
39866  * <script type="text/javascript">
39867  */
39868  
39869 /**
39870  * @class Roo.form.Field
39871  * @extends Roo.BoxComponent
39872  * Base class for form fields that provides default event handling, sizing, value handling and other functionality.
39873  * @constructor
39874  * Creates a new Field
39875  * @param {Object} config Configuration options
39876  */
39877 Roo.form.Field = function(config){
39878     Roo.form.Field.superclass.constructor.call(this, config);
39879 };
39880
39881 Roo.extend(Roo.form.Field, Roo.BoxComponent,  {
39882     /**
39883      * @cfg {String} fieldLabel Label to use when rendering a form.
39884      */
39885        /**
39886      * @cfg {String} qtip Mouse over tip
39887      */
39888      
39889     /**
39890      * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
39891      */
39892     invalidClass : "x-form-invalid",
39893     /**
39894      * @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")
39895      */
39896     invalidText : "The value in this field is invalid",
39897     /**
39898      * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
39899      */
39900     focusClass : "x-form-focus",
39901     /**
39902      * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
39903       automatic validation (defaults to "keyup").
39904      */
39905     validationEvent : "keyup",
39906     /**
39907      * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
39908      */
39909     validateOnBlur : true,
39910     /**
39911      * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
39912      */
39913     validationDelay : 250,
39914     /**
39915      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
39916      * {tag: "input", type: "text", size: "20", autocomplete: "off"})
39917      */
39918     defaultAutoCreate : {tag: "input", type: "text", size: "20", autocomplete: "new-password"},
39919     /**
39920      * @cfg {String} fieldClass The default CSS class for the field (defaults to "x-form-field")
39921      */
39922     fieldClass : "x-form-field",
39923     /**
39924      * @cfg {String} msgTarget The location where error text should display.  Should be one of the following values (defaults to 'qtip'):
39925      *<pre>
39926 Value         Description
39927 -----------   ----------------------------------------------------------------------
39928 qtip          Display a quick tip when the user hovers over the field
39929 title         Display a default browser title attribute popup
39930 under         Add a block div beneath the field containing the error text
39931 side          Add an error icon to the right of the field with a popup on hover
39932 [element id]  Add the error text directly to the innerHTML of the specified element
39933 </pre>
39934      */
39935     msgTarget : 'qtip',
39936     /**
39937      * @cfg {String} msgFx <b>Experimental</b> The effect used when displaying a validation message under the field (defaults to 'normal').
39938      */
39939     msgFx : 'normal',
39940
39941     /**
39942      * @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.
39943      */
39944     readOnly : false,
39945
39946     /**
39947      * @cfg {Boolean} disabled True to disable the field (defaults to false).
39948      */
39949     disabled : false,
39950
39951     /**
39952      * @cfg {String} inputType The type attribute for input fields -- e.g. radio, text, password (defaults to "text").
39953      */
39954     inputType : undefined,
39955     
39956     /**
39957      * @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).
39958          */
39959         tabIndex : undefined,
39960         
39961     // private
39962     isFormField : true,
39963
39964     // private
39965     hasFocus : false,
39966     /**
39967      * @property {Roo.Element} fieldEl
39968      * Element Containing the rendered Field (with label etc.)
39969      */
39970     /**
39971      * @cfg {Mixed} value A value to initialize this field with.
39972      */
39973     value : undefined,
39974
39975     /**
39976      * @cfg {String} name The field's HTML name attribute.
39977      */
39978     /**
39979      * @cfg {String} cls A CSS class to apply to the field's underlying element.
39980      */
39981     // private
39982     loadedValue : false,
39983      
39984      
39985         // private ??
39986         initComponent : function(){
39987         Roo.form.Field.superclass.initComponent.call(this);
39988         this.addEvents({
39989             /**
39990              * @event focus
39991              * Fires when this field receives input focus.
39992              * @param {Roo.form.Field} this
39993              */
39994             focus : true,
39995             /**
39996              * @event blur
39997              * Fires when this field loses input focus.
39998              * @param {Roo.form.Field} this
39999              */
40000             blur : true,
40001             /**
40002              * @event specialkey
40003              * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
40004              * {@link Roo.EventObject#getKey} to determine which key was pressed.
40005              * @param {Roo.form.Field} this
40006              * @param {Roo.EventObject} e The event object
40007              */
40008             specialkey : true,
40009             /**
40010              * @event change
40011              * Fires just before the field blurs if the field value has changed.
40012              * @param {Roo.form.Field} this
40013              * @param {Mixed} newValue The new value
40014              * @param {Mixed} oldValue The original value
40015              */
40016             change : true,
40017             /**
40018              * @event invalid
40019              * Fires after the field has been marked as invalid.
40020              * @param {Roo.form.Field} this
40021              * @param {String} msg The validation message
40022              */
40023             invalid : true,
40024             /**
40025              * @event valid
40026              * Fires after the field has been validated with no errors.
40027              * @param {Roo.form.Field} this
40028              */
40029             valid : true,
40030              /**
40031              * @event keyup
40032              * Fires after the key up
40033              * @param {Roo.form.Field} this
40034              * @param {Roo.EventObject}  e The event Object
40035              */
40036             keyup : true
40037         });
40038     },
40039
40040     /**
40041      * Returns the name attribute of the field if available
40042      * @return {String} name The field name
40043      */
40044     getName: function(){
40045          return this.rendered && this.el.dom.name ? this.el.dom.name : (this.hiddenName || '');
40046     },
40047
40048     // private
40049     onRender : function(ct, position){
40050         Roo.form.Field.superclass.onRender.call(this, ct, position);
40051         if(!this.el){
40052             var cfg = this.getAutoCreate();
40053             if(!cfg.name){
40054                 cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
40055             }
40056             if (!cfg.name.length) {
40057                 delete cfg.name;
40058             }
40059             if(this.inputType){
40060                 cfg.type = this.inputType;
40061             }
40062             this.el = ct.createChild(cfg, position);
40063         }
40064         var type = this.el.dom.type;
40065         if(type){
40066             if(type == 'password'){
40067                 type = 'text';
40068             }
40069             this.el.addClass('x-form-'+type);
40070         }
40071         if(this.readOnly){
40072             this.el.dom.readOnly = true;
40073         }
40074         if(this.tabIndex !== undefined){
40075             this.el.dom.setAttribute('tabIndex', this.tabIndex);
40076         }
40077
40078         this.el.addClass([this.fieldClass, this.cls]);
40079         this.initValue();
40080     },
40081
40082     /**
40083      * Apply the behaviors of this component to an existing element. <b>This is used instead of render().</b>
40084      * @param {String/HTMLElement/Element} el The id of the node, a DOM node or an existing Element
40085      * @return {Roo.form.Field} this
40086      */
40087     applyTo : function(target){
40088         this.allowDomMove = false;
40089         this.el = Roo.get(target);
40090         this.render(this.el.dom.parentNode);
40091         return this;
40092     },
40093
40094     // private
40095     initValue : function(){
40096         if(this.value !== undefined){
40097             this.setValue(this.value);
40098         }else if(this.el.dom.value.length > 0){
40099             this.setValue(this.el.dom.value);
40100         }
40101     },
40102
40103     /**
40104      * Returns true if this field has been changed since it was originally loaded and is not disabled.
40105      * DEPRICATED  - it never worked well - use hasChanged/resetHasChanged.
40106      */
40107     isDirty : function() {
40108         if(this.disabled) {
40109             return false;
40110         }
40111         return String(this.getValue()) !== String(this.originalValue);
40112     },
40113
40114     /**
40115      * stores the current value in loadedValue
40116      */
40117     resetHasChanged : function()
40118     {
40119         this.loadedValue = String(this.getValue());
40120     },
40121     /**
40122      * checks the current value against the 'loaded' value.
40123      * Note - will return false if 'resetHasChanged' has not been called first.
40124      */
40125     hasChanged : function()
40126     {
40127         if(this.disabled || this.readOnly) {
40128             return false;
40129         }
40130         return this.loadedValue !== false && String(this.getValue()) !== this.loadedValue;
40131     },
40132     
40133     
40134     
40135     // private
40136     afterRender : function(){
40137         Roo.form.Field.superclass.afterRender.call(this);
40138         this.initEvents();
40139     },
40140
40141     // private
40142     fireKey : function(e){
40143         //Roo.log('field ' + e.getKey());
40144         if(e.isNavKeyPress()){
40145             this.fireEvent("specialkey", this, e);
40146         }
40147     },
40148
40149     /**
40150      * Resets the current field value to the originally loaded value and clears any validation messages
40151      */
40152     reset : function(){
40153         this.setValue(this.resetValue);
40154         this.originalValue = this.getValue();
40155         this.clearInvalid();
40156     },
40157
40158     // private
40159     initEvents : function(){
40160         // safari killled keypress - so keydown is now used..
40161         this.el.on("keydown" , this.fireKey,  this);
40162         this.el.on("focus", this.onFocus,  this);
40163         this.el.on("blur", this.onBlur,  this);
40164         this.el.relayEvent('keyup', this);
40165
40166         // reference to original value for reset
40167         this.originalValue = this.getValue();
40168         this.resetValue =  this.getValue();
40169     },
40170
40171     // private
40172     onFocus : function(){
40173         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
40174             this.el.addClass(this.focusClass);
40175         }
40176         if(!this.hasFocus){
40177             this.hasFocus = true;
40178             this.startValue = this.getValue();
40179             this.fireEvent("focus", this);
40180         }
40181     },
40182
40183     beforeBlur : Roo.emptyFn,
40184
40185     // private
40186     onBlur : function(){
40187         this.beforeBlur();
40188         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
40189             this.el.removeClass(this.focusClass);
40190         }
40191         this.hasFocus = false;
40192         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
40193             this.validate();
40194         }
40195         var v = this.getValue();
40196         if(String(v) !== String(this.startValue)){
40197             this.fireEvent('change', this, v, this.startValue);
40198         }
40199         this.fireEvent("blur", this);
40200     },
40201
40202     /**
40203      * Returns whether or not the field value is currently valid
40204      * @param {Boolean} preventMark True to disable marking the field invalid
40205      * @return {Boolean} True if the value is valid, else false
40206      */
40207     isValid : function(preventMark){
40208         if(this.disabled){
40209             return true;
40210         }
40211         var restore = this.preventMark;
40212         this.preventMark = preventMark === true;
40213         var v = this.validateValue(this.processValue(this.getRawValue()));
40214         this.preventMark = restore;
40215         return v;
40216     },
40217
40218     /**
40219      * Validates the field value
40220      * @return {Boolean} True if the value is valid, else false
40221      */
40222     validate : function(){
40223         if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
40224             this.clearInvalid();
40225             return true;
40226         }
40227         return false;
40228     },
40229
40230     processValue : function(value){
40231         return value;
40232     },
40233
40234     // private
40235     // Subclasses should provide the validation implementation by overriding this
40236     validateValue : function(value){
40237         return true;
40238     },
40239
40240     /**
40241      * Mark this field as invalid
40242      * @param {String} msg The validation message
40243      */
40244     markInvalid : function(msg){
40245         if(!this.rendered || this.preventMark){ // not rendered
40246             return;
40247         }
40248         
40249         var obj = (typeof(this.combo) != 'undefined') ? this.combo : this; // fix the combox array!!
40250         
40251         obj.el.addClass(this.invalidClass);
40252         msg = msg || this.invalidText;
40253         switch(this.msgTarget){
40254             case 'qtip':
40255                 obj.el.dom.qtip = msg;
40256                 obj.el.dom.qclass = 'x-form-invalid-tip';
40257                 if(Roo.QuickTips){ // fix for floating editors interacting with DND
40258                     Roo.QuickTips.enable();
40259                 }
40260                 break;
40261             case 'title':
40262                 this.el.dom.title = msg;
40263                 break;
40264             case 'under':
40265                 if(!this.errorEl){
40266                     var elp = this.el.findParent('.x-form-element', 5, true);
40267                     this.errorEl = elp.createChild({cls:'x-form-invalid-msg'});
40268                     this.errorEl.setWidth(elp.getWidth(true)-20);
40269                 }
40270                 this.errorEl.update(msg);
40271                 Roo.form.Field.msgFx[this.msgFx].show(this.errorEl, this);
40272                 break;
40273             case 'side':
40274                 if(!this.errorIcon){
40275                     var elp = this.el.findParent('.x-form-element', 5, true);
40276                     this.errorIcon = elp.createChild({cls:'x-form-invalid-icon'});
40277                 }
40278                 this.alignErrorIcon();
40279                 this.errorIcon.dom.qtip = msg;
40280                 this.errorIcon.dom.qclass = 'x-form-invalid-tip';
40281                 this.errorIcon.show();
40282                 this.on('resize', this.alignErrorIcon, this);
40283                 break;
40284             default:
40285                 var t = Roo.getDom(this.msgTarget);
40286                 t.innerHTML = msg;
40287                 t.style.display = this.msgDisplay;
40288                 break;
40289         }
40290         this.fireEvent('invalid', this, msg);
40291     },
40292
40293     // private
40294     alignErrorIcon : function(){
40295         this.errorIcon.alignTo(this.el, 'tl-tr', [2, 0]);
40296     },
40297
40298     /**
40299      * Clear any invalid styles/messages for this field
40300      */
40301     clearInvalid : function(){
40302         if(!this.rendered || this.preventMark){ // not rendered
40303             return;
40304         }
40305         var obj = (typeof(this.combo) != 'undefined') ? this.combo : this; // fix the combox array!!
40306         
40307         obj.el.removeClass(this.invalidClass);
40308         switch(this.msgTarget){
40309             case 'qtip':
40310                 obj.el.dom.qtip = '';
40311                 break;
40312             case 'title':
40313                 this.el.dom.title = '';
40314                 break;
40315             case 'under':
40316                 if(this.errorEl){
40317                     Roo.form.Field.msgFx[this.msgFx].hide(this.errorEl, this);
40318                 }
40319                 break;
40320             case 'side':
40321                 if(this.errorIcon){
40322                     this.errorIcon.dom.qtip = '';
40323                     this.errorIcon.hide();
40324                     this.un('resize', this.alignErrorIcon, this);
40325                 }
40326                 break;
40327             default:
40328                 var t = Roo.getDom(this.msgTarget);
40329                 t.innerHTML = '';
40330                 t.style.display = 'none';
40331                 break;
40332         }
40333         this.fireEvent('valid', this);
40334     },
40335
40336     /**
40337      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
40338      * @return {Mixed} value The field value
40339      */
40340     getRawValue : function(){
40341         var v = this.el.getValue();
40342         
40343         return v;
40344     },
40345
40346     /**
40347      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
40348      * @return {Mixed} value The field value
40349      */
40350     getValue : function(){
40351         var v = this.el.getValue();
40352          
40353         return v;
40354     },
40355
40356     /**
40357      * Sets the underlying DOM field's value directly, bypassing validation.  To set the value with validation see {@link #setValue}.
40358      * @param {Mixed} value The value to set
40359      */
40360     setRawValue : function(v){
40361         return this.el.dom.value = (v === null || v === undefined ? '' : v);
40362     },
40363
40364     /**
40365      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
40366      * @param {Mixed} value The value to set
40367      */
40368     setValue : function(v){
40369         this.value = v;
40370         if(this.rendered){
40371             this.el.dom.value = (v === null || v === undefined ? '' : v);
40372              this.validate();
40373         }
40374     },
40375
40376     adjustSize : function(w, h){
40377         var s = Roo.form.Field.superclass.adjustSize.call(this, w, h);
40378         s.width = this.adjustWidth(this.el.dom.tagName, s.width);
40379         return s;
40380     },
40381
40382     adjustWidth : function(tag, w){
40383         tag = tag.toLowerCase();
40384         if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
40385             if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
40386                 if(tag == 'input'){
40387                     return w + 2;
40388                 }
40389                 if(tag == 'textarea'){
40390                     return w-2;
40391                 }
40392             }else if(Roo.isOpera){
40393                 if(tag == 'input'){
40394                     return w + 2;
40395                 }
40396                 if(tag == 'textarea'){
40397                     return w-2;
40398                 }
40399             }
40400         }
40401         return w;
40402     }
40403 });
40404
40405
40406 // anything other than normal should be considered experimental
40407 Roo.form.Field.msgFx = {
40408     normal : {
40409         show: function(msgEl, f){
40410             msgEl.setDisplayed('block');
40411         },
40412
40413         hide : function(msgEl, f){
40414             msgEl.setDisplayed(false).update('');
40415         }
40416     },
40417
40418     slide : {
40419         show: function(msgEl, f){
40420             msgEl.slideIn('t', {stopFx:true});
40421         },
40422
40423         hide : function(msgEl, f){
40424             msgEl.slideOut('t', {stopFx:true,useDisplay:true});
40425         }
40426     },
40427
40428     slideRight : {
40429         show: function(msgEl, f){
40430             msgEl.fixDisplay();
40431             msgEl.alignTo(f.el, 'tl-tr');
40432             msgEl.slideIn('l', {stopFx:true});
40433         },
40434
40435         hide : function(msgEl, f){
40436             msgEl.slideOut('l', {stopFx:true,useDisplay:true});
40437         }
40438     }
40439 };/*
40440  * Based on:
40441  * Ext JS Library 1.1.1
40442  * Copyright(c) 2006-2007, Ext JS, LLC.
40443  *
40444  * Originally Released Under LGPL - original licence link has changed is not relivant.
40445  *
40446  * Fork - LGPL
40447  * <script type="text/javascript">
40448  */
40449  
40450
40451 /**
40452  * @class Roo.form.TextField
40453  * @extends Roo.form.Field
40454  * Basic text field.  Can be used as a direct replacement for traditional text inputs, or as the base
40455  * class for more sophisticated input controls (like {@link Roo.form.TextArea} and {@link Roo.form.ComboBox}).
40456  * @constructor
40457  * Creates a new TextField
40458  * @param {Object} config Configuration options
40459  */
40460 Roo.form.TextField = function(config){
40461     Roo.form.TextField.superclass.constructor.call(this, config);
40462     this.addEvents({
40463         /**
40464          * @event autosize
40465          * Fires when the autosize function is triggered.  The field may or may not have actually changed size
40466          * according to the default logic, but this event provides a hook for the developer to apply additional
40467          * logic at runtime to resize the field if needed.
40468              * @param {Roo.form.Field} this This text field
40469              * @param {Number} width The new field width
40470              */
40471         autosize : true
40472     });
40473 };
40474
40475 Roo.extend(Roo.form.TextField, Roo.form.Field,  {
40476     /**
40477      * @cfg {Boolean} grow True if this field should automatically grow and shrink to its content
40478      */
40479     grow : false,
40480     /**
40481      * @cfg {Number} growMin The minimum width to allow when grow = true (defaults to 30)
40482      */
40483     growMin : 30,
40484     /**
40485      * @cfg {Number} growMax The maximum width to allow when grow = true (defaults to 800)
40486      */
40487     growMax : 800,
40488     /**
40489      * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
40490      */
40491     vtype : null,
40492     /**
40493      * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
40494      */
40495     maskRe : null,
40496     /**
40497      * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
40498      */
40499     disableKeyFilter : false,
40500     /**
40501      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
40502      */
40503     allowBlank : true,
40504     /**
40505      * @cfg {Number} minLength Minimum input field length required (defaults to 0)
40506      */
40507     minLength : 0,
40508     /**
40509      * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
40510      */
40511     maxLength : Number.MAX_VALUE,
40512     /**
40513      * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
40514      */
40515     minLengthText : "The minimum length for this field is {0}",
40516     /**
40517      * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
40518      */
40519     maxLengthText : "The maximum length for this field is {0}",
40520     /**
40521      * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
40522      */
40523     selectOnFocus : false,
40524     /**
40525      * @cfg {Boolean} allowLeadingSpace True to prevent the stripping of leading white space 
40526      */    
40527     allowLeadingSpace : false,
40528     /**
40529      * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
40530      */
40531     blankText : "This field is required",
40532     /**
40533      * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
40534      * If available, this function will be called only after the basic validators all return true, and will be passed the
40535      * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
40536      */
40537     validator : null,
40538     /**
40539      * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
40540      * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
40541      * current field value.  If the test fails, the field will be marked invalid using {@link #regexText}.
40542      */
40543     regex : null,
40544     /**
40545      * @cfg {String} regexText The error text to display if {@link #regex} is used and the test fails during validation (defaults to "")
40546      */
40547     regexText : "",
40548     /**
40549      * @cfg {String} emptyText The default text to display in an empty field - placeholder... (defaults to null).
40550      */
40551     emptyText : null,
40552    
40553
40554     // private
40555     initEvents : function()
40556     {
40557         if (this.emptyText) {
40558             this.el.attr('placeholder', this.emptyText);
40559         }
40560         
40561         Roo.form.TextField.superclass.initEvents.call(this);
40562         if(this.validationEvent == 'keyup'){
40563             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
40564             this.el.on('keyup', this.filterValidation, this);
40565         }
40566         else if(this.validationEvent !== false){
40567             this.el.on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
40568         }
40569         
40570         if(this.selectOnFocus){
40571             this.on("focus", this.preFocus, this);
40572         }
40573         if (!this.allowLeadingSpace) {
40574             this.on('blur', this.cleanLeadingSpace, this);
40575         }
40576         
40577         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
40578             this.el.on("keypress", this.filterKeys, this);
40579         }
40580         if(this.grow){
40581             this.el.on("keyup", this.onKeyUp,  this, {buffer:50});
40582             this.el.on("click", this.autoSize,  this);
40583         }
40584         if(this.el.is('input[type=password]') && Roo.isSafari){
40585             this.el.on('keydown', this.SafariOnKeyDown, this);
40586         }
40587     },
40588
40589     processValue : function(value){
40590         if(this.stripCharsRe){
40591             var newValue = value.replace(this.stripCharsRe, '');
40592             if(newValue !== value){
40593                 this.setRawValue(newValue);
40594                 return newValue;
40595             }
40596         }
40597         return value;
40598     },
40599
40600     filterValidation : function(e){
40601         if(!e.isNavKeyPress()){
40602             this.validationTask.delay(this.validationDelay);
40603         }
40604     },
40605
40606     // private
40607     onKeyUp : function(e){
40608         if(!e.isNavKeyPress()){
40609             this.autoSize();
40610         }
40611     },
40612     // private - clean the leading white space
40613     cleanLeadingSpace : function(e)
40614     {
40615         if ( this.inputType == 'file') {
40616             return;
40617         }
40618         
40619         this.setValue((this.getValue() + '').replace(/^\s+/,''));
40620     },
40621     /**
40622      * Resets the current field value to the originally-loaded value and clears any validation messages.
40623      *  
40624      */
40625     reset : function(){
40626         Roo.form.TextField.superclass.reset.call(this);
40627        
40628     }, 
40629     // private
40630     preFocus : function(){
40631         
40632         if(this.selectOnFocus){
40633             this.el.dom.select();
40634         }
40635     },
40636
40637     
40638     // private
40639     filterKeys : function(e){
40640         var k = e.getKey();
40641         if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
40642             return;
40643         }
40644         var c = e.getCharCode(), cc = String.fromCharCode(c);
40645         if(Roo.isIE && (e.isSpecialKey() || !cc)){
40646             return;
40647         }
40648         if(!this.maskRe.test(cc)){
40649             e.stopEvent();
40650         }
40651     },
40652
40653     setValue : function(v){
40654         
40655         Roo.form.TextField.superclass.setValue.apply(this, arguments);
40656         
40657         this.autoSize();
40658     },
40659
40660     /**
40661      * Validates a value according to the field's validation rules and marks the field as invalid
40662      * if the validation fails
40663      * @param {Mixed} value The value to validate
40664      * @return {Boolean} True if the value is valid, else false
40665      */
40666     validateValue : function(value){
40667         if(value.length < 1)  { // if it's blank
40668              if(this.allowBlank){
40669                 this.clearInvalid();
40670                 return true;
40671              }else{
40672                 this.markInvalid(this.blankText);
40673                 return false;
40674              }
40675         }
40676         if(value.length < this.minLength){
40677             this.markInvalid(String.format(this.minLengthText, this.minLength));
40678             return false;
40679         }
40680         if(value.length > this.maxLength){
40681             this.markInvalid(String.format(this.maxLengthText, this.maxLength));
40682             return false;
40683         }
40684         if(this.vtype){
40685             var vt = Roo.form.VTypes;
40686             if(!vt[this.vtype](value, this)){
40687                 this.markInvalid(this.vtypeText || vt[this.vtype +'Text']);
40688                 return false;
40689             }
40690         }
40691         if(typeof this.validator == "function"){
40692             var msg = this.validator(value);
40693             if(msg !== true){
40694                 this.markInvalid(msg);
40695                 return false;
40696             }
40697         }
40698         if(this.regex && !this.regex.test(value)){
40699             this.markInvalid(this.regexText);
40700             return false;
40701         }
40702         return true;
40703     },
40704
40705     /**
40706      * Selects text in this field
40707      * @param {Number} start (optional) The index where the selection should start (defaults to 0)
40708      * @param {Number} end (optional) The index where the selection should end (defaults to the text length)
40709      */
40710     selectText : function(start, end){
40711         var v = this.getRawValue();
40712         if(v.length > 0){
40713             start = start === undefined ? 0 : start;
40714             end = end === undefined ? v.length : end;
40715             var d = this.el.dom;
40716             if(d.setSelectionRange){
40717                 d.setSelectionRange(start, end);
40718             }else if(d.createTextRange){
40719                 var range = d.createTextRange();
40720                 range.moveStart("character", start);
40721                 range.moveEnd("character", v.length-end);
40722                 range.select();
40723             }
40724         }
40725     },
40726
40727     /**
40728      * Automatically grows the field to accomodate the width of the text up to the maximum field width allowed.
40729      * This only takes effect if grow = true, and fires the autosize event.
40730      */
40731     autoSize : function(){
40732         if(!this.grow || !this.rendered){
40733             return;
40734         }
40735         if(!this.metrics){
40736             this.metrics = Roo.util.TextMetrics.createInstance(this.el);
40737         }
40738         var el = this.el;
40739         var v = el.dom.value;
40740         var d = document.createElement('div');
40741         d.appendChild(document.createTextNode(v));
40742         v = d.innerHTML;
40743         d = null;
40744         v += "&#160;";
40745         var w = Math.min(this.growMax, Math.max(this.metrics.getWidth(v) + /* add extra padding */ 10, this.growMin));
40746         this.el.setWidth(w);
40747         this.fireEvent("autosize", this, w);
40748     },
40749     
40750     // private
40751     SafariOnKeyDown : function(event)
40752     {
40753         // this is a workaround for a password hang bug on chrome/ webkit.
40754         
40755         var isSelectAll = false;
40756         
40757         if(this.el.dom.selectionEnd > 0){
40758             isSelectAll = (this.el.dom.selectionEnd - this.el.dom.selectionStart - this.getValue().length == 0) ? true : false;
40759         }
40760         if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
40761             event.preventDefault();
40762             this.setValue('');
40763             return;
40764         }
40765         
40766         if(isSelectAll && event.getCharCode() > 31){ // backspace and delete key
40767             
40768             event.preventDefault();
40769             // this is very hacky as keydown always get's upper case.
40770             
40771             var cc = String.fromCharCode(event.getCharCode());
40772             
40773             
40774             this.setValue( event.shiftKey ?  cc : cc.toLowerCase());
40775             
40776         }
40777         
40778         
40779     }
40780 });/*
40781  * Based on:
40782  * Ext JS Library 1.1.1
40783  * Copyright(c) 2006-2007, Ext JS, LLC.
40784  *
40785  * Originally Released Under LGPL - original licence link has changed is not relivant.
40786  *
40787  * Fork - LGPL
40788  * <script type="text/javascript">
40789  */
40790  
40791 /**
40792  * @class Roo.form.Hidden
40793  * @extends Roo.form.TextField
40794  * Simple Hidden element used on forms 
40795  * 
40796  * usage: form.add(new Roo.form.HiddenField({ 'name' : 'test1' }));
40797  * 
40798  * @constructor
40799  * Creates a new Hidden form element.
40800  * @param {Object} config Configuration options
40801  */
40802
40803
40804
40805 // easy hidden field...
40806 Roo.form.Hidden = function(config){
40807     Roo.form.Hidden.superclass.constructor.call(this, config);
40808 };
40809   
40810 Roo.extend(Roo.form.Hidden, Roo.form.TextField, {
40811     fieldLabel:      '',
40812     inputType:      'hidden',
40813     width:          50,
40814     allowBlank:     true,
40815     labelSeparator: '',
40816     hidden:         true,
40817     itemCls :       'x-form-item-display-none'
40818
40819
40820 });
40821
40822
40823 /*
40824  * Based on:
40825  * Ext JS Library 1.1.1
40826  * Copyright(c) 2006-2007, Ext JS, LLC.
40827  *
40828  * Originally Released Under LGPL - original licence link has changed is not relivant.
40829  *
40830  * Fork - LGPL
40831  * <script type="text/javascript">
40832  */
40833  
40834 /**
40835  * @class Roo.form.TriggerField
40836  * @extends Roo.form.TextField
40837  * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
40838  * The trigger has no default action, so you must assign a function to implement the trigger click handler by
40839  * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
40840  * for which you can provide a custom implementation.  For example:
40841  * <pre><code>
40842 var trigger = new Roo.form.TriggerField();
40843 trigger.onTriggerClick = myTriggerFn;
40844 trigger.applyTo('my-field');
40845 </code></pre>
40846  *
40847  * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
40848  * {@link Roo.form.DateField} and {@link Roo.form.ComboBox} are perfect examples of this.
40849  * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
40850  * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
40851  * @constructor
40852  * Create a new TriggerField.
40853  * @param {Object} config Configuration options (valid {@Roo.form.TextField} config options will also be applied
40854  * to the base TextField)
40855  */
40856 Roo.form.TriggerField = function(config){
40857     this.mimicing = false;
40858     Roo.form.TriggerField.superclass.constructor.call(this, config);
40859 };
40860
40861 Roo.extend(Roo.form.TriggerField, Roo.form.TextField,  {
40862     /**
40863      * @cfg {String} triggerClass A CSS class to apply to the trigger
40864      */
40865     /**
40866      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
40867      * {tag: "input", type: "text", size: "16", autocomplete: "off"})
40868      */
40869     defaultAutoCreate : {tag: "input", type: "text", size: "16", autocomplete: "new-password"},
40870     /**
40871      * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
40872      */
40873     hideTrigger:false,
40874
40875     /** @cfg {Boolean} grow @hide */
40876     /** @cfg {Number} growMin @hide */
40877     /** @cfg {Number} growMax @hide */
40878
40879     /**
40880      * @hide 
40881      * @method
40882      */
40883     autoSize: Roo.emptyFn,
40884     // private
40885     monitorTab : true,
40886     // private
40887     deferHeight : true,
40888
40889     
40890     actionMode : 'wrap',
40891     // private
40892     onResize : function(w, h){
40893         Roo.form.TriggerField.superclass.onResize.apply(this, arguments);
40894         if(typeof w == 'number'){
40895             var x = w - this.trigger.getWidth();
40896             this.el.setWidth(this.adjustWidth('input', x));
40897             this.trigger.setStyle('left', x+'px');
40898         }
40899     },
40900
40901     // private
40902     adjustSize : Roo.BoxComponent.prototype.adjustSize,
40903
40904     // private
40905     getResizeEl : function(){
40906         return this.wrap;
40907     },
40908
40909     // private
40910     getPositionEl : function(){
40911         return this.wrap;
40912     },
40913
40914     // private
40915     alignErrorIcon : function(){
40916         this.errorIcon.alignTo(this.wrap, 'tl-tr', [2, 0]);
40917     },
40918
40919     // private
40920     onRender : function(ct, position){
40921         Roo.form.TriggerField.superclass.onRender.call(this, ct, position);
40922         this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
40923         this.trigger = this.wrap.createChild(this.triggerConfig ||
40924                 {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.triggerClass});
40925         if(this.hideTrigger){
40926             this.trigger.setDisplayed(false);
40927         }
40928         this.initTrigger();
40929         if(!this.width){
40930             this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
40931         }
40932     },
40933
40934     // private
40935     initTrigger : function(){
40936         this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
40937         this.trigger.addClassOnOver('x-form-trigger-over');
40938         this.trigger.addClassOnClick('x-form-trigger-click');
40939     },
40940
40941     // private
40942     onDestroy : function(){
40943         if(this.trigger){
40944             this.trigger.removeAllListeners();
40945             this.trigger.remove();
40946         }
40947         if(this.wrap){
40948             this.wrap.remove();
40949         }
40950         Roo.form.TriggerField.superclass.onDestroy.call(this);
40951     },
40952
40953     // private
40954     onFocus : function(){
40955         Roo.form.TriggerField.superclass.onFocus.call(this);
40956         if(!this.mimicing){
40957             this.wrap.addClass('x-trigger-wrap-focus');
40958             this.mimicing = true;
40959             Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
40960             if(this.monitorTab){
40961                 this.el.on("keydown", this.checkTab, this);
40962             }
40963         }
40964     },
40965
40966     // private
40967     checkTab : function(e){
40968         if(e.getKey() == e.TAB){
40969             this.triggerBlur();
40970         }
40971     },
40972
40973     // private
40974     onBlur : function(){
40975         // do nothing
40976     },
40977
40978     // private
40979     mimicBlur : function(e, t){
40980         if(!this.wrap.contains(t) && this.validateBlur()){
40981             this.triggerBlur();
40982         }
40983     },
40984
40985     // private
40986     triggerBlur : function(){
40987         this.mimicing = false;
40988         Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
40989         if(this.monitorTab){
40990             this.el.un("keydown", this.checkTab, this);
40991         }
40992         this.wrap.removeClass('x-trigger-wrap-focus');
40993         Roo.form.TriggerField.superclass.onBlur.call(this);
40994     },
40995
40996     // private
40997     // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
40998     validateBlur : function(e, t){
40999         return true;
41000     },
41001
41002     // private
41003     onDisable : function(){
41004         Roo.form.TriggerField.superclass.onDisable.call(this);
41005         if(this.wrap){
41006             this.wrap.addClass('x-item-disabled');
41007         }
41008     },
41009
41010     // private
41011     onEnable : function(){
41012         Roo.form.TriggerField.superclass.onEnable.call(this);
41013         if(this.wrap){
41014             this.wrap.removeClass('x-item-disabled');
41015         }
41016     },
41017
41018     // private
41019     onShow : function(){
41020         var ae = this.getActionEl();
41021         
41022         if(ae){
41023             ae.dom.style.display = '';
41024             ae.dom.style.visibility = 'visible';
41025         }
41026     },
41027
41028     // private
41029     
41030     onHide : function(){
41031         var ae = this.getActionEl();
41032         ae.dom.style.display = 'none';
41033     },
41034
41035     /**
41036      * The function that should handle the trigger's click event.  This method does nothing by default until overridden
41037      * by an implementing function.
41038      * @method
41039      * @param {EventObject} e
41040      */
41041     onTriggerClick : Roo.emptyFn
41042 });
41043
41044 // TwinTriggerField is not a public class to be used directly.  It is meant as an abstract base class
41045 // to be extended by an implementing class.  For an example of implementing this class, see the custom
41046 // SearchField implementation here: http://extjs.com/deploy/ext/examples/form/custom.html
41047 Roo.form.TwinTriggerField = Roo.extend(Roo.form.TriggerField, {
41048     initComponent : function(){
41049         Roo.form.TwinTriggerField.superclass.initComponent.call(this);
41050
41051         this.triggerConfig = {
41052             tag:'span', cls:'x-form-twin-triggers', cn:[
41053             {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.trigger1Class},
41054             {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.trigger2Class}
41055         ]};
41056     },
41057
41058     getTrigger : function(index){
41059         return this.triggers[index];
41060     },
41061
41062     initTrigger : function(){
41063         var ts = this.trigger.select('.x-form-trigger', true);
41064         this.wrap.setStyle('overflow', 'hidden');
41065         var triggerField = this;
41066         ts.each(function(t, all, index){
41067             t.hide = function(){
41068                 var w = triggerField.wrap.getWidth();
41069                 this.dom.style.display = 'none';
41070                 triggerField.el.setWidth(w-triggerField.trigger.getWidth());
41071             };
41072             t.show = function(){
41073                 var w = triggerField.wrap.getWidth();
41074                 this.dom.style.display = '';
41075                 triggerField.el.setWidth(w-triggerField.trigger.getWidth());
41076             };
41077             var triggerIndex = 'Trigger'+(index+1);
41078
41079             if(this['hide'+triggerIndex]){
41080                 t.dom.style.display = 'none';
41081             }
41082             t.on("click", this['on'+triggerIndex+'Click'], this, {preventDefault:true});
41083             t.addClassOnOver('x-form-trigger-over');
41084             t.addClassOnClick('x-form-trigger-click');
41085         }, this);
41086         this.triggers = ts.elements;
41087     },
41088
41089     onTrigger1Click : Roo.emptyFn,
41090     onTrigger2Click : Roo.emptyFn
41091 });/*
41092  * Based on:
41093  * Ext JS Library 1.1.1
41094  * Copyright(c) 2006-2007, Ext JS, LLC.
41095  *
41096  * Originally Released Under LGPL - original licence link has changed is not relivant.
41097  *
41098  * Fork - LGPL
41099  * <script type="text/javascript">
41100  */
41101  
41102 /**
41103  * @class Roo.form.TextArea
41104  * @extends Roo.form.TextField
41105  * Multiline text field.  Can be used as a direct replacement for traditional textarea fields, plus adds
41106  * support for auto-sizing.
41107  * @constructor
41108  * Creates a new TextArea
41109  * @param {Object} config Configuration options
41110  */
41111 Roo.form.TextArea = function(config){
41112     Roo.form.TextArea.superclass.constructor.call(this, config);
41113     // these are provided exchanges for backwards compat
41114     // minHeight/maxHeight were replaced by growMin/growMax to be
41115     // compatible with TextField growing config values
41116     if(this.minHeight !== undefined){
41117         this.growMin = this.minHeight;
41118     }
41119     if(this.maxHeight !== undefined){
41120         this.growMax = this.maxHeight;
41121     }
41122 };
41123
41124 Roo.extend(Roo.form.TextArea, Roo.form.TextField,  {
41125     /**
41126      * @cfg {Number} growMin The minimum height to allow when grow = true (defaults to 60)
41127      */
41128     growMin : 60,
41129     /**
41130      * @cfg {Number} growMax The maximum height to allow when grow = true (defaults to 1000)
41131      */
41132     growMax: 1000,
41133     /**
41134      * @cfg {Boolean} preventScrollbars True to prevent scrollbars from appearing regardless of how much text is
41135      * in the field (equivalent to setting overflow: hidden, defaults to false)
41136      */
41137     preventScrollbars: false,
41138     /**
41139      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
41140      * {tag: "textarea", style: "width:300px;height:60px;", autocomplete: "off"})
41141      */
41142
41143     // private
41144     onRender : function(ct, position){
41145         if(!this.el){
41146             this.defaultAutoCreate = {
41147                 tag: "textarea",
41148                 style:"width:300px;height:60px;",
41149                 autocomplete: "new-password"
41150             };
41151         }
41152         Roo.form.TextArea.superclass.onRender.call(this, ct, position);
41153         if(this.grow){
41154             this.textSizeEl = Roo.DomHelper.append(document.body, {
41155                 tag: "pre", cls: "x-form-grow-sizer"
41156             });
41157             if(this.preventScrollbars){
41158                 this.el.setStyle("overflow", "hidden");
41159             }
41160             this.el.setHeight(this.growMin);
41161         }
41162     },
41163
41164     onDestroy : function(){
41165         if(this.textSizeEl){
41166             this.textSizeEl.parentNode.removeChild(this.textSizeEl);
41167         }
41168         Roo.form.TextArea.superclass.onDestroy.call(this);
41169     },
41170
41171     // private
41172     onKeyUp : function(e){
41173         if(!e.isNavKeyPress() || e.getKey() == e.ENTER){
41174             this.autoSize();
41175         }
41176     },
41177
41178     /**
41179      * Automatically grows the field to accomodate the height of the text up to the maximum field height allowed.
41180      * This only takes effect if grow = true, and fires the autosize event if the height changes.
41181      */
41182     autoSize : function(){
41183         if(!this.grow || !this.textSizeEl){
41184             return;
41185         }
41186         var el = this.el;
41187         var v = el.dom.value;
41188         var ts = this.textSizeEl;
41189
41190         ts.innerHTML = '';
41191         ts.appendChild(document.createTextNode(v));
41192         v = ts.innerHTML;
41193
41194         Roo.fly(ts).setWidth(this.el.getWidth());
41195         if(v.length < 1){
41196             v = "&#160;&#160;";
41197         }else{
41198             if(Roo.isIE){
41199                 v = v.replace(/\n/g, '<p>&#160;</p>');
41200             }
41201             v += "&#160;\n&#160;";
41202         }
41203         ts.innerHTML = v;
41204         var h = Math.min(this.growMax, Math.max(ts.offsetHeight, this.growMin));
41205         if(h != this.lastHeight){
41206             this.lastHeight = h;
41207             this.el.setHeight(h);
41208             this.fireEvent("autosize", this, h);
41209         }
41210     }
41211 });/*
41212  * Based on:
41213  * Ext JS Library 1.1.1
41214  * Copyright(c) 2006-2007, Ext JS, LLC.
41215  *
41216  * Originally Released Under LGPL - original licence link has changed is not relivant.
41217  *
41218  * Fork - LGPL
41219  * <script type="text/javascript">
41220  */
41221  
41222
41223 /**
41224  * @class Roo.form.NumberField
41225  * @extends Roo.form.TextField
41226  * Numeric text field that provides automatic keystroke filtering and numeric validation.
41227  * @constructor
41228  * Creates a new NumberField
41229  * @param {Object} config Configuration options
41230  */
41231 Roo.form.NumberField = function(config){
41232     Roo.form.NumberField.superclass.constructor.call(this, config);
41233 };
41234
41235 Roo.extend(Roo.form.NumberField, Roo.form.TextField,  {
41236     /**
41237      * @cfg {String} fieldClass The default CSS class for the field (defaults to "x-form-field x-form-num-field")
41238      */
41239     fieldClass: "x-form-field x-form-num-field",
41240     /**
41241      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
41242      */
41243     allowDecimals : true,
41244     /**
41245      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
41246      */
41247     decimalSeparator : ".",
41248     /**
41249      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
41250      */
41251     decimalPrecision : 2,
41252     /**
41253      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
41254      */
41255     allowNegative : true,
41256     /**
41257      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
41258      */
41259     minValue : Number.NEGATIVE_INFINITY,
41260     /**
41261      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
41262      */
41263     maxValue : Number.MAX_VALUE,
41264     /**
41265      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
41266      */
41267     minText : "The minimum value for this field is {0}",
41268     /**
41269      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
41270      */
41271     maxText : "The maximum value for this field is {0}",
41272     /**
41273      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
41274      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
41275      */
41276     nanText : "{0} is not a valid number",
41277
41278     // private
41279     initEvents : function(){
41280         Roo.form.NumberField.superclass.initEvents.call(this);
41281         var allowed = "0123456789";
41282         if(this.allowDecimals){
41283             allowed += this.decimalSeparator;
41284         }
41285         if(this.allowNegative){
41286             allowed += "-";
41287         }
41288         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
41289         var keyPress = function(e){
41290             var k = e.getKey();
41291             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
41292                 return;
41293             }
41294             var c = e.getCharCode();
41295             if(allowed.indexOf(String.fromCharCode(c)) === -1){
41296                 e.stopEvent();
41297             }
41298         };
41299         this.el.on("keypress", keyPress, this);
41300     },
41301
41302     // private
41303     validateValue : function(value){
41304         if(!Roo.form.NumberField.superclass.validateValue.call(this, value)){
41305             return false;
41306         }
41307         if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
41308              return true;
41309         }
41310         var num = this.parseValue(value);
41311         if(isNaN(num)){
41312             this.markInvalid(String.format(this.nanText, value));
41313             return false;
41314         }
41315         if(num < this.minValue){
41316             this.markInvalid(String.format(this.minText, this.minValue));
41317             return false;
41318         }
41319         if(num > this.maxValue){
41320             this.markInvalid(String.format(this.maxText, this.maxValue));
41321             return false;
41322         }
41323         return true;
41324     },
41325
41326     getValue : function(){
41327         return this.fixPrecision(this.parseValue(Roo.form.NumberField.superclass.getValue.call(this)));
41328     },
41329
41330     // private
41331     parseValue : function(value){
41332         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
41333         return isNaN(value) ? '' : value;
41334     },
41335
41336     // private
41337     fixPrecision : function(value){
41338         var nan = isNaN(value);
41339         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
41340             return nan ? '' : value;
41341         }
41342         return parseFloat(value).toFixed(this.decimalPrecision);
41343     },
41344
41345     setValue : function(v){
41346         v = this.fixPrecision(v);
41347         Roo.form.NumberField.superclass.setValue.call(this, String(v).replace(".", this.decimalSeparator));
41348     },
41349
41350     // private
41351     decimalPrecisionFcn : function(v){
41352         return Math.floor(v);
41353     },
41354
41355     beforeBlur : function(){
41356         var v = this.parseValue(this.getRawValue());
41357         if(v){
41358             this.setValue(v);
41359         }
41360     }
41361 });/*
41362  * Based on:
41363  * Ext JS Library 1.1.1
41364  * Copyright(c) 2006-2007, Ext JS, LLC.
41365  *
41366  * Originally Released Under LGPL - original licence link has changed is not relivant.
41367  *
41368  * Fork - LGPL
41369  * <script type="text/javascript">
41370  */
41371  
41372 /**
41373  * @class Roo.form.DateField
41374  * @extends Roo.form.TriggerField
41375  * Provides a date input field with a {@link Roo.DatePicker} dropdown and automatic date validation.
41376 * @constructor
41377 * Create a new DateField
41378 * @param {Object} config
41379  */
41380 Roo.form.DateField = function(config)
41381 {
41382     Roo.form.DateField.superclass.constructor.call(this, config);
41383     
41384       this.addEvents({
41385          
41386         /**
41387          * @event select
41388          * Fires when a date is selected
41389              * @param {Roo.form.DateField} combo This combo box
41390              * @param {Date} date The date selected
41391              */
41392         'select' : true
41393          
41394     });
41395     
41396     
41397     if(typeof this.minValue == "string") {
41398         this.minValue = this.parseDate(this.minValue);
41399     }
41400     if(typeof this.maxValue == "string") {
41401         this.maxValue = this.parseDate(this.maxValue);
41402     }
41403     this.ddMatch = null;
41404     if(this.disabledDates){
41405         var dd = this.disabledDates;
41406         var re = "(?:";
41407         for(var i = 0; i < dd.length; i++){
41408             re += dd[i];
41409             if(i != dd.length-1) {
41410                 re += "|";
41411             }
41412         }
41413         this.ddMatch = new RegExp(re + ")");
41414     }
41415 };
41416
41417 Roo.extend(Roo.form.DateField, Roo.form.TriggerField,  {
41418     /**
41419      * @cfg {String} format
41420      * The default date format string which can be overriden for localization support.  The format must be
41421      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
41422      */
41423     format : "m/d/y",
41424     /**
41425      * @cfg {String} altFormats
41426      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
41427      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
41428      */
41429     altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
41430     /**
41431      * @cfg {Array} disabledDays
41432      * An array of days to disable, 0 based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
41433      */
41434     disabledDays : null,
41435     /**
41436      * @cfg {String} disabledDaysText
41437      * The tooltip to display when the date falls on a disabled day (defaults to 'Disabled')
41438      */
41439     disabledDaysText : "Disabled",
41440     /**
41441      * @cfg {Array} disabledDates
41442      * An array of "dates" to disable, as strings. These strings will be used to build a dynamic regular
41443      * expression so they are very powerful. Some examples:
41444      * <ul>
41445      * <li>["03/08/2003", "09/16/2003"] would disable those exact dates</li>
41446      * <li>["03/08", "09/16"] would disable those days for every year</li>
41447      * <li>["^03/08"] would only match the beginning (useful if you are using short years)</li>
41448      * <li>["03/../2006"] would disable every day in March 2006</li>
41449      * <li>["^03"] would disable every day in every March</li>
41450      * </ul>
41451      * In order to support regular expressions, if you are using a date format that has "." in it, you will have to
41452      * escape the dot when restricting dates. For example: ["03\\.08\\.03"].
41453      */
41454     disabledDates : null,
41455     /**
41456      * @cfg {String} disabledDatesText
41457      * The tooltip text to display when the date falls on a disabled date (defaults to 'Disabled')
41458      */
41459     disabledDatesText : "Disabled",
41460     /**
41461      * @cfg {Date/String} minValue
41462      * The minimum allowed date. Can be either a Javascript date object or a string date in a
41463      * valid format (defaults to null).
41464      */
41465     minValue : null,
41466     /**
41467      * @cfg {Date/String} maxValue
41468      * The maximum allowed date. Can be either a Javascript date object or a string date in a
41469      * valid format (defaults to null).
41470      */
41471     maxValue : null,
41472     /**
41473      * @cfg {String} minText
41474      * The error text to display when the date in the cell is before minValue (defaults to
41475      * 'The date in this field must be after {minValue}').
41476      */
41477     minText : "The date in this field must be equal to or after {0}",
41478     /**
41479      * @cfg {String} maxText
41480      * The error text to display when the date in the cell is after maxValue (defaults to
41481      * 'The date in this field must be before {maxValue}').
41482      */
41483     maxText : "The date in this field must be equal to or before {0}",
41484     /**
41485      * @cfg {String} invalidText
41486      * The error text to display when the date in the field is invalid (defaults to
41487      * '{value} is not a valid date - it must be in the format {format}').
41488      */
41489     invalidText : "{0} is not a valid date - it must be in the format {1}",
41490     /**
41491      * @cfg {String} triggerClass
41492      * An additional CSS class used to style the trigger button.  The trigger will always get the
41493      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-date-trigger'
41494      * which displays a calendar icon).
41495      */
41496     triggerClass : 'x-form-date-trigger',
41497     
41498
41499     /**
41500      * @cfg {Boolean} useIso
41501      * if enabled, then the date field will use a hidden field to store the 
41502      * real value as iso formated date. default (false)
41503      */ 
41504     useIso : false,
41505     /**
41506      * @cfg {String/Object} autoCreate
41507      * A DomHelper element spec, or true for a default element spec (defaults to
41508      * {tag: "input", type: "text", size: "10", autocomplete: "off"})
41509      */ 
41510     // private
41511     defaultAutoCreate : {tag: "input", type: "text", size: "10", autocomplete: "off"},
41512     
41513     // private
41514     hiddenField: false,
41515     
41516     onRender : function(ct, position)
41517     {
41518         Roo.form.DateField.superclass.onRender.call(this, ct, position);
41519         if (this.useIso) {
41520             //this.el.dom.removeAttribute('name'); 
41521             Roo.log("Changing name?");
41522             this.el.dom.setAttribute('name', this.name + '____hidden___' ); 
41523             this.hiddenField = this.el.insertSibling({ tag:'input', type:'hidden', name: this.name },
41524                     'before', true);
41525             this.hiddenField.value = this.value ? this.formatDate(this.value, 'Y-m-d') : '';
41526             // prevent input submission
41527             this.hiddenName = this.name;
41528         }
41529             
41530             
41531     },
41532     
41533     // private
41534     validateValue : function(value)
41535     {
41536         value = this.formatDate(value);
41537         if(!Roo.form.DateField.superclass.validateValue.call(this, value)){
41538             Roo.log('super failed');
41539             return false;
41540         }
41541         if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
41542              return true;
41543         }
41544         var svalue = value;
41545         value = this.parseDate(value);
41546         if(!value){
41547             Roo.log('parse date failed' + svalue);
41548             this.markInvalid(String.format(this.invalidText, svalue, this.format));
41549             return false;
41550         }
41551         var time = value.getTime();
41552         if(this.minValue && time < this.minValue.getTime()){
41553             this.markInvalid(String.format(this.minText, this.formatDate(this.minValue)));
41554             return false;
41555         }
41556         if(this.maxValue && time > this.maxValue.getTime()){
41557             this.markInvalid(String.format(this.maxText, this.formatDate(this.maxValue)));
41558             return false;
41559         }
41560         if(this.disabledDays){
41561             var day = value.getDay();
41562             for(var i = 0; i < this.disabledDays.length; i++) {
41563                 if(day === this.disabledDays[i]){
41564                     this.markInvalid(this.disabledDaysText);
41565                     return false;
41566                 }
41567             }
41568         }
41569         var fvalue = this.formatDate(value);
41570         if(this.ddMatch && this.ddMatch.test(fvalue)){
41571             this.markInvalid(String.format(this.disabledDatesText, fvalue));
41572             return false;
41573         }
41574         return true;
41575     },
41576
41577     // private
41578     // Provides logic to override the default TriggerField.validateBlur which just returns true
41579     validateBlur : function(){
41580         return !this.menu || !this.menu.isVisible();
41581     },
41582     
41583     getName: function()
41584     {
41585         // returns hidden if it's set..
41586         if (!this.rendered) {return ''};
41587         return !this.hiddenName && this.el.dom.name  ? this.el.dom.name : (this.hiddenName || '');
41588         
41589     },
41590
41591     /**
41592      * Returns the current date value of the date field.
41593      * @return {Date} The date value
41594      */
41595     getValue : function(){
41596         
41597         return  this.hiddenField ?
41598                 this.hiddenField.value :
41599                 this.parseDate(Roo.form.DateField.superclass.getValue.call(this)) || "";
41600     },
41601
41602     /**
41603      * Sets the value of the date field.  You can pass a date object or any string that can be parsed into a valid
41604      * date, using DateField.format as the date format, according to the same rules as {@link Date#parseDate}
41605      * (the default format used is "m/d/y").
41606      * <br />Usage:
41607      * <pre><code>
41608 //All of these calls set the same date value (May 4, 2006)
41609
41610 //Pass a date object:
41611 var dt = new Date('5/4/06');
41612 dateField.setValue(dt);
41613
41614 //Pass a date string (default format):
41615 dateField.setValue('5/4/06');
41616
41617 //Pass a date string (custom format):
41618 dateField.format = 'Y-m-d';
41619 dateField.setValue('2006-5-4');
41620 </code></pre>
41621      * @param {String/Date} date The date or valid date string
41622      */
41623     setValue : function(date){
41624         if (this.hiddenField) {
41625             this.hiddenField.value = this.formatDate(this.parseDate(date), 'Y-m-d');
41626         }
41627         Roo.form.DateField.superclass.setValue.call(this, this.formatDate(this.parseDate(date)));
41628         // make sure the value field is always stored as a date..
41629         this.value = this.parseDate(date);
41630         
41631         
41632     },
41633
41634     // private
41635     parseDate : function(value){
41636         if(!value || value instanceof Date){
41637             return value;
41638         }
41639         var v = Date.parseDate(value, this.format);
41640          if (!v && this.useIso) {
41641             v = Date.parseDate(value, 'Y-m-d');
41642         }
41643         if(!v && this.altFormats){
41644             if(!this.altFormatsArray){
41645                 this.altFormatsArray = this.altFormats.split("|");
41646             }
41647             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
41648                 v = Date.parseDate(value, this.altFormatsArray[i]);
41649             }
41650         }
41651         return v;
41652     },
41653
41654     // private
41655     formatDate : function(date, fmt){
41656         return (!date || !(date instanceof Date)) ?
41657                date : date.dateFormat(fmt || this.format);
41658     },
41659
41660     // private
41661     menuListeners : {
41662         select: function(m, d){
41663             
41664             this.setValue(d);
41665             this.fireEvent('select', this, d);
41666         },
41667         show : function(){ // retain focus styling
41668             this.onFocus();
41669         },
41670         hide : function(){
41671             this.focus.defer(10, this);
41672             var ml = this.menuListeners;
41673             this.menu.un("select", ml.select,  this);
41674             this.menu.un("show", ml.show,  this);
41675             this.menu.un("hide", ml.hide,  this);
41676         }
41677     },
41678
41679     // private
41680     // Implements the default empty TriggerField.onTriggerClick function to display the DatePicker
41681     onTriggerClick : function(){
41682         if(this.disabled){
41683             return;
41684         }
41685         if(this.menu == null){
41686             this.menu = new Roo.menu.DateMenu();
41687         }
41688         Roo.apply(this.menu.picker,  {
41689             showClear: this.allowBlank,
41690             minDate : this.minValue,
41691             maxDate : this.maxValue,
41692             disabledDatesRE : this.ddMatch,
41693             disabledDatesText : this.disabledDatesText,
41694             disabledDays : this.disabledDays,
41695             disabledDaysText : this.disabledDaysText,
41696             format : this.useIso ? 'Y-m-d' : this.format,
41697             minText : String.format(this.minText, this.formatDate(this.minValue)),
41698             maxText : String.format(this.maxText, this.formatDate(this.maxValue))
41699         });
41700         this.menu.on(Roo.apply({}, this.menuListeners, {
41701             scope:this
41702         }));
41703         this.menu.picker.setValue(this.getValue() || new Date());
41704         this.menu.show(this.el, "tl-bl?");
41705     },
41706
41707     beforeBlur : function(){
41708         var v = this.parseDate(this.getRawValue());
41709         if(v){
41710             this.setValue(v);
41711         }
41712     },
41713
41714     /*@
41715      * overide
41716      * 
41717      */
41718     isDirty : function() {
41719         if(this.disabled) {
41720             return false;
41721         }
41722         
41723         if(typeof(this.startValue) === 'undefined'){
41724             return false;
41725         }
41726         
41727         return String(this.getValue()) !== String(this.startValue);
41728         
41729     },
41730     // @overide
41731     cleanLeadingSpace : function(e)
41732     {
41733        return;
41734     }
41735     
41736 });/*
41737  * Based on:
41738  * Ext JS Library 1.1.1
41739  * Copyright(c) 2006-2007, Ext JS, LLC.
41740  *
41741  * Originally Released Under LGPL - original licence link has changed is not relivant.
41742  *
41743  * Fork - LGPL
41744  * <script type="text/javascript">
41745  */
41746  
41747 /**
41748  * @class Roo.form.MonthField
41749  * @extends Roo.form.TriggerField
41750  * Provides a date input field with a {@link Roo.DatePicker} dropdown and automatic date validation.
41751 * @constructor
41752 * Create a new MonthField
41753 * @param {Object} config
41754  */
41755 Roo.form.MonthField = function(config){
41756     
41757     Roo.form.MonthField.superclass.constructor.call(this, config);
41758     
41759       this.addEvents({
41760          
41761         /**
41762          * @event select
41763          * Fires when a date is selected
41764              * @param {Roo.form.MonthFieeld} combo This combo box
41765              * @param {Date} date The date selected
41766              */
41767         'select' : true
41768          
41769     });
41770     
41771     
41772     if(typeof this.minValue == "string") {
41773         this.minValue = this.parseDate(this.minValue);
41774     }
41775     if(typeof this.maxValue == "string") {
41776         this.maxValue = this.parseDate(this.maxValue);
41777     }
41778     this.ddMatch = null;
41779     if(this.disabledDates){
41780         var dd = this.disabledDates;
41781         var re = "(?:";
41782         for(var i = 0; i < dd.length; i++){
41783             re += dd[i];
41784             if(i != dd.length-1) {
41785                 re += "|";
41786             }
41787         }
41788         this.ddMatch = new RegExp(re + ")");
41789     }
41790 };
41791
41792 Roo.extend(Roo.form.MonthField, Roo.form.TriggerField,  {
41793     /**
41794      * @cfg {String} format
41795      * The default date format string which can be overriden for localization support.  The format must be
41796      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
41797      */
41798     format : "M Y",
41799     /**
41800      * @cfg {String} altFormats
41801      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
41802      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
41803      */
41804     altFormats : "M Y|m/Y|m-y|m-Y|my|mY",
41805     /**
41806      * @cfg {Array} disabledDays
41807      * An array of days to disable, 0 based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
41808      */
41809     disabledDays : [0,1,2,3,4,5,6],
41810     /**
41811      * @cfg {String} disabledDaysText
41812      * The tooltip to display when the date falls on a disabled day (defaults to 'Disabled')
41813      */
41814     disabledDaysText : "Disabled",
41815     /**
41816      * @cfg {Array} disabledDates
41817      * An array of "dates" to disable, as strings. These strings will be used to build a dynamic regular
41818      * expression so they are very powerful. Some examples:
41819      * <ul>
41820      * <li>["03/08/2003", "09/16/2003"] would disable those exact dates</li>
41821      * <li>["03/08", "09/16"] would disable those days for every year</li>
41822      * <li>["^03/08"] would only match the beginning (useful if you are using short years)</li>
41823      * <li>["03/../2006"] would disable every day in March 2006</li>
41824      * <li>["^03"] would disable every day in every March</li>
41825      * </ul>
41826      * In order to support regular expressions, if you are using a date format that has "." in it, you will have to
41827      * escape the dot when restricting dates. For example: ["03\\.08\\.03"].
41828      */
41829     disabledDates : null,
41830     /**
41831      * @cfg {String} disabledDatesText
41832      * The tooltip text to display when the date falls on a disabled date (defaults to 'Disabled')
41833      */
41834     disabledDatesText : "Disabled",
41835     /**
41836      * @cfg {Date/String} minValue
41837      * The minimum allowed date. Can be either a Javascript date object or a string date in a
41838      * valid format (defaults to null).
41839      */
41840     minValue : null,
41841     /**
41842      * @cfg {Date/String} maxValue
41843      * The maximum allowed date. Can be either a Javascript date object or a string date in a
41844      * valid format (defaults to null).
41845      */
41846     maxValue : null,
41847     /**
41848      * @cfg {String} minText
41849      * The error text to display when the date in the cell is before minValue (defaults to
41850      * 'The date in this field must be after {minValue}').
41851      */
41852     minText : "The date in this field must be equal to or after {0}",
41853     /**
41854      * @cfg {String} maxTextf
41855      * The error text to display when the date in the cell is after maxValue (defaults to
41856      * 'The date in this field must be before {maxValue}').
41857      */
41858     maxText : "The date in this field must be equal to or before {0}",
41859     /**
41860      * @cfg {String} invalidText
41861      * The error text to display when the date in the field is invalid (defaults to
41862      * '{value} is not a valid date - it must be in the format {format}').
41863      */
41864     invalidText : "{0} is not a valid date - it must be in the format {1}",
41865     /**
41866      * @cfg {String} triggerClass
41867      * An additional CSS class used to style the trigger button.  The trigger will always get the
41868      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-date-trigger'
41869      * which displays a calendar icon).
41870      */
41871     triggerClass : 'x-form-date-trigger',
41872     
41873
41874     /**
41875      * @cfg {Boolean} useIso
41876      * if enabled, then the date field will use a hidden field to store the 
41877      * real value as iso formated date. default (true)
41878      */ 
41879     useIso : true,
41880     /**
41881      * @cfg {String/Object} autoCreate
41882      * A DomHelper element spec, or true for a default element spec (defaults to
41883      * {tag: "input", type: "text", size: "10", autocomplete: "off"})
41884      */ 
41885     // private
41886     defaultAutoCreate : {tag: "input", type: "text", size: "10", autocomplete: "new-password"},
41887     
41888     // private
41889     hiddenField: false,
41890     
41891     hideMonthPicker : false,
41892     
41893     onRender : function(ct, position)
41894     {
41895         Roo.form.MonthField.superclass.onRender.call(this, ct, position);
41896         if (this.useIso) {
41897             this.el.dom.removeAttribute('name'); 
41898             this.hiddenField = this.el.insertSibling({ tag:'input', type:'hidden', name: this.name },
41899                     'before', true);
41900             this.hiddenField.value = this.value ? this.formatDate(this.value, 'Y-m-d') : '';
41901             // prevent input submission
41902             this.hiddenName = this.name;
41903         }
41904             
41905             
41906     },
41907     
41908     // private
41909     validateValue : function(value)
41910     {
41911         value = this.formatDate(value);
41912         if(!Roo.form.MonthField.superclass.validateValue.call(this, value)){
41913             return false;
41914         }
41915         if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
41916              return true;
41917         }
41918         var svalue = value;
41919         value = this.parseDate(value);
41920         if(!value){
41921             this.markInvalid(String.format(this.invalidText, svalue, this.format));
41922             return false;
41923         }
41924         var time = value.getTime();
41925         if(this.minValue && time < this.minValue.getTime()){
41926             this.markInvalid(String.format(this.minText, this.formatDate(this.minValue)));
41927             return false;
41928         }
41929         if(this.maxValue && time > this.maxValue.getTime()){
41930             this.markInvalid(String.format(this.maxText, this.formatDate(this.maxValue)));
41931             return false;
41932         }
41933         /*if(this.disabledDays){
41934             var day = value.getDay();
41935             for(var i = 0; i < this.disabledDays.length; i++) {
41936                 if(day === this.disabledDays[i]){
41937                     this.markInvalid(this.disabledDaysText);
41938                     return false;
41939                 }
41940             }
41941         }
41942         */
41943         var fvalue = this.formatDate(value);
41944         /*if(this.ddMatch && this.ddMatch.test(fvalue)){
41945             this.markInvalid(String.format(this.disabledDatesText, fvalue));
41946             return false;
41947         }
41948         */
41949         return true;
41950     },
41951
41952     // private
41953     // Provides logic to override the default TriggerField.validateBlur which just returns true
41954     validateBlur : function(){
41955         return !this.menu || !this.menu.isVisible();
41956     },
41957
41958     /**
41959      * Returns the current date value of the date field.
41960      * @return {Date} The date value
41961      */
41962     getValue : function(){
41963         
41964         
41965         
41966         return  this.hiddenField ?
41967                 this.hiddenField.value :
41968                 this.parseDate(Roo.form.MonthField.superclass.getValue.call(this)) || "";
41969     },
41970
41971     /**
41972      * Sets the value of the date field.  You can pass a date object or any string that can be parsed into a valid
41973      * date, using MonthField.format as the date format, according to the same rules as {@link Date#parseDate}
41974      * (the default format used is "m/d/y").
41975      * <br />Usage:
41976      * <pre><code>
41977 //All of these calls set the same date value (May 4, 2006)
41978
41979 //Pass a date object:
41980 var dt = new Date('5/4/06');
41981 monthField.setValue(dt);
41982
41983 //Pass a date string (default format):
41984 monthField.setValue('5/4/06');
41985
41986 //Pass a date string (custom format):
41987 monthField.format = 'Y-m-d';
41988 monthField.setValue('2006-5-4');
41989 </code></pre>
41990      * @param {String/Date} date The date or valid date string
41991      */
41992     setValue : function(date){
41993         Roo.log('month setValue' + date);
41994         // can only be first of month..
41995         
41996         var val = this.parseDate(date);
41997         
41998         if (this.hiddenField) {
41999             this.hiddenField.value = this.formatDate(this.parseDate(date), 'Y-m-d');
42000         }
42001         Roo.form.MonthField.superclass.setValue.call(this, this.formatDate(this.parseDate(date)));
42002         this.value = this.parseDate(date);
42003     },
42004
42005     // private
42006     parseDate : function(value){
42007         if(!value || value instanceof Date){
42008             value = value ? Date.parseDate(value.format('Y-m') + '-01', 'Y-m-d') : null;
42009             return value;
42010         }
42011         var v = Date.parseDate(value, this.format);
42012         if (!v && this.useIso) {
42013             v = Date.parseDate(value, 'Y-m-d');
42014         }
42015         if (v) {
42016             // 
42017             v = Date.parseDate(v.format('Y-m') +'-01', 'Y-m-d');
42018         }
42019         
42020         
42021         if(!v && this.altFormats){
42022             if(!this.altFormatsArray){
42023                 this.altFormatsArray = this.altFormats.split("|");
42024             }
42025             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
42026                 v = Date.parseDate(value, this.altFormatsArray[i]);
42027             }
42028         }
42029         return v;
42030     },
42031
42032     // private
42033     formatDate : function(date, fmt){
42034         return (!date || !(date instanceof Date)) ?
42035                date : date.dateFormat(fmt || this.format);
42036     },
42037
42038     // private
42039     menuListeners : {
42040         select: function(m, d){
42041             this.setValue(d);
42042             this.fireEvent('select', this, d);
42043         },
42044         show : function(){ // retain focus styling
42045             this.onFocus();
42046         },
42047         hide : function(){
42048             this.focus.defer(10, this);
42049             var ml = this.menuListeners;
42050             this.menu.un("select", ml.select,  this);
42051             this.menu.un("show", ml.show,  this);
42052             this.menu.un("hide", ml.hide,  this);
42053         }
42054     },
42055     // private
42056     // Implements the default empty TriggerField.onTriggerClick function to display the DatePicker
42057     onTriggerClick : function(){
42058         if(this.disabled){
42059             return;
42060         }
42061         if(this.menu == null){
42062             this.menu = new Roo.menu.DateMenu();
42063            
42064         }
42065         
42066         Roo.apply(this.menu.picker,  {
42067             
42068             showClear: this.allowBlank,
42069             minDate : this.minValue,
42070             maxDate : this.maxValue,
42071             disabledDatesRE : this.ddMatch,
42072             disabledDatesText : this.disabledDatesText,
42073             
42074             format : this.useIso ? 'Y-m-d' : this.format,
42075             minText : String.format(this.minText, this.formatDate(this.minValue)),
42076             maxText : String.format(this.maxText, this.formatDate(this.maxValue))
42077             
42078         });
42079          this.menu.on(Roo.apply({}, this.menuListeners, {
42080             scope:this
42081         }));
42082        
42083         
42084         var m = this.menu;
42085         var p = m.picker;
42086         
42087         // hide month picker get's called when we called by 'before hide';
42088         
42089         var ignorehide = true;
42090         p.hideMonthPicker  = function(disableAnim){
42091             if (ignorehide) {
42092                 return;
42093             }
42094              if(this.monthPicker){
42095                 Roo.log("hideMonthPicker called");
42096                 if(disableAnim === true){
42097                     this.monthPicker.hide();
42098                 }else{
42099                     this.monthPicker.slideOut('t', {duration:.2});
42100                     p.setValue(new Date(m.picker.mpSelYear, m.picker.mpSelMonth, 1));
42101                     p.fireEvent("select", this, this.value);
42102                     m.hide();
42103                 }
42104             }
42105         }
42106         
42107         Roo.log('picker set value');
42108         Roo.log(this.getValue());
42109         p.setValue(this.getValue() ? this.parseDate(this.getValue()) : new Date());
42110         m.show(this.el, 'tl-bl?');
42111         ignorehide  = false;
42112         // this will trigger hideMonthPicker..
42113         
42114         
42115         // hidden the day picker
42116         Roo.select('.x-date-picker table', true).first().dom.style.visibility = "hidden";
42117         
42118         
42119         
42120       
42121         
42122         p.showMonthPicker.defer(100, p);
42123     
42124         
42125        
42126     },
42127
42128     beforeBlur : function(){
42129         var v = this.parseDate(this.getRawValue());
42130         if(v){
42131             this.setValue(v);
42132         }
42133     }
42134
42135     /** @cfg {Boolean} grow @hide */
42136     /** @cfg {Number} growMin @hide */
42137     /** @cfg {Number} growMax @hide */
42138     /**
42139      * @hide
42140      * @method autoSize
42141      */
42142 });/*
42143  * Based on:
42144  * Ext JS Library 1.1.1
42145  * Copyright(c) 2006-2007, Ext JS, LLC.
42146  *
42147  * Originally Released Under LGPL - original licence link has changed is not relivant.
42148  *
42149  * Fork - LGPL
42150  * <script type="text/javascript">
42151  */
42152  
42153
42154 /**
42155  * @class Roo.form.ComboBox
42156  * @extends Roo.form.TriggerField
42157  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
42158  * @constructor
42159  * Create a new ComboBox.
42160  * @param {Object} config Configuration options
42161  */
42162 Roo.form.ComboBox = function(config){
42163     Roo.form.ComboBox.superclass.constructor.call(this, config);
42164     this.addEvents({
42165         /**
42166          * @event expand
42167          * Fires when the dropdown list is expanded
42168              * @param {Roo.form.ComboBox} combo This combo box
42169              */
42170         'expand' : true,
42171         /**
42172          * @event collapse
42173          * Fires when the dropdown list is collapsed
42174              * @param {Roo.form.ComboBox} combo This combo box
42175              */
42176         'collapse' : true,
42177         /**
42178          * @event beforeselect
42179          * Fires before a list item is selected. Return false to cancel the selection.
42180              * @param {Roo.form.ComboBox} combo This combo box
42181              * @param {Roo.data.Record} record The data record returned from the underlying store
42182              * @param {Number} index The index of the selected item in the dropdown list
42183              */
42184         'beforeselect' : true,
42185         /**
42186          * @event select
42187          * Fires when a list item is selected
42188              * @param {Roo.form.ComboBox} combo This combo box
42189              * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
42190              * @param {Number} index The index of the selected item in the dropdown list
42191              */
42192         'select' : true,
42193         /**
42194          * @event beforequery
42195          * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
42196          * The event object passed has these properties:
42197              * @param {Roo.form.ComboBox} combo This combo box
42198              * @param {String} query The query
42199              * @param {Boolean} forceAll true to force "all" query
42200              * @param {Boolean} cancel true to cancel the query
42201              * @param {Object} e The query event object
42202              */
42203         'beforequery': true,
42204          /**
42205          * @event add
42206          * Fires when the 'add' icon is pressed (add a listener to enable add button)
42207              * @param {Roo.form.ComboBox} combo This combo box
42208              */
42209         'add' : true,
42210         /**
42211          * @event edit
42212          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
42213              * @param {Roo.form.ComboBox} combo This combo box
42214              * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
42215              */
42216         'edit' : true
42217         
42218         
42219     });
42220     if(this.transform){
42221         this.allowDomMove = false;
42222         var s = Roo.getDom(this.transform);
42223         if(!this.hiddenName){
42224             this.hiddenName = s.name;
42225         }
42226         if(!this.store){
42227             this.mode = 'local';
42228             var d = [], opts = s.options;
42229             for(var i = 0, len = opts.length;i < len; i++){
42230                 var o = opts[i];
42231                 var value = (Roo.isIE ? o.getAttributeNode('value').specified : o.hasAttribute('value')) ? o.value : o.text;
42232                 if(o.selected) {
42233                     this.value = value;
42234                 }
42235                 d.push([value, o.text]);
42236             }
42237             this.store = new Roo.data.SimpleStore({
42238                 'id': 0,
42239                 fields: ['value', 'text'],
42240                 data : d
42241             });
42242             this.valueField = 'value';
42243             this.displayField = 'text';
42244         }
42245         s.name = Roo.id(); // wipe out the name in case somewhere else they have a reference
42246         if(!this.lazyRender){
42247             this.target = true;
42248             this.el = Roo.DomHelper.insertBefore(s, this.autoCreate || this.defaultAutoCreate);
42249             s.parentNode.removeChild(s); // remove it
42250             this.render(this.el.parentNode);
42251         }else{
42252             s.parentNode.removeChild(s); // remove it
42253         }
42254
42255     }
42256     if (this.store) {
42257         this.store = Roo.factory(this.store, Roo.data);
42258     }
42259     
42260     this.selectedIndex = -1;
42261     if(this.mode == 'local'){
42262         if(config.queryDelay === undefined){
42263             this.queryDelay = 10;
42264         }
42265         if(config.minChars === undefined){
42266             this.minChars = 0;
42267         }
42268     }
42269 };
42270
42271 Roo.extend(Roo.form.ComboBox, Roo.form.TriggerField, {
42272     /**
42273      * @cfg {String/HTMLElement/Element} transform The id, DOM node or element of an existing select to convert to a ComboBox
42274      */
42275     /**
42276      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
42277      * rendering into an Roo.Editor, defaults to false)
42278      */
42279     /**
42280      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
42281      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
42282      */
42283     /**
42284      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
42285      */
42286     /**
42287      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
42288      * the dropdown list (defaults to undefined, with no header element)
42289      */
42290
42291      /**
42292      * @cfg {String/Roo.Template} tpl The template to use to render the output
42293      */
42294      
42295     // private
42296     defaultAutoCreate : {tag: "input", type: "text", size: "24", autocomplete: "off"},
42297     /**
42298      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
42299      */
42300     listWidth: undefined,
42301     /**
42302      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
42303      * mode = 'remote' or 'text' if mode = 'local')
42304      */
42305     displayField: undefined,
42306     /**
42307      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
42308      * mode = 'remote' or 'value' if mode = 'local'). 
42309      * Note: use of a valueField requires the user make a selection
42310      * in order for a value to be mapped.
42311      */
42312     valueField: undefined,
42313     
42314     
42315     /**
42316      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
42317      * field's data value (defaults to the underlying DOM element's name)
42318      */
42319     hiddenName: undefined,
42320     /**
42321      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
42322      */
42323     listClass: '',
42324     /**
42325      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
42326      */
42327     selectedClass: 'x-combo-selected',
42328     /**
42329      * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
42330      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-arrow-trigger'
42331      * which displays a downward arrow icon).
42332      */
42333     triggerClass : 'x-form-arrow-trigger',
42334     /**
42335      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
42336      */
42337     shadow:'sides',
42338     /**
42339      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
42340      * anchor positions (defaults to 'tl-bl')
42341      */
42342     listAlign: 'tl-bl?',
42343     /**
42344      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
42345      */
42346     maxHeight: 300,
42347     /**
42348      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
42349      * query specified by the allQuery config option (defaults to 'query')
42350      */
42351     triggerAction: 'query',
42352     /**
42353      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
42354      * (defaults to 4, does not apply if editable = false)
42355      */
42356     minChars : 4,
42357     /**
42358      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
42359      * delay (typeAheadDelay) if it matches a known value (defaults to false)
42360      */
42361     typeAhead: false,
42362     /**
42363      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
42364      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
42365      */
42366     queryDelay: 500,
42367     /**
42368      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
42369      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
42370      */
42371     pageSize: 0,
42372     /**
42373      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
42374      * when editable = true (defaults to false)
42375      */
42376     selectOnFocus:false,
42377     /**
42378      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
42379      */
42380     queryParam: 'query',
42381     /**
42382      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
42383      * when mode = 'remote' (defaults to 'Loading...')
42384      */
42385     loadingText: 'Loading...',
42386     /**
42387      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
42388      */
42389     resizable: false,
42390     /**
42391      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
42392      */
42393     handleHeight : 8,
42394     /**
42395      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
42396      * traditional select (defaults to true)
42397      */
42398     editable: true,
42399     /**
42400      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
42401      */
42402     allQuery: '',
42403     /**
42404      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
42405      */
42406     mode: 'remote',
42407     /**
42408      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
42409      * listWidth has a higher value)
42410      */
42411     minListWidth : 70,
42412     /**
42413      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
42414      * allow the user to set arbitrary text into the field (defaults to false)
42415      */
42416     forceSelection:false,
42417     /**
42418      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
42419      * if typeAhead = true (defaults to 250)
42420      */
42421     typeAheadDelay : 250,
42422     /**
42423      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
42424      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
42425      */
42426     valueNotFoundText : undefined,
42427     /**
42428      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
42429      */
42430     blockFocus : false,
42431     
42432     /**
42433      * @cfg {Boolean} disableClear Disable showing of clear button.
42434      */
42435     disableClear : false,
42436     /**
42437      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
42438      */
42439     alwaysQuery : false,
42440     
42441     //private
42442     addicon : false,
42443     editicon: false,
42444     
42445     // element that contains real text value.. (when hidden is used..)
42446      
42447     // private
42448     onRender : function(ct, position)
42449     {
42450         Roo.form.ComboBox.superclass.onRender.call(this, ct, position);
42451         
42452         if(this.hiddenName){
42453             this.hiddenField = this.el.insertSibling({tag:'input', type:'hidden', name: this.hiddenName, id:  (this.hiddenId||this.hiddenName)},
42454                     'before', true);
42455             this.hiddenField.value =
42456                 this.hiddenValue !== undefined ? this.hiddenValue :
42457                 this.value !== undefined ? this.value : '';
42458
42459             // prevent input submission
42460             this.el.dom.removeAttribute('name');
42461              
42462              
42463         }
42464         
42465         if(Roo.isGecko){
42466             this.el.dom.setAttribute('autocomplete', 'off');
42467         }
42468
42469         var cls = 'x-combo-list';
42470
42471         this.list = new Roo.Layer({
42472             shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
42473         });
42474
42475         var lw = this.listWidth || Math.max(this.wrap.getWidth(), this.minListWidth);
42476         this.list.setWidth(lw);
42477         this.list.swallowEvent('mousewheel');
42478         this.assetHeight = 0;
42479
42480         if(this.title){
42481             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
42482             this.assetHeight += this.header.getHeight();
42483         }
42484
42485         this.innerList = this.list.createChild({cls:cls+'-inner'});
42486         this.innerList.on('mouseover', this.onViewOver, this);
42487         this.innerList.on('mousemove', this.onViewMove, this);
42488         this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
42489         
42490         if(this.allowBlank && !this.pageSize && !this.disableClear){
42491             this.footer = this.list.createChild({cls:cls+'-ft'});
42492             this.pageTb = new Roo.Toolbar(this.footer);
42493            
42494         }
42495         if(this.pageSize){
42496             this.footer = this.list.createChild({cls:cls+'-ft'});
42497             this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
42498                     {pageSize: this.pageSize});
42499             
42500         }
42501         
42502         if (this.pageTb && this.allowBlank && !this.disableClear) {
42503             var _this = this;
42504             this.pageTb.add(new Roo.Toolbar.Fill(), {
42505                 cls: 'x-btn-icon x-btn-clear',
42506                 text: '&#160;',
42507                 handler: function()
42508                 {
42509                     _this.collapse();
42510                     _this.clearValue();
42511                     _this.onSelect(false, -1);
42512                 }
42513             });
42514         }
42515         if (this.footer) {
42516             this.assetHeight += this.footer.getHeight();
42517         }
42518         
42519
42520         if(!this.tpl){
42521             this.tpl = '<div class="'+cls+'-item">{' + this.displayField + '}</div>';
42522         }
42523
42524         this.view = new Roo.View(this.innerList, this.tpl, {
42525             singleSelect:true,
42526             store: this.store,
42527             selectedClass: this.selectedClass
42528         });
42529
42530         this.view.on('click', this.onViewClick, this);
42531
42532         this.store.on('beforeload', this.onBeforeLoad, this);
42533         this.store.on('load', this.onLoad, this);
42534         this.store.on('loadexception', this.onLoadException, this);
42535
42536         if(this.resizable){
42537             this.resizer = new Roo.Resizable(this.list,  {
42538                pinned:true, handles:'se'
42539             });
42540             this.resizer.on('resize', function(r, w, h){
42541                 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
42542                 this.listWidth = w;
42543                 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
42544                 this.restrictHeight();
42545             }, this);
42546             this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
42547         }
42548         if(!this.editable){
42549             this.editable = true;
42550             this.setEditable(false);
42551         }  
42552         
42553         
42554         if (typeof(this.events.add.listeners) != 'undefined') {
42555             
42556             this.addicon = this.wrap.createChild(
42557                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });  
42558        
42559             this.addicon.on('click', function(e) {
42560                 this.fireEvent('add', this);
42561             }, this);
42562         }
42563         if (typeof(this.events.edit.listeners) != 'undefined') {
42564             
42565             this.editicon = this.wrap.createChild(
42566                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });  
42567             if (this.addicon) {
42568                 this.editicon.setStyle('margin-left', '40px');
42569             }
42570             this.editicon.on('click', function(e) {
42571                 
42572                 // we fire even  if inothing is selected..
42573                 this.fireEvent('edit', this, this.lastData );
42574                 
42575             }, this);
42576         }
42577         
42578         
42579         
42580     },
42581
42582     // private
42583     initEvents : function(){
42584         Roo.form.ComboBox.superclass.initEvents.call(this);
42585
42586         this.keyNav = new Roo.KeyNav(this.el, {
42587             "up" : function(e){
42588                 this.inKeyMode = true;
42589                 this.selectPrev();
42590             },
42591
42592             "down" : function(e){
42593                 if(!this.isExpanded()){
42594                     this.onTriggerClick();
42595                 }else{
42596                     this.inKeyMode = true;
42597                     this.selectNext();
42598                 }
42599             },
42600
42601             "enter" : function(e){
42602                 this.onViewClick();
42603                 //return true;
42604             },
42605
42606             "esc" : function(e){
42607                 this.collapse();
42608             },
42609
42610             "tab" : function(e){
42611                 this.onViewClick(false);
42612                 this.fireEvent("specialkey", this, e);
42613                 return true;
42614             },
42615
42616             scope : this,
42617
42618             doRelay : function(foo, bar, hname){
42619                 if(hname == 'down' || this.scope.isExpanded()){
42620                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
42621                 }
42622                 return true;
42623             },
42624
42625             forceKeyDown: true
42626         });
42627         this.queryDelay = Math.max(this.queryDelay || 10,
42628                 this.mode == 'local' ? 10 : 250);
42629         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
42630         if(this.typeAhead){
42631             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
42632         }
42633         if(this.editable !== false){
42634             this.el.on("keyup", this.onKeyUp, this);
42635         }
42636         if(this.forceSelection){
42637             this.on('blur', this.doForce, this);
42638         }
42639     },
42640
42641     onDestroy : function(){
42642         if(this.view){
42643             this.view.setStore(null);
42644             this.view.el.removeAllListeners();
42645             this.view.el.remove();
42646             this.view.purgeListeners();
42647         }
42648         if(this.list){
42649             this.list.destroy();
42650         }
42651         if(this.store){
42652             this.store.un('beforeload', this.onBeforeLoad, this);
42653             this.store.un('load', this.onLoad, this);
42654             this.store.un('loadexception', this.onLoadException, this);
42655         }
42656         Roo.form.ComboBox.superclass.onDestroy.call(this);
42657     },
42658
42659     // private
42660     fireKey : function(e){
42661         if(e.isNavKeyPress() && !this.list.isVisible()){
42662             this.fireEvent("specialkey", this, e);
42663         }
42664     },
42665
42666     // private
42667     onResize: function(w, h){
42668         Roo.form.ComboBox.superclass.onResize.apply(this, arguments);
42669         
42670         if(typeof w != 'number'){
42671             // we do not handle it!?!?
42672             return;
42673         }
42674         var tw = this.trigger.getWidth();
42675         tw += this.addicon ? this.addicon.getWidth() : 0;
42676         tw += this.editicon ? this.editicon.getWidth() : 0;
42677         var x = w - tw;
42678         this.el.setWidth( this.adjustWidth('input', x));
42679             
42680         this.trigger.setStyle('left', x+'px');
42681         
42682         if(this.list && this.listWidth === undefined){
42683             var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
42684             this.list.setWidth(lw);
42685             this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
42686         }
42687         
42688     
42689         
42690     },
42691
42692     /**
42693      * Allow or prevent the user from directly editing the field text.  If false is passed,
42694      * the user will only be able to select from the items defined in the dropdown list.  This method
42695      * is the runtime equivalent of setting the 'editable' config option at config time.
42696      * @param {Boolean} value True to allow the user to directly edit the field text
42697      */
42698     setEditable : function(value){
42699         if(value == this.editable){
42700             return;
42701         }
42702         this.editable = value;
42703         if(!value){
42704             this.el.dom.setAttribute('readOnly', true);
42705             this.el.on('mousedown', this.onTriggerClick,  this);
42706             this.el.addClass('x-combo-noedit');
42707         }else{
42708             this.el.dom.setAttribute('readOnly', false);
42709             this.el.un('mousedown', this.onTriggerClick,  this);
42710             this.el.removeClass('x-combo-noedit');
42711         }
42712     },
42713
42714     // private
42715     onBeforeLoad : function(){
42716         if(!this.hasFocus){
42717             return;
42718         }
42719         this.innerList.update(this.loadingText ?
42720                '<div class="loading-indicator">'+this.loadingText+'</div>' : '');
42721         this.restrictHeight();
42722         this.selectedIndex = -1;
42723     },
42724
42725     // private
42726     onLoad : function(){
42727         if(!this.hasFocus){
42728             return;
42729         }
42730         if(this.store.getCount() > 0){
42731             this.expand();
42732             this.restrictHeight();
42733             if(this.lastQuery == this.allQuery){
42734                 if(this.editable){
42735                     this.el.dom.select();
42736                 }
42737                 if(!this.selectByValue(this.value, true)){
42738                     this.select(0, true);
42739                 }
42740             }else{
42741                 this.selectNext();
42742                 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
42743                     this.taTask.delay(this.typeAheadDelay);
42744                 }
42745             }
42746         }else{
42747             this.onEmptyResults();
42748         }
42749         //this.el.focus();
42750     },
42751     // private
42752     onLoadException : function()
42753     {
42754         this.collapse();
42755         Roo.log(this.store.reader.jsonData);
42756         if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
42757             Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
42758         }
42759         
42760         
42761     },
42762     // private
42763     onTypeAhead : function(){
42764         if(this.store.getCount() > 0){
42765             var r = this.store.getAt(0);
42766             var newValue = r.data[this.displayField];
42767             var len = newValue.length;
42768             var selStart = this.getRawValue().length;
42769             if(selStart != len){
42770                 this.setRawValue(newValue);
42771                 this.selectText(selStart, newValue.length);
42772             }
42773         }
42774     },
42775
42776     // private
42777     onSelect : function(record, index){
42778         if(this.fireEvent('beforeselect', this, record, index) !== false){
42779             this.setFromData(index > -1 ? record.data : false);
42780             this.collapse();
42781             this.fireEvent('select', this, record, index);
42782         }
42783     },
42784
42785     /**
42786      * Returns the currently selected field value or empty string if no value is set.
42787      * @return {String} value The selected value
42788      */
42789     getValue : function(){
42790         if(this.valueField){
42791             return typeof this.value != 'undefined' ? this.value : '';
42792         }
42793         return Roo.form.ComboBox.superclass.getValue.call(this);
42794     },
42795
42796     /**
42797      * Clears any text/value currently set in the field
42798      */
42799     clearValue : function(){
42800         if(this.hiddenField){
42801             this.hiddenField.value = '';
42802         }
42803         this.value = '';
42804         this.setRawValue('');
42805         this.lastSelectionText = '';
42806         
42807     },
42808
42809     /**
42810      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
42811      * will be displayed in the field.  If the value does not match the data value of an existing item,
42812      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
42813      * Otherwise the field will be blank (although the value will still be set).
42814      * @param {String} value The value to match
42815      */
42816     setValue : function(v){
42817         var text = v;
42818         if(this.valueField){
42819             var r = this.findRecord(this.valueField, v);
42820             if(r){
42821                 text = r.data[this.displayField];
42822             }else if(this.valueNotFoundText !== undefined){
42823                 text = this.valueNotFoundText;
42824             }
42825         }
42826         this.lastSelectionText = text;
42827         if(this.hiddenField){
42828             this.hiddenField.value = v;
42829         }
42830         Roo.form.ComboBox.superclass.setValue.call(this, text);
42831         this.value = v;
42832     },
42833     /**
42834      * @property {Object} the last set data for the element
42835      */
42836     
42837     lastData : false,
42838     /**
42839      * Sets the value of the field based on a object which is related to the record format for the store.
42840      * @param {Object} value the value to set as. or false on reset?
42841      */
42842     setFromData : function(o){
42843         var dv = ''; // display value
42844         var vv = ''; // value value..
42845         this.lastData = o;
42846         if (this.displayField) {
42847             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
42848         } else {
42849             // this is an error condition!!!
42850             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
42851         }
42852         
42853         if(this.valueField){
42854             vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
42855         }
42856         if(this.hiddenField){
42857             this.hiddenField.value = vv;
42858             
42859             this.lastSelectionText = dv;
42860             Roo.form.ComboBox.superclass.setValue.call(this, dv);
42861             this.value = vv;
42862             return;
42863         }
42864         // no hidden field.. - we store the value in 'value', but still display
42865         // display field!!!!
42866         this.lastSelectionText = dv;
42867         Roo.form.ComboBox.superclass.setValue.call(this, dv);
42868         this.value = vv;
42869         
42870         
42871     },
42872     // private
42873     reset : function(){
42874         // overridden so that last data is reset..
42875         this.setValue(this.resetValue);
42876         this.originalValue = this.getValue();
42877         this.clearInvalid();
42878         this.lastData = false;
42879         if (this.view) {
42880             this.view.clearSelections();
42881         }
42882     },
42883     // private
42884     findRecord : function(prop, value){
42885         var record;
42886         if(this.store.getCount() > 0){
42887             this.store.each(function(r){
42888                 if(r.data[prop] == value){
42889                     record = r;
42890                     return false;
42891                 }
42892                 return true;
42893             });
42894         }
42895         return record;
42896     },
42897     
42898     getName: function()
42899     {
42900         // returns hidden if it's set..
42901         if (!this.rendered) {return ''};
42902         return !this.hiddenName && this.el.dom.name  ? this.el.dom.name : (this.hiddenName || '');
42903         
42904     },
42905     // private
42906     onViewMove : function(e, t){
42907         this.inKeyMode = false;
42908     },
42909
42910     // private
42911     onViewOver : function(e, t){
42912         if(this.inKeyMode){ // prevent key nav and mouse over conflicts
42913             return;
42914         }
42915         var item = this.view.findItemFromChild(t);
42916         if(item){
42917             var index = this.view.indexOf(item);
42918             this.select(index, false);
42919         }
42920     },
42921
42922     // private
42923     onViewClick : function(doFocus)
42924     {
42925         var index = this.view.getSelectedIndexes()[0];
42926         var r = this.store.getAt(index);
42927         if(r){
42928             this.onSelect(r, index);
42929         }
42930         if(doFocus !== false && !this.blockFocus){
42931             this.el.focus();
42932         }
42933     },
42934
42935     // private
42936     restrictHeight : function(){
42937         this.innerList.dom.style.height = '';
42938         var inner = this.innerList.dom;
42939         var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
42940         this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
42941         this.list.beginUpdate();
42942         this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
42943         this.list.alignTo(this.el, this.listAlign);
42944         this.list.endUpdate();
42945     },
42946
42947     // private
42948     onEmptyResults : function(){
42949         this.collapse();
42950     },
42951
42952     /**
42953      * Returns true if the dropdown list is expanded, else false.
42954      */
42955     isExpanded : function(){
42956         return this.list.isVisible();
42957     },
42958
42959     /**
42960      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
42961      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
42962      * @param {String} value The data value of the item to select
42963      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
42964      * selected item if it is not currently in view (defaults to true)
42965      * @return {Boolean} True if the value matched an item in the list, else false
42966      */
42967     selectByValue : function(v, scrollIntoView){
42968         if(v !== undefined && v !== null){
42969             var r = this.findRecord(this.valueField || this.displayField, v);
42970             if(r){
42971                 this.select(this.store.indexOf(r), scrollIntoView);
42972                 return true;
42973             }
42974         }
42975         return false;
42976     },
42977
42978     /**
42979      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
42980      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
42981      * @param {Number} index The zero-based index of the list item to select
42982      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
42983      * selected item if it is not currently in view (defaults to true)
42984      */
42985     select : function(index, scrollIntoView){
42986         this.selectedIndex = index;
42987         this.view.select(index);
42988         if(scrollIntoView !== false){
42989             var el = this.view.getNode(index);
42990             if(el){
42991                 this.innerList.scrollChildIntoView(el, false);
42992             }
42993         }
42994     },
42995
42996     // private
42997     selectNext : function(){
42998         var ct = this.store.getCount();
42999         if(ct > 0){
43000             if(this.selectedIndex == -1){
43001                 this.select(0);
43002             }else if(this.selectedIndex < ct-1){
43003                 this.select(this.selectedIndex+1);
43004             }
43005         }
43006     },
43007
43008     // private
43009     selectPrev : function(){
43010         var ct = this.store.getCount();
43011         if(ct > 0){
43012             if(this.selectedIndex == -1){
43013                 this.select(0);
43014             }else if(this.selectedIndex != 0){
43015                 this.select(this.selectedIndex-1);
43016             }
43017         }
43018     },
43019
43020     // private
43021     onKeyUp : function(e){
43022         if(this.editable !== false && !e.isSpecialKey()){
43023             this.lastKey = e.getKey();
43024             this.dqTask.delay(this.queryDelay);
43025         }
43026     },
43027
43028     // private
43029     validateBlur : function(){
43030         return !this.list || !this.list.isVisible();   
43031     },
43032
43033     // private
43034     initQuery : function(){
43035         this.doQuery(this.getRawValue());
43036     },
43037
43038     // private
43039     doForce : function(){
43040         if(this.el.dom.value.length > 0){
43041             this.el.dom.value =
43042                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
43043              
43044         }
43045     },
43046
43047     /**
43048      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
43049      * query allowing the query action to be canceled if needed.
43050      * @param {String} query The SQL query to execute
43051      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
43052      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
43053      * saved in the current store (defaults to false)
43054      */
43055     doQuery : function(q, forceAll){
43056         if(q === undefined || q === null){
43057             q = '';
43058         }
43059         var qe = {
43060             query: q,
43061             forceAll: forceAll,
43062             combo: this,
43063             cancel:false
43064         };
43065         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
43066             return false;
43067         }
43068         q = qe.query;
43069         forceAll = qe.forceAll;
43070         if(forceAll === true || (q.length >= this.minChars)){
43071             if(this.lastQuery != q || this.alwaysQuery){
43072                 this.lastQuery = q;
43073                 if(this.mode == 'local'){
43074                     this.selectedIndex = -1;
43075                     if(forceAll){
43076                         this.store.clearFilter();
43077                     }else{
43078                         this.store.filter(this.displayField, q);
43079                     }
43080                     this.onLoad();
43081                 }else{
43082                     this.store.baseParams[this.queryParam] = q;
43083                     this.store.load({
43084                         params: this.getParams(q)
43085                     });
43086                     this.expand();
43087                 }
43088             }else{
43089                 this.selectedIndex = -1;
43090                 this.onLoad();   
43091             }
43092         }
43093     },
43094
43095     // private
43096     getParams : function(q){
43097         var p = {};
43098         //p[this.queryParam] = q;
43099         if(this.pageSize){
43100             p.start = 0;
43101             p.limit = this.pageSize;
43102         }
43103         return p;
43104     },
43105
43106     /**
43107      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
43108      */
43109     collapse : function(){
43110         if(!this.isExpanded()){
43111             return;
43112         }
43113         this.list.hide();
43114         Roo.get(document).un('mousedown', this.collapseIf, this);
43115         Roo.get(document).un('mousewheel', this.collapseIf, this);
43116         if (!this.editable) {
43117             Roo.get(document).un('keydown', this.listKeyPress, this);
43118         }
43119         this.fireEvent('collapse', this);
43120     },
43121
43122     // private
43123     collapseIf : function(e){
43124         if(!e.within(this.wrap) && !e.within(this.list)){
43125             this.collapse();
43126         }
43127     },
43128
43129     /**
43130      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
43131      */
43132     expand : function(){
43133         if(this.isExpanded() || !this.hasFocus){
43134             return;
43135         }
43136         this.list.alignTo(this.el, this.listAlign);
43137         this.list.show();
43138         Roo.get(document).on('mousedown', this.collapseIf, this);
43139         Roo.get(document).on('mousewheel', this.collapseIf, this);
43140         if (!this.editable) {
43141             Roo.get(document).on('keydown', this.listKeyPress, this);
43142         }
43143         
43144         this.fireEvent('expand', this);
43145     },
43146
43147     // private
43148     // Implements the default empty TriggerField.onTriggerClick function
43149     onTriggerClick : function(){
43150         if(this.disabled){
43151             return;
43152         }
43153         if(this.isExpanded()){
43154             this.collapse();
43155             if (!this.blockFocus) {
43156                 this.el.focus();
43157             }
43158             
43159         }else {
43160             this.hasFocus = true;
43161             if(this.triggerAction == 'all') {
43162                 this.doQuery(this.allQuery, true);
43163             } else {
43164                 this.doQuery(this.getRawValue());
43165             }
43166             if (!this.blockFocus) {
43167                 this.el.focus();
43168             }
43169         }
43170     },
43171     listKeyPress : function(e)
43172     {
43173         //Roo.log('listkeypress');
43174         // scroll to first matching element based on key pres..
43175         if (e.isSpecialKey()) {
43176             return false;
43177         }
43178         var k = String.fromCharCode(e.getKey()).toUpperCase();
43179         //Roo.log(k);
43180         var match  = false;
43181         var csel = this.view.getSelectedNodes();
43182         var cselitem = false;
43183         if (csel.length) {
43184             var ix = this.view.indexOf(csel[0]);
43185             cselitem  = this.store.getAt(ix);
43186             if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
43187                 cselitem = false;
43188             }
43189             
43190         }
43191         
43192         this.store.each(function(v) { 
43193             if (cselitem) {
43194                 // start at existing selection.
43195                 if (cselitem.id == v.id) {
43196                     cselitem = false;
43197                 }
43198                 return;
43199             }
43200                 
43201             if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
43202                 match = this.store.indexOf(v);
43203                 return false;
43204             }
43205         }, this);
43206         
43207         if (match === false) {
43208             return true; // no more action?
43209         }
43210         // scroll to?
43211         this.view.select(match);
43212         var sn = Roo.get(this.view.getSelectedNodes()[0]);
43213         sn.scrollIntoView(sn.dom.parentNode, false);
43214     } 
43215
43216     /** 
43217     * @cfg {Boolean} grow 
43218     * @hide 
43219     */
43220     /** 
43221     * @cfg {Number} growMin 
43222     * @hide 
43223     */
43224     /** 
43225     * @cfg {Number} growMax 
43226     * @hide 
43227     */
43228     /**
43229      * @hide
43230      * @method autoSize
43231      */
43232 });/*
43233  * Copyright(c) 2010-2012, Roo J Solutions Limited
43234  *
43235  * Licence LGPL
43236  *
43237  */
43238
43239 /**
43240  * @class Roo.form.ComboBoxArray
43241  * @extends Roo.form.TextField
43242  * A facebook style adder... for lists of email / people / countries  etc...
43243  * pick multiple items from a combo box, and shows each one.
43244  *
43245  *  Fred [x]  Brian [x]  [Pick another |v]
43246  *
43247  *
43248  *  For this to work: it needs various extra information
43249  *    - normal combo problay has
43250  *      name, hiddenName
43251  *    + displayField, valueField
43252  *
43253  *    For our purpose...
43254  *
43255  *
43256  *   If we change from 'extends' to wrapping...
43257  *   
43258  *  
43259  *
43260  
43261  
43262  * @constructor
43263  * Create a new ComboBoxArray.
43264  * @param {Object} config Configuration options
43265  */
43266  
43267
43268 Roo.form.ComboBoxArray = function(config)
43269 {
43270     this.addEvents({
43271         /**
43272          * @event beforeremove
43273          * Fires before remove the value from the list
43274              * @param {Roo.form.ComboBoxArray} _self This combo box array
43275              * @param {Roo.form.ComboBoxArray.Item} item removed item
43276              */
43277         'beforeremove' : true,
43278         /**
43279          * @event remove
43280          * Fires when remove the value from the list
43281              * @param {Roo.form.ComboBoxArray} _self This combo box array
43282              * @param {Roo.form.ComboBoxArray.Item} item removed item
43283              */
43284         'remove' : true
43285         
43286         
43287     });
43288     
43289     Roo.form.ComboBoxArray.superclass.constructor.call(this, config);
43290     
43291     this.items = new Roo.util.MixedCollection(false);
43292     
43293     // construct the child combo...
43294     
43295     
43296     
43297     
43298    
43299     
43300 }
43301
43302  
43303 Roo.extend(Roo.form.ComboBoxArray, Roo.form.TextField,
43304
43305     /**
43306      * @cfg {Roo.form.ComboBox} combo [required] The combo box that is wrapped
43307      */
43308     
43309     lastData : false,
43310     
43311     // behavies liek a hiddne field
43312     inputType:      'hidden',
43313     /**
43314      * @cfg {Number} width The width of the box that displays the selected element
43315      */ 
43316     width:          300,
43317
43318     
43319     
43320     /**
43321      * @cfg {String} name    The name of the visable items on this form (eg. titles not ids)
43322      */
43323     name : false,
43324     /**
43325      * @cfg {String} hiddenName    The hidden name of the field, often contains an comma seperated list of names
43326      */
43327     hiddenName : false,
43328       /**
43329      * @cfg {String} seperator    The value seperator normally ',' 
43330      */
43331     seperator : ',',
43332     
43333     // private the array of items that are displayed..
43334     items  : false,
43335     // private - the hidden field el.
43336     hiddenEl : false,
43337     // private - the filed el..
43338     el : false,
43339     
43340     //validateValue : function() { return true; }, // all values are ok!
43341     //onAddClick: function() { },
43342     
43343     onRender : function(ct, position) 
43344     {
43345         
43346         // create the standard hidden element
43347         //Roo.form.ComboBoxArray.superclass.onRender.call(this, ct, position);
43348         
43349         
43350         // give fake names to child combo;
43351         this.combo.hiddenName = this.hiddenName ? (this.hiddenName+'-subcombo') : this.hiddenName;
43352         this.combo.name = this.name ? (this.name+'-subcombo') : this.name;
43353         
43354         this.combo = Roo.factory(this.combo, Roo.form);
43355         this.combo.onRender(ct, position);
43356         if (typeof(this.combo.width) != 'undefined') {
43357             this.combo.onResize(this.combo.width,0);
43358         }
43359         
43360         this.combo.initEvents();
43361         
43362         // assigned so form know we need to do this..
43363         this.store          = this.combo.store;
43364         this.valueField     = this.combo.valueField;
43365         this.displayField   = this.combo.displayField ;
43366         
43367         
43368         this.combo.wrap.addClass('x-cbarray-grp');
43369         
43370         var cbwrap = this.combo.wrap.createChild(
43371             {tag: 'div', cls: 'x-cbarray-cb'},
43372             this.combo.el.dom
43373         );
43374         
43375              
43376         this.hiddenEl = this.combo.wrap.createChild({
43377             tag: 'input',  type:'hidden' , name: this.hiddenName, value : ''
43378         });
43379         this.el = this.combo.wrap.createChild({
43380             tag: 'input',  type:'hidden' , name: this.name, value : ''
43381         });
43382          //   this.el.dom.removeAttribute("name");
43383         
43384         
43385         this.outerWrap = this.combo.wrap;
43386         this.wrap = cbwrap;
43387         
43388         this.outerWrap.setWidth(this.width);
43389         this.outerWrap.dom.removeChild(this.el.dom);
43390         
43391         this.wrap.dom.appendChild(this.el.dom);
43392         this.outerWrap.dom.removeChild(this.combo.trigger.dom);
43393         this.combo.wrap.dom.appendChild(this.combo.trigger.dom);
43394         
43395         this.combo.trigger.setStyle('position','relative');
43396         this.combo.trigger.setStyle('left', '0px');
43397         this.combo.trigger.setStyle('top', '2px');
43398         
43399         this.combo.el.setStyle('vertical-align', 'text-bottom');
43400         
43401         //this.trigger.setStyle('vertical-align', 'top');
43402         
43403         // this should use the code from combo really... on('add' ....)
43404         if (this.adder) {
43405             
43406         
43407             this.adder = this.outerWrap.createChild(
43408                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-adder', style: 'margin-left:2px'});  
43409             var _t = this;
43410             this.adder.on('click', function(e) {
43411                 _t.fireEvent('adderclick', this, e);
43412             }, _t);
43413         }
43414         //var _t = this;
43415         //this.adder.on('click', this.onAddClick, _t);
43416         
43417         
43418         this.combo.on('select', function(cb, rec, ix) {
43419             this.addItem(rec.data);
43420             
43421             cb.setValue('');
43422             cb.el.dom.value = '';
43423             //cb.lastData = rec.data;
43424             // add to list
43425             
43426         }, this);
43427         
43428         
43429     },
43430     
43431     
43432     getName: function()
43433     {
43434         // returns hidden if it's set..
43435         if (!this.rendered) {return ''};
43436         return  this.hiddenName ? this.hiddenName : this.name;
43437         
43438     },
43439     
43440     
43441     onResize: function(w, h){
43442         
43443         return;
43444         // not sure if this is needed..
43445         //this.combo.onResize(w,h);
43446         
43447         if(typeof w != 'number'){
43448             // we do not handle it!?!?
43449             return;
43450         }
43451         var tw = this.combo.trigger.getWidth();
43452         tw += this.addicon ? this.addicon.getWidth() : 0;
43453         tw += this.editicon ? this.editicon.getWidth() : 0;
43454         var x = w - tw;
43455         this.combo.el.setWidth( this.combo.adjustWidth('input', x));
43456             
43457         this.combo.trigger.setStyle('left', '0px');
43458         
43459         if(this.list && this.listWidth === undefined){
43460             var lw = Math.max(x + this.combo.trigger.getWidth(), this.combo.minListWidth);
43461             this.list.setWidth(lw);
43462             this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
43463         }
43464         
43465     
43466         
43467     },
43468     
43469     addItem: function(rec)
43470     {
43471         var valueField = this.combo.valueField;
43472         var displayField = this.combo.displayField;
43473         
43474         if (this.items.indexOfKey(rec[valueField]) > -1) {
43475             //console.log("GOT " + rec.data.id);
43476             return;
43477         }
43478         
43479         var x = new Roo.form.ComboBoxArray.Item({
43480             //id : rec[this.idField],
43481             data : rec,
43482             displayField : displayField ,
43483             tipField : displayField ,
43484             cb : this
43485         });
43486         // use the 
43487         this.items.add(rec[valueField],x);
43488         // add it before the element..
43489         this.updateHiddenEl();
43490         x.render(this.outerWrap, this.wrap.dom);
43491         // add the image handler..
43492     },
43493     
43494     updateHiddenEl : function()
43495     {
43496         this.validate();
43497         if (!this.hiddenEl) {
43498             return;
43499         }
43500         var ar = [];
43501         var idField = this.combo.valueField;
43502         
43503         this.items.each(function(f) {
43504             ar.push(f.data[idField]);
43505         });
43506         this.hiddenEl.dom.value = ar.join(this.seperator);
43507         this.validate();
43508     },
43509     
43510     reset : function()
43511     {
43512         this.items.clear();
43513         
43514         Roo.each(this.outerWrap.select('.x-cbarray-item', true).elements, function(el){
43515            el.remove();
43516         });
43517         
43518         this.el.dom.value = '';
43519         if (this.hiddenEl) {
43520             this.hiddenEl.dom.value = '';
43521         }
43522         
43523     },
43524     getValue: function()
43525     {
43526         return this.hiddenEl ? this.hiddenEl.dom.value : '';
43527     },
43528     setValue: function(v) // not a valid action - must use addItems..
43529     {
43530         
43531         this.reset();
43532          
43533         if (this.store.isLocal && (typeof(v) == 'string')) {
43534             // then we can use the store to find the values..
43535             // comma seperated at present.. this needs to allow JSON based encoding..
43536             this.hiddenEl.value  = v;
43537             var v_ar = [];
43538             Roo.each(v.split(this.seperator), function(k) {
43539                 Roo.log("CHECK " + this.valueField + ',' + k);
43540                 var li = this.store.query(this.valueField, k);
43541                 if (!li.length) {
43542                     return;
43543                 }
43544                 var add = {};
43545                 add[this.valueField] = k;
43546                 add[this.displayField] = li.item(0).data[this.displayField];
43547                 
43548                 this.addItem(add);
43549             }, this) 
43550              
43551         }
43552         if (typeof(v) == 'object' ) {
43553             // then let's assume it's an array of objects..
43554             Roo.each(v, function(l) {
43555                 var add = l;
43556                 if (typeof(l) == 'string') {
43557                     add = {};
43558                     add[this.valueField] = l;
43559                     add[this.displayField] = l
43560                 }
43561                 this.addItem(add);
43562             }, this);
43563              
43564         }
43565         
43566         
43567     },
43568     setFromData: function(v)
43569     {
43570         // this recieves an object, if setValues is called.
43571         this.reset();
43572         this.el.dom.value = v[this.displayField];
43573         this.hiddenEl.dom.value = v[this.valueField];
43574         if (typeof(v[this.valueField]) != 'string' || !v[this.valueField].length) {
43575             return;
43576         }
43577         var kv = v[this.valueField];
43578         var dv = v[this.displayField];
43579         kv = typeof(kv) != 'string' ? '' : kv;
43580         dv = typeof(dv) != 'string' ? '' : dv;
43581         
43582         
43583         var keys = kv.split(this.seperator);
43584         var display = dv.split(this.seperator);
43585         for (var i = 0 ; i < keys.length; i++) {
43586             add = {};
43587             add[this.valueField] = keys[i];
43588             add[this.displayField] = display[i];
43589             this.addItem(add);
43590         }
43591       
43592         
43593     },
43594     
43595     /**
43596      * Validates the combox array value
43597      * @return {Boolean} True if the value is valid, else false
43598      */
43599     validate : function(){
43600         if(this.disabled || this.validateValue(this.processValue(this.getValue()))){
43601             this.clearInvalid();
43602             return true;
43603         }
43604         return false;
43605     },
43606     
43607     validateValue : function(value){
43608         return Roo.form.ComboBoxArray.superclass.validateValue.call(this, this.getValue());
43609         
43610     },
43611     
43612     /*@
43613      * overide
43614      * 
43615      */
43616     isDirty : function() {
43617         if(this.disabled) {
43618             return false;
43619         }
43620         
43621         try {
43622             var d = Roo.decode(String(this.originalValue));
43623         } catch (e) {
43624             return String(this.getValue()) !== String(this.originalValue);
43625         }
43626         
43627         var originalValue = [];
43628         
43629         for (var i = 0; i < d.length; i++){
43630             originalValue.push(d[i][this.valueField]);
43631         }
43632         
43633         return String(this.getValue()) !== String(originalValue.join(this.seperator));
43634         
43635     }
43636     
43637 });
43638
43639
43640
43641 /**
43642  * @class Roo.form.ComboBoxArray.Item
43643  * @extends Roo.BoxComponent
43644  * A selected item in the list
43645  *  Fred [x]  Brian [x]  [Pick another |v]
43646  * 
43647  * @constructor
43648  * Create a new item.
43649  * @param {Object} config Configuration options
43650  */
43651  
43652 Roo.form.ComboBoxArray.Item = function(config) {
43653     config.id = Roo.id();
43654     Roo.form.ComboBoxArray.Item.superclass.constructor.call(this, config);
43655 }
43656
43657 Roo.extend(Roo.form.ComboBoxArray.Item, Roo.BoxComponent, {
43658     data : {},
43659     cb: false,
43660     displayField : false,
43661     tipField : false,
43662     
43663     
43664     defaultAutoCreate : {
43665         tag: 'div',
43666         cls: 'x-cbarray-item',
43667         cn : [ 
43668             { tag: 'div' },
43669             {
43670                 tag: 'img',
43671                 width:16,
43672                 height : 16,
43673                 src : Roo.BLANK_IMAGE_URL ,
43674                 align: 'center'
43675             }
43676         ]
43677         
43678     },
43679     
43680  
43681     onRender : function(ct, position)
43682     {
43683         Roo.form.Field.superclass.onRender.call(this, ct, position);
43684         
43685         if(!this.el){
43686             var cfg = this.getAutoCreate();
43687             this.el = ct.createChild(cfg, position);
43688         }
43689         
43690         this.el.child('img').dom.setAttribute('src', Roo.BLANK_IMAGE_URL);
43691         
43692         this.el.child('div').dom.innerHTML = this.cb.renderer ? 
43693             this.cb.renderer(this.data) :
43694             String.format('{0}',this.data[this.displayField]);
43695         
43696             
43697         this.el.child('div').dom.setAttribute('qtip',
43698                         String.format('{0}',this.data[this.tipField])
43699         );
43700         
43701         this.el.child('img').on('click', this.remove, this);
43702         
43703     },
43704    
43705     remove : function()
43706     {
43707         if(this.cb.disabled){
43708             return;
43709         }
43710         
43711         if(false !== this.cb.fireEvent('beforeremove', this.cb, this)){
43712             this.cb.items.remove(this);
43713             this.el.child('img').un('click', this.remove, this);
43714             this.el.remove();
43715             this.cb.updateHiddenEl();
43716
43717             this.cb.fireEvent('remove', this.cb, this);
43718         }
43719         
43720     }
43721 });/*
43722  * RooJS Library 1.1.1
43723  * Copyright(c) 2008-2011  Alan Knowles
43724  *
43725  * License - LGPL
43726  */
43727  
43728
43729 /**
43730  * @class Roo.form.ComboNested
43731  * @extends Roo.form.ComboBox
43732  * A combobox for that allows selection of nested items in a list,
43733  * eg.
43734  *
43735  *  Book
43736  *    -> red
43737  *    -> green
43738  *  Table
43739  *    -> square
43740  *      ->red
43741  *      ->green
43742  *    -> rectangle
43743  *      ->green
43744  *      
43745  * 
43746  * @constructor
43747  * Create a new ComboNested
43748  * @param {Object} config Configuration options
43749  */
43750 Roo.form.ComboNested = function(config){
43751     Roo.form.ComboCheck.superclass.constructor.call(this, config);
43752     // should verify some data...
43753     // like
43754     // hiddenName = required..
43755     // displayField = required
43756     // valudField == required
43757     var req= [ 'hiddenName', 'displayField', 'valueField' ];
43758     var _t = this;
43759     Roo.each(req, function(e) {
43760         if ((typeof(_t[e]) == 'undefined' ) || !_t[e].length) {
43761             throw "Roo.form.ComboNested : missing value for: " + e;
43762         }
43763     });
43764      
43765     
43766 };
43767
43768 Roo.extend(Roo.form.ComboNested, Roo.form.ComboBox, {
43769    
43770     /*
43771      * @config {Number} max Number of columns to show
43772      */
43773     
43774     maxColumns : 3,
43775    
43776     list : null, // the outermost div..
43777     innerLists : null, // the
43778     views : null,
43779     stores : null,
43780     // private
43781     loadingChildren : false,
43782     
43783     onRender : function(ct, position)
43784     {
43785         Roo.form.ComboBox.superclass.onRender.call(this, ct, position); // skip parent call - got to above..
43786         
43787         if(this.hiddenName){
43788             this.hiddenField = this.el.insertSibling({tag:'input', type:'hidden', name: this.hiddenName, id:  (this.hiddenId||this.hiddenName)},
43789                     'before', true);
43790             this.hiddenField.value =
43791                 this.hiddenValue !== undefined ? this.hiddenValue :
43792                 this.value !== undefined ? this.value : '';
43793
43794             // prevent input submission
43795             this.el.dom.removeAttribute('name');
43796              
43797              
43798         }
43799         
43800         if(Roo.isGecko){
43801             this.el.dom.setAttribute('autocomplete', 'off');
43802         }
43803
43804         var cls = 'x-combo-list';
43805
43806         this.list = new Roo.Layer({
43807             shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
43808         });
43809
43810         var lw = this.listWidth || Math.max(this.wrap.getWidth(), this.minListWidth);
43811         this.list.setWidth(lw);
43812         this.list.swallowEvent('mousewheel');
43813         this.assetHeight = 0;
43814
43815         if(this.title){
43816             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
43817             this.assetHeight += this.header.getHeight();
43818         }
43819         this.innerLists = [];
43820         this.views = [];
43821         this.stores = [];
43822         for (var i =0 ; i < this.maxColumns; i++) {
43823             this.onRenderList( cls, i);
43824         }
43825         
43826         // always needs footer, as we are going to have an 'OK' button.
43827         this.footer = this.list.createChild({cls:cls+'-ft'});
43828         this.pageTb = new Roo.Toolbar(this.footer);  
43829         var _this = this;
43830         this.pageTb.add(  {
43831             
43832             text: 'Done',
43833             handler: function()
43834             {
43835                 _this.collapse();
43836             }
43837         });
43838         
43839         if ( this.allowBlank && !this.disableClear) {
43840             
43841             this.pageTb.add(new Roo.Toolbar.Fill(), {
43842                 cls: 'x-btn-icon x-btn-clear',
43843                 text: '&#160;',
43844                 handler: function()
43845                 {
43846                     _this.collapse();
43847                     _this.clearValue();
43848                     _this.onSelect(false, -1);
43849                 }
43850             });
43851         }
43852         if (this.footer) {
43853             this.assetHeight += this.footer.getHeight();
43854         }
43855         
43856     },
43857     onRenderList : function (  cls, i)
43858     {
43859         
43860         var lw = Math.floor(
43861                 ((this.listWidth * this.maxColumns || Math.max(this.wrap.getWidth(), this.minListWidth)) - this.list.getFrameWidth('lr')) / this.maxColumns
43862         );
43863         
43864         this.list.setWidth(lw); // default to '1'
43865
43866         var il = this.innerLists[i] = this.list.createChild({cls:cls+'-inner'});
43867         //il.on('mouseover', this.onViewOver, this, { list:  i });
43868         //il.on('mousemove', this.onViewMove, this, { list:  i });
43869         il.setWidth(lw);
43870         il.setStyle({ 'overflow-x' : 'hidden'});
43871
43872         if(!this.tpl){
43873             this.tpl = new Roo.Template({
43874                 html :  '<div class="'+cls+'-item '+cls+'-item-{cn:this.isEmpty}">{' + this.displayField + '}</div>',
43875                 isEmpty: function (value, allValues) {
43876                     //Roo.log(value);
43877                     var dl = typeof(value.data) != 'undefined' ? value.data.length : value.length; ///json is a nested response..
43878                     return dl ? 'has-children' : 'no-children'
43879                 }
43880             });
43881         }
43882         
43883         var store  = this.store;
43884         if (i > 0) {
43885             store  = new Roo.data.SimpleStore({
43886                 //fields : this.store.reader.meta.fields,
43887                 reader : this.store.reader,
43888                 data : [ ]
43889             });
43890         }
43891         this.stores[i]  = store;
43892                   
43893         var view = this.views[i] = new Roo.View(
43894             il,
43895             this.tpl,
43896             {
43897                 singleSelect:true,
43898                 store: store,
43899                 selectedClass: this.selectedClass
43900             }
43901         );
43902         view.getEl().setWidth(lw);
43903         view.getEl().setStyle({
43904             position: i < 1 ? 'relative' : 'absolute',
43905             top: 0,
43906             left: (i * lw ) + 'px',
43907             display : i > 0 ? 'none' : 'block'
43908         });
43909         view.on('selectionchange', this.onSelectChange.createDelegate(this, {list : i }, true));
43910         view.on('dblclick', this.onDoubleClick.createDelegate(this, {list : i }, true));
43911         //view.on('click', this.onViewClick, this, { list : i });
43912
43913         store.on('beforeload', this.onBeforeLoad, this);
43914         store.on('load',  this.onLoad, this, { list  : i});
43915         store.on('loadexception', this.onLoadException, this);
43916
43917         // hide the other vies..
43918         
43919         
43920         
43921     },
43922       
43923     restrictHeight : function()
43924     {
43925         var mh = 0;
43926         Roo.each(this.innerLists, function(il,i) {
43927             var el = this.views[i].getEl();
43928             el.dom.style.height = '';
43929             var inner = el.dom;
43930             var h = Math.max(il.clientHeight, il.offsetHeight, il.scrollHeight);
43931             // only adjust heights on other ones..
43932             mh = Math.max(h, mh);
43933             if (i < 1) {
43934                 
43935                 el.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
43936                 il.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
43937                
43938             }
43939             
43940             
43941         }, this);
43942         
43943         this.list.beginUpdate();
43944         this.list.setHeight(mh+this.list.getFrameWidth('tb')+this.assetHeight);
43945         this.list.alignTo(this.el, this.listAlign);
43946         this.list.endUpdate();
43947         
43948     },
43949      
43950     
43951     // -- store handlers..
43952     // private
43953     onBeforeLoad : function()
43954     {
43955         if(!this.hasFocus){
43956             return;
43957         }
43958         this.innerLists[0].update(this.loadingText ?
43959                '<div class="loading-indicator">'+this.loadingText+'</div>' : '');
43960         this.restrictHeight();
43961         this.selectedIndex = -1;
43962     },
43963     // private
43964     onLoad : function(a,b,c,d)
43965     {
43966         if (!this.loadingChildren) {
43967             // then we are loading the top level. - hide the children
43968             for (var i = 1;i < this.views.length; i++) {
43969                 this.views[i].getEl().setStyle({ display : 'none' });
43970             }
43971             var lw = Math.floor(
43972                 ((this.listWidth * this.maxColumns || Math.max(this.wrap.getWidth(), this.minListWidth)) - this.list.getFrameWidth('lr')) / this.maxColumns
43973             );
43974         
43975              this.list.setWidth(lw); // default to '1'
43976
43977             
43978         }
43979         if(!this.hasFocus){
43980             return;
43981         }
43982         
43983         if(this.store.getCount() > 0) {
43984             this.expand();
43985             this.restrictHeight();   
43986         } else {
43987             this.onEmptyResults();
43988         }
43989         
43990         if (!this.loadingChildren) {
43991             this.selectActive();
43992         }
43993         /*
43994         this.stores[1].loadData([]);
43995         this.stores[2].loadData([]);
43996         this.views
43997         */    
43998     
43999         //this.el.focus();
44000     },
44001     
44002     
44003     // private
44004     onLoadException : function()
44005     {
44006         this.collapse();
44007         Roo.log(this.store.reader.jsonData);
44008         if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
44009             Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
44010         }
44011         
44012         
44013     },
44014     // no cleaning of leading spaces on blur here.
44015     cleanLeadingSpace : function(e) { },
44016     
44017
44018     onSelectChange : function (view, sels, opts )
44019     {
44020         var ix = view.getSelectedIndexes();
44021          
44022         if (opts.list > this.maxColumns - 2) {
44023             if (view.store.getCount()<  1) {
44024                 this.views[opts.list ].getEl().setStyle({ display :   'none' });
44025
44026             } else  {
44027                 if (ix.length) {
44028                     // used to clear ?? but if we are loading unselected 
44029                     this.setFromData(view.store.getAt(ix[0]).data);
44030                 }
44031                 
44032             }
44033             
44034             return;
44035         }
44036         
44037         if (!ix.length) {
44038             // this get's fired when trigger opens..
44039            // this.setFromData({});
44040             var str = this.stores[opts.list+1];
44041             str.data.clear(); // removeall wihtout the fire events..
44042             return;
44043         }
44044         
44045         var rec = view.store.getAt(ix[0]);
44046          
44047         this.setFromData(rec.data);
44048         this.fireEvent('select', this, rec, ix[0]);
44049         
44050         var lw = Math.floor(
44051              (
44052                 (this.listWidth * this.maxColumns || Math.max(this.wrap.getWidth(), this.minListWidth)) - this.list.getFrameWidth('lr')
44053              ) / this.maxColumns
44054         );
44055         this.loadingChildren = true;
44056         this.stores[opts.list+1].loadDataFromChildren( rec );
44057         this.loadingChildren = false;
44058         var dl = this.stores[opts.list+1]. getTotalCount();
44059         
44060         this.views[opts.list+1].getEl().setHeight( this.innerLists[0].getHeight());
44061         
44062         this.views[opts.list+1].getEl().setStyle({ display : dl ? 'block' : 'none' });
44063         for (var i = opts.list+2; i < this.views.length;i++) {
44064             this.views[i].getEl().setStyle({ display : 'none' });
44065         }
44066         
44067         this.innerLists[opts.list+1].setHeight( this.innerLists[0].getHeight());
44068         this.list.setWidth(lw * (opts.list + (dl ? 2 : 1)));
44069         
44070         if (this.isLoading) {
44071            // this.selectActive(opts.list);
44072         }
44073          
44074     },
44075     
44076     
44077     
44078     
44079     onDoubleClick : function()
44080     {
44081         this.collapse(); //??
44082     },
44083     
44084      
44085     
44086     
44087     
44088     // private
44089     recordToStack : function(store, prop, value, stack)
44090     {
44091         var cstore = new Roo.data.SimpleStore({
44092             //fields : this.store.reader.meta.fields, // we need array reader.. for
44093             reader : this.store.reader,
44094             data : [ ]
44095         });
44096         var _this = this;
44097         var record  = false;
44098         var srec = false;
44099         if(store.getCount() < 1){
44100             return false;
44101         }
44102         store.each(function(r){
44103             if(r.data[prop] == value){
44104                 record = r;
44105             srec = r;
44106                 return false;
44107             }
44108             if (r.data.cn && r.data.cn.length) {
44109                 cstore.loadDataFromChildren( r);
44110                 var cret = _this.recordToStack(cstore, prop, value, stack);
44111                 if (cret !== false) {
44112                     record = cret;
44113                     srec = r;
44114                     return false;
44115                 }
44116             }
44117              
44118             return true;
44119         });
44120         if (record == false) {
44121             return false
44122         }
44123         stack.unshift(srec);
44124         return record;
44125     },
44126     
44127     /*
44128      * find the stack of stores that match our value.
44129      *
44130      * 
44131      */
44132     
44133     selectActive : function ()
44134     {
44135         // if store is not loaded, then we will need to wait for that to happen first.
44136         var stack = [];
44137         this.recordToStack(this.store, this.valueField, this.getValue(), stack);
44138         for (var i = 0; i < stack.length; i++ ) {
44139             this.views[i].select(stack[i].store.indexOf(stack[i]), false, false );
44140         }
44141         
44142     }
44143         
44144          
44145     
44146     
44147     
44148     
44149 });/*
44150  * Based on:
44151  * Ext JS Library 1.1.1
44152  * Copyright(c) 2006-2007, Ext JS, LLC.
44153  *
44154  * Originally Released Under LGPL - original licence link has changed is not relivant.
44155  *
44156  * Fork - LGPL
44157  * <script type="text/javascript">
44158  */
44159 /**
44160  * @class Roo.form.Checkbox
44161  * @extends Roo.form.Field
44162  * Single checkbox field.  Can be used as a direct replacement for traditional checkbox fields.
44163  * @constructor
44164  * Creates a new Checkbox
44165  * @param {Object} config Configuration options
44166  */
44167 Roo.form.Checkbox = function(config){
44168     Roo.form.Checkbox.superclass.constructor.call(this, config);
44169     this.addEvents({
44170         /**
44171          * @event check
44172          * Fires when the checkbox is checked or unchecked.
44173              * @param {Roo.form.Checkbox} this This checkbox
44174              * @param {Boolean} checked The new checked value
44175              */
44176         check : true
44177     });
44178 };
44179
44180 Roo.extend(Roo.form.Checkbox, Roo.form.Field,  {
44181     /**
44182      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
44183      */
44184     focusClass : undefined,
44185     /**
44186      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
44187      */
44188     fieldClass: "x-form-field",
44189     /**
44190      * @cfg {Boolean} checked True if the the checkbox should render already checked (defaults to false)
44191      */
44192     checked: false,
44193     /**
44194      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
44195      * {tag: "input", type: "checkbox", autocomplete: "off"})
44196      */
44197     defaultAutoCreate : { tag: "input", type: 'hidden', autocomplete: "off"},
44198     /**
44199      * @cfg {String} boxLabel The text that appears beside the checkbox
44200      */
44201     boxLabel : "",
44202     /**
44203      * @cfg {String} inputValue The value that should go into the generated input element's value attribute
44204      */  
44205     inputValue : '1',
44206     /**
44207      * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
44208      */
44209      valueOff: '0', // value when not checked..
44210
44211     actionMode : 'viewEl', 
44212     //
44213     // private
44214     itemCls : 'x-menu-check-item x-form-item',
44215     groupClass : 'x-menu-group-item',
44216     inputType : 'hidden',
44217     
44218     
44219     inSetChecked: false, // check that we are not calling self...
44220     
44221     inputElement: false, // real input element?
44222     basedOn: false, // ????
44223     
44224     isFormField: true, // not sure where this is needed!!!!
44225
44226     onResize : function(){
44227         Roo.form.Checkbox.superclass.onResize.apply(this, arguments);
44228         if(!this.boxLabel){
44229             this.el.alignTo(this.wrap, 'c-c');
44230         }
44231     },
44232
44233     initEvents : function(){
44234         Roo.form.Checkbox.superclass.initEvents.call(this);
44235         this.el.on("click", this.onClick,  this);
44236         this.el.on("change", this.onClick,  this);
44237     },
44238
44239
44240     getResizeEl : function(){
44241         return this.wrap;
44242     },
44243
44244     getPositionEl : function(){
44245         return this.wrap;
44246     },
44247
44248     // private
44249     onRender : function(ct, position){
44250         Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
44251         /*
44252         if(this.inputValue !== undefined){
44253             this.el.dom.value = this.inputValue;
44254         }
44255         */
44256         //this.wrap = this.el.wrap({cls: "x-form-check-wrap"});
44257         this.wrap = this.el.wrap({cls: 'x-menu-check-item '});
44258         var viewEl = this.wrap.createChild({ 
44259             tag: 'img', cls: 'x-menu-item-icon', style: 'margin: 0px;' ,src : Roo.BLANK_IMAGE_URL });
44260         this.viewEl = viewEl;   
44261         this.wrap.on('click', this.onClick,  this); 
44262         
44263         this.el.on('DOMAttrModified', this.setFromHidden,  this); //ff
44264         this.el.on('propertychange', this.setFromHidden,  this);  //ie
44265         
44266         
44267         
44268         if(this.boxLabel){
44269             this.wrap.createChild({tag: 'label', htmlFor: this.el.id, cls: 'x-form-cb-label', html: this.boxLabel});
44270         //    viewEl.on('click', this.onClick,  this); 
44271         }
44272         //if(this.checked){
44273             this.setChecked(this.checked);
44274         //}else{
44275             //this.checked = this.el.dom;
44276         //}
44277
44278     },
44279
44280     // private
44281     initValue : Roo.emptyFn,
44282
44283     /**
44284      * Returns the checked state of the checkbox.
44285      * @return {Boolean} True if checked, else false
44286      */
44287     getValue : function(){
44288         if(this.el){
44289             return String(this.el.dom.value) == String(this.inputValue ) ? this.inputValue : this.valueOff;
44290         }
44291         return this.valueOff;
44292         
44293     },
44294
44295         // private
44296     onClick : function(){ 
44297         if (this.disabled) {
44298             return;
44299         }
44300         this.setChecked(!this.checked);
44301
44302         //if(this.el.dom.checked != this.checked){
44303         //    this.setValue(this.el.dom.checked);
44304        // }
44305     },
44306
44307     /**
44308      * Sets the checked state of the checkbox.
44309      * On is always based on a string comparison between inputValue and the param.
44310      * @param {Boolean/String} value - the value to set 
44311      * @param {Boolean/String} suppressEvent - whether to suppress the checkchange event.
44312      */
44313     setValue : function(v,suppressEvent){
44314         
44315         
44316         //this.checked = (v === true || v === 'true' || v == '1' || String(v).toLowerCase() == 'on');
44317         //if(this.el && this.el.dom){
44318         //    this.el.dom.checked = this.checked;
44319         //    this.el.dom.defaultChecked = this.checked;
44320         //}
44321         this.setChecked(String(v) === String(this.inputValue), suppressEvent);
44322         //this.fireEvent("check", this, this.checked);
44323     },
44324     // private..
44325     setChecked : function(state,suppressEvent)
44326     {
44327         if (this.inSetChecked) {
44328             this.checked = state;
44329             return;
44330         }
44331         
44332     
44333         if(this.wrap){
44334             this.wrap[state ? 'addClass' : 'removeClass']('x-menu-item-checked');
44335         }
44336         this.checked = state;
44337         if(suppressEvent !== true){
44338             this.fireEvent('check', this, state);
44339         }
44340         this.inSetChecked = true;
44341         this.el.dom.value = state ? this.inputValue : this.valueOff;
44342         this.inSetChecked = false;
44343         
44344     },
44345     // handle setting of hidden value by some other method!!?!?
44346     setFromHidden: function()
44347     {
44348         if(!this.el){
44349             return;
44350         }
44351         //console.log("SET FROM HIDDEN");
44352         //alert('setFrom hidden');
44353         this.setValue(this.el.dom.value);
44354     },
44355     
44356     onDestroy : function()
44357     {
44358         if(this.viewEl){
44359             Roo.get(this.viewEl).remove();
44360         }
44361          
44362         Roo.form.Checkbox.superclass.onDestroy.call(this);
44363     },
44364     
44365     setBoxLabel : function(str)
44366     {
44367         this.wrap.select('.x-form-cb-label', true).first().dom.innerHTML = str;
44368     }
44369
44370 });/*
44371  * Based on:
44372  * Ext JS Library 1.1.1
44373  * Copyright(c) 2006-2007, Ext JS, LLC.
44374  *
44375  * Originally Released Under LGPL - original licence link has changed is not relivant.
44376  *
44377  * Fork - LGPL
44378  * <script type="text/javascript">
44379  */
44380  
44381 /**
44382  * @class Roo.form.Radio
44383  * @extends Roo.form.Checkbox
44384  * Single radio field.  Same as Checkbox, but provided as a convenience for automatically setting the input type.
44385  * Radio grouping is handled automatically by the browser if you give each radio in a group the same name.
44386  * @constructor
44387  * Creates a new Radio
44388  * @param {Object} config Configuration options
44389  */
44390 Roo.form.Radio = function(){
44391     Roo.form.Radio.superclass.constructor.apply(this, arguments);
44392 };
44393 Roo.extend(Roo.form.Radio, Roo.form.Checkbox, {
44394     inputType: 'radio',
44395
44396     /**
44397      * If this radio is part of a group, it will return the selected value
44398      * @return {String}
44399      */
44400     getGroupValue : function(){
44401         return this.el.up('form').child('input[name='+this.el.dom.name+']:checked', true).value;
44402     },
44403     
44404     
44405     onRender : function(ct, position){
44406         Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
44407         
44408         if(this.inputValue !== undefined){
44409             this.el.dom.value = this.inputValue;
44410         }
44411          
44412         this.wrap = this.el.wrap({cls: "x-form-check-wrap"});
44413         //this.wrap = this.el.wrap({cls: 'x-menu-check-item '});
44414         //var viewEl = this.wrap.createChild({ 
44415         //    tag: 'img', cls: 'x-menu-item-icon', style: 'margin: 0px;' ,src : Roo.BLANK_IMAGE_URL });
44416         //this.viewEl = viewEl;   
44417         //this.wrap.on('click', this.onClick,  this); 
44418         
44419         //this.el.on('DOMAttrModified', this.setFromHidden,  this); //ff
44420         //this.el.on('propertychange', this.setFromHidden,  this);  //ie
44421         
44422         
44423         
44424         if(this.boxLabel){
44425             this.wrap.createChild({tag: 'label', htmlFor: this.el.id, cls: 'x-form-cb-label', html: this.boxLabel});
44426         //    viewEl.on('click', this.onClick,  this); 
44427         }
44428          if(this.checked){
44429             this.el.dom.checked =   'checked' ;
44430         }
44431          
44432     } 
44433     
44434     
44435 });Roo.htmleditor = {}; 
44436 /**
44437  * @class Roo.htmleditor.Filter
44438  * Base Class for filtering htmleditor stuff. - do not use this directly - extend it.
44439  * @cfg {DomElement} node The node to iterate and filter
44440  * @cfg {boolean|String|Array} tag Tags to replace 
44441  * @constructor
44442  * Create a new Filter.
44443  * @param {Object} config Configuration options
44444  */
44445
44446
44447
44448 Roo.htmleditor.Filter = function(cfg) {
44449     Roo.apply(this.cfg);
44450     // this does not actually call walk as it's really just a abstract class
44451 }
44452
44453
44454 Roo.htmleditor.Filter.prototype = {
44455     
44456     node: false,
44457     
44458     tag: false,
44459
44460     // overrride to do replace comments.
44461     replaceComment : false,
44462     
44463     // overrride to do replace or do stuff with tags..
44464     replaceTag : false,
44465     
44466     walk : function(dom)
44467     {
44468         Roo.each( Array.from(dom.childNodes), function( e ) {
44469             switch(true) {
44470                 
44471                 case e.nodeType == 8 && typeof(this.replaceComment) != 'undefined': // comment
44472                     this.replaceComment(e);
44473                     return;
44474                 
44475                 case e.nodeType != 1: //not a node.
44476                     return;
44477                 
44478                 case this.tag === true: // everything
44479                 case typeof(this.tag) == 'object' && this.tag.indexOf(e.tagName) > -1: // array and it matches.
44480                 case typeof(this.tag) == 'string' && this.tag == e.tagName: // array and it matches.
44481                     if (this.replaceTag && false === this.replaceTag(e)) {
44482                         return;
44483                     }
44484                     if (e.hasChildNodes()) {
44485                         this.walk(e);
44486                     }
44487                     return;
44488                 
44489                 default:    // tags .. that do not match.
44490                     if (e.hasChildNodes()) {
44491                         this.walk(e);
44492                     }
44493             }
44494             
44495         }, this);
44496         
44497     }
44498 }; 
44499
44500 /**
44501  * @class Roo.htmleditor.FilterAttributes
44502  * clean attributes and  styles including http:// etc.. in attribute
44503  * @constructor
44504 * Run a new Attribute Filter
44505 * @param {Object} config Configuration options
44506  */
44507 Roo.htmleditor.FilterAttributes = function(cfg)
44508 {
44509     Roo.apply(this, cfg);
44510     this.attrib_black = this.attrib_black || [];
44511     this.attrib_clean = this.attrib_clean || [];
44512     this.style_white = this.style_white || [];
44513     this.style_black = this.style_black || [];
44514     this.walk(cfg.node);
44515 }
44516
44517 Roo.extend(Roo.htmleditor.FilterAttributes, Roo.htmleditor.Filter,
44518 {
44519     tag: true, // all tags
44520     
44521     attrib_black : false, // array
44522     attrib_clean : false,
44523     style_white : false,
44524     style_black : false,
44525      
44526      
44527     replaceTag : function(node)
44528     {
44529         if (!node.attributes || !node.attributes.length) {
44530             return true;
44531         }
44532         
44533         for (var i = node.attributes.length-1; i > -1 ; i--) {
44534             var a = node.attributes[i];
44535             //console.log(a);
44536             
44537             if (a.name.toLowerCase().substr(0,2)=='on')  {
44538                 node.removeAttribute(a.name);
44539                 continue;
44540             }
44541             
44542             
44543             if (this.attrib_black.indexOf(a.name.toLowerCase()) > -1) {
44544                 node.removeAttribute(a.name);
44545                 continue;
44546             }
44547             if (this.attrib_clean.indexOf(a.name.toLowerCase()) > -1) {
44548                 this.cleanAttr(node,a.name,a.value); // fixme..
44549                 continue;
44550             }
44551             if (a.name == 'style') {
44552                 this.cleanStyle(node,a.name,a.value);
44553                 continue;
44554             }
44555             /// clean up MS crap..
44556             // tecnically this should be a list of valid class'es..
44557             
44558             
44559             if (a.name == 'class') {
44560                 if (a.value.match(/^Mso/)) {
44561                     node.removeAttribute('class');
44562                 }
44563                 
44564                 if (a.value.match(/^body$/)) {
44565                     node.removeAttribute('class');
44566                 }
44567                 continue;
44568             }
44569             
44570             
44571             // style cleanup!?
44572             // class cleanup?
44573             
44574         }
44575         return true; // clean children
44576     },
44577         
44578     cleanAttr: function(node, n,v)
44579     {
44580         
44581         if (v.match(/^\./) || v.match(/^\//)) {
44582             return;
44583         }
44584         if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/) || v.match(/^ftp:/)) {
44585             return;
44586         }
44587         if (v.match(/^#/)) {
44588             return;
44589         }
44590         if (v.match(/^\{/)) { // allow template editing.
44591             return;
44592         }
44593 //            Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
44594         node.removeAttribute(n);
44595         
44596     },
44597     cleanStyle : function(node,  n,v)
44598     {
44599         if (v.match(/expression/)) { //XSS?? should we even bother..
44600             node.removeAttribute(n);
44601             return;
44602         }
44603         
44604         var parts = v.split(/;/);
44605         var clean = [];
44606         
44607         Roo.each(parts, function(p) {
44608             p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
44609             if (!p.length) {
44610                 return true;
44611             }
44612             var l = p.split(':').shift().replace(/\s+/g,'');
44613             l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
44614             
44615             if ( this.style_black.length && (this.style_black.indexOf(l) > -1 || this.style_black.indexOf(l.toLowerCase()) > -1)) {
44616                 return true;
44617             }
44618             //Roo.log()
44619             // only allow 'c whitelisted system attributes'
44620             if ( this.style_white.length &&  style_white.indexOf(l) < 0 && style_white.indexOf(l.toLowerCase()) < 0 ) {
44621                 return true;
44622             }
44623             
44624             
44625             clean.push(p);
44626             return true;
44627         },this);
44628         if (clean.length) { 
44629             node.setAttribute(n, clean.join(';'));
44630         } else {
44631             node.removeAttribute(n);
44632         }
44633         
44634     }
44635         
44636         
44637         
44638     
44639 });/**
44640  * @class Roo.htmleditor.FilterBlack
44641  * remove blacklisted elements.
44642  * @constructor
44643  * Run a new Blacklisted Filter
44644  * @param {Object} config Configuration options
44645  */
44646
44647 Roo.htmleditor.FilterBlack = function(cfg)
44648 {
44649     Roo.apply(this, cfg);
44650     this.walk(cfg.node);
44651 }
44652
44653 Roo.extend(Roo.htmleditor.FilterBlack, Roo.htmleditor.Filter,
44654 {
44655     tag : true, // all elements.
44656    
44657     replace : function(n)
44658     {
44659         n.parentNode.removeChild(n);
44660     }
44661 });
44662 /**
44663  * @class Roo.htmleditor.FilterComment
44664  * remove comments.
44665  * @constructor
44666 * Run a new Comments Filter
44667 * @param {Object} config Configuration options
44668  */
44669 Roo.htmleditor.FilterComment = function(cfg)
44670 {
44671     this.walk(cfg.node);
44672 }
44673
44674 Roo.extend(Roo.htmleditor.FilterComment, Roo.htmleditor.Filter,
44675 {
44676   
44677     replaceComment : function(n)
44678     {
44679         n.parentNode.removeChild(n);
44680     }
44681 });/**
44682  * @class Roo.htmleditor.FilterKeepChildren
44683  * remove tags but keep children
44684  * @constructor
44685  * Run a new Keep Children Filter
44686  * @param {Object} config Configuration options
44687  */
44688
44689 Roo.htmleditor.FilterKeepChildren = function(cfg)
44690 {
44691     Roo.apply(this, cfg);
44692     this.walk(cfg.node);
44693 }
44694
44695 Roo.extend(Roo.htmleditor.FilterKeepChildren, Roo.htmleditor.FilterBlack,
44696 {
44697     
44698   
44699     replaceTag : function(node)
44700     {
44701         // walk children...
44702         var ar = Array.from(node.childNodes);
44703         for (var i = 0; i < ar.length; i++) {
44704             node.removeChild(ar[i]);
44705             // what if we need to walk these???
44706             node.parentNode.insertBefore(ar[i], node);
44707             this.walk(ar[i]);
44708         }
44709         node.parentNode.removeChild(node);
44710         return false; // don't walk children
44711         
44712         
44713     }
44714 });/**
44715  * @class Roo.htmleditor.FilterParagraph
44716  * paragraphs cause a nightmare for shared content - this filter is designed to be called ? at various points when editing
44717  * like on 'push' to remove the <p> tags and replace them with line breaks.
44718  * @constructor
44719  * Run a new Paragraph Filter
44720  * @param {Object} config Configuration options
44721  */
44722
44723 Roo.htmleditor.FilterParagraph = function(cfg)
44724 {
44725     // no need to apply config.
44726     this.walk(cfg.node);
44727 }
44728
44729 Roo.extend(Roo.htmleditor.FilterParagraph, Roo.htmleditor.Filter,
44730 {
44731     
44732      
44733     tag : 'P',
44734     
44735      
44736     replaceTag : function(node)
44737     {
44738         
44739         if (node.childNodes.length == 1 &&
44740             node.childNodes[0].nodeType == 3 &&
44741             node.childNodes[0].textContent.trim().length < 1
44742             ) {
44743             // remove and replace with '<BR>';
44744             node.parentNode.replaceChild(node.ownerDocument.createElement('BR'),node);
44745             return false; // no need to walk..
44746         }
44747         var ar = Array.from(node.childNodes);
44748         for (var i = 0; i < ar.length; i++) {
44749             node.removeChild(ar[i]);
44750             // what if we need to walk these???
44751             node.parentNode.insertBefore(ar[i], node);
44752         }
44753         // now what about this?
44754         // <p> &nbsp; </p>
44755         
44756         // double BR.
44757         node.parentNode.insertBefore(node.ownerDocument.createElement('BR'), node);
44758         node.parentNode.removeChild(node);
44759         
44760         return false;
44761
44762     }
44763     
44764 });/**
44765  * @class Roo.htmleditor.FilterSpan
44766  * filter span's with no attributes out..
44767  * @constructor
44768  * Run a new Span Filter
44769  * @param {Object} config Configuration options
44770  */
44771
44772 Roo.htmleditor.FilterSpan = function(cfg)
44773 {
44774     // no need to apply config.
44775     this.walk(cfg.node);
44776 }
44777
44778 Roo.extend(Roo.htmleditor.FilterSpan, Roo.htmleditor.FilterKeepChildren,
44779 {
44780      
44781     tag : 'SPAN',
44782      
44783  
44784     replaceTag : function(node)
44785     {
44786         if (node.attributes && node.attributes.length > 0) {
44787             return true; // walk if there are any.
44788         }
44789         Roo.htmleditor.FilterKeepChildren.prototype.replaceTag.call(this, node);
44790         return false;
44791      
44792     }
44793     
44794 });/**
44795  * @class Roo.htmleditor.FilterTableWidth
44796   try and remove table width data - as that frequently messes up other stuff.
44797  * 
44798  *      was cleanTableWidths.
44799  *
44800  * Quite often pasting from word etc.. results in tables with column and widths.
44801  * This does not work well on fluid HTML layouts - like emails. - so this code should hunt an destroy them..
44802  *
44803  * @constructor
44804  * Run a new Table Filter
44805  * @param {Object} config Configuration options
44806  */
44807
44808 Roo.htmleditor.FilterTableWidth = function(cfg)
44809 {
44810     // no need to apply config.
44811     this.tag = ['TABLE', 'TD', 'TR', 'TH', 'THEAD', 'TBODY' ];
44812     this.walk(cfg.node);
44813 }
44814
44815 Roo.extend(Roo.htmleditor.FilterTableWidth, Roo.htmleditor.Filter,
44816 {
44817      
44818      
44819     
44820     replaceTag: function(node) {
44821         
44822         
44823       
44824         if (node.hasAttribute('width')) {
44825             node.removeAttribute('width');
44826         }
44827         
44828          
44829         if (node.hasAttribute("style")) {
44830             // pretty basic...
44831             
44832             var styles = node.getAttribute("style").split(";");
44833             var nstyle = [];
44834             Roo.each(styles, function(s) {
44835                 if (!s.match(/:/)) {
44836                     return;
44837                 }
44838                 var kv = s.split(":");
44839                 if (kv[0].match(/^\s*(width|min-width)\s*$/)) {
44840                     return;
44841                 }
44842                 // what ever is left... we allow.
44843                 nstyle.push(s);
44844             });
44845             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
44846             if (!nstyle.length) {
44847                 node.removeAttribute('style');
44848             }
44849         }
44850         
44851         return true; // continue doing children..
44852     }
44853 });/**
44854  * @class Roo.htmleditor.FilterWord
44855  * try and clean up all the mess that Word generates.
44856  * 
44857  * This is the 'nice version' - see 'Heavy' that white lists a very short list of elements, and multi-filters 
44858  
44859  * @constructor
44860  * Run a new Span Filter
44861  * @param {Object} config Configuration options
44862  */
44863
44864 Roo.htmleditor.FilterWord = function(cfg)
44865 {
44866     // no need to apply config.
44867     this.walk(cfg.node);
44868 }
44869
44870 Roo.extend(Roo.htmleditor.FilterWord, Roo.htmleditor.Filter,
44871 {
44872     tag: true,
44873      
44874     
44875     /**
44876      * Clean up MS wordisms...
44877      */
44878     replaceTag : function(node)
44879     {
44880          
44881         // no idea what this does - span with text, replaceds with just text.
44882         if(
44883                 node.nodeName == 'SPAN' &&
44884                 !node.hasAttributes() &&
44885                 node.childNodes.length == 1 &&
44886                 node.firstChild.nodeName == "#text"  
44887         ) {
44888             var textNode = node.firstChild;
44889             node.removeChild(textNode);
44890             if (node.getAttribute('lang') != 'zh-CN') {   // do not space pad on chinese characters..
44891                 node.parentNode.insertBefore(node.ownerDocument.createTextNode(" "), node);
44892             }
44893             node.parentNode.insertBefore(textNode, node);
44894             if (node.getAttribute('lang') != 'zh-CN') {   // do not space pad on chinese characters..
44895                 node.parentNode.insertBefore(node.ownerDocument.createTextNode(" ") , node);
44896             }
44897             
44898             node.parentNode.removeChild(node);
44899             return false; // dont do chidren - we have remove our node - so no need to do chdhilren?
44900         }
44901         
44902    
44903         
44904         if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
44905             node.parentNode.removeChild(node);
44906             return false; // dont do chidlren
44907         }
44908         //Roo.log(node.tagName);
44909         // remove - but keep children..
44910         if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|v:|font)/)) {
44911             //Roo.log('-- removed');
44912             while (node.childNodes.length) {
44913                 var cn = node.childNodes[0];
44914                 node.removeChild(cn);
44915                 node.parentNode.insertBefore(cn, node);
44916                 // move node to parent - and clean it..
44917                 this.replaceTag(cn);
44918             }
44919             node.parentNode.removeChild(node);
44920             /// no need to iterate chidlren = it's got none..
44921             //this.iterateChildren(node, this.cleanWord);
44922             return false; // no need to iterate children.
44923         }
44924         // clean styles
44925         if (node.className.length) {
44926             
44927             var cn = node.className.split(/\W+/);
44928             var cna = [];
44929             Roo.each(cn, function(cls) {
44930                 if (cls.match(/Mso[a-zA-Z]+/)) {
44931                     return;
44932                 }
44933                 cna.push(cls);
44934             });
44935             node.className = cna.length ? cna.join(' ') : '';
44936             if (!cna.length) {
44937                 node.removeAttribute("class");
44938             }
44939         }
44940         
44941         if (node.hasAttribute("lang")) {
44942             node.removeAttribute("lang");
44943         }
44944         
44945         if (node.hasAttribute("style")) {
44946             
44947             var styles = node.getAttribute("style").split(";");
44948             var nstyle = [];
44949             Roo.each(styles, function(s) {
44950                 if (!s.match(/:/)) {
44951                     return;
44952                 }
44953                 var kv = s.split(":");
44954                 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
44955                     return;
44956                 }
44957                 // what ever is left... we allow.
44958                 nstyle.push(s);
44959             });
44960             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
44961             if (!nstyle.length) {
44962                 node.removeAttribute('style');
44963             }
44964         }
44965         return true; // do children
44966         
44967         
44968         
44969     }
44970 });
44971 /**
44972  * @class Roo.htmleditor.Tidy
44973  * Tidy HTML 
44974  * @cfg {Roo.HtmlEditorCore} core the editor.
44975  * @constructor
44976  * Create a new Filter.
44977  * @param {Object} config Configuration options
44978  */
44979
44980
44981 Roo.htmleditor.Tidy = function(cfg) {
44982     Roo.apply(this, cfg);
44983     
44984     this.core.doc.body.innerHTML = this.tidy(this.core.doc.body, '');
44985      
44986 }
44987
44988 Roo.htmleditor.Tidy.toString = function(node)
44989 {
44990     return Roo.htmleditor.Tidy.prototype.tidy(node, '');
44991 }
44992
44993 Roo.htmleditor.Tidy.prototype = {
44994     
44995     
44996     wrap : function(s) {
44997         return s.replace(/\n/g, " ").replace(/(?![^\n]{1,80}$)([^\n]{1,80})\s/g, '$1\n');
44998     },
44999
45000     
45001     tidy : function(node, indent) {
45002      
45003         if  (node.nodeType == 3) {
45004             // text.
45005             
45006             
45007             return indent === false ? node.nodeValue : this.wrap(node.nodeValue.trim()).split("\n").join("\n" + indent);
45008                 
45009             
45010         }
45011         
45012         if  (node.nodeType != 1) {
45013             return '';
45014         }
45015         
45016         
45017         
45018         if (node.tagName == 'BODY') {
45019             
45020             return this.cn(node, '');
45021         }
45022              
45023              // Prints the node tagName, such as <A>, <IMG>, etc
45024         var ret = "<" + node.tagName +  this.attr(node) ;
45025         
45026         // elements with no children..
45027         if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(node.tagName) > -1) {
45028                 return ret + '/>';
45029         }
45030         ret += '>';
45031         
45032         
45033         var cindent = indent === false ? '' : (indent + '  ');
45034         // tags where we will not pad the children.. (inline text tags etc..)
45035         if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN', 'B', 'I', 'S'].indexOf(node.tagName) > -1) { // or code?
45036             cindent = false;
45037             
45038             
45039         }
45040         
45041         var cn = this.cn(node, cindent );
45042         
45043         return ret + cn  + '</' + node.tagName + '>';
45044         
45045     },
45046     cn: function(node, indent)
45047     {
45048         var ret = [];
45049         
45050         var ar = Array.from(node.childNodes);
45051         for (var i = 0 ; i < ar.length ; i++) {
45052             
45053             
45054             
45055             if (indent !== false   // indent==false preservies everything
45056                 && i > 0
45057                 && ar[i].nodeType == 3 
45058                 && ar[i].nodeValue.length > 0
45059                 && ar[i].nodeValue.match(/^\s+/)
45060             ) {
45061                 if (ret.length && ret[ret.length-1] == "\n" + indent) {
45062                     ret.pop(); // remove line break from last?
45063                 }
45064                 
45065                 ret.push(" "); // add a space if i'm a text item with a space at the front, as tidy will strip spaces.
45066             }
45067             if (indent !== false
45068                 && ar[i].nodeType == 1 // element - and indent is not set... 
45069             ) {
45070                 ret.push("\n" + indent); 
45071             }
45072             
45073             ret.push(this.tidy(ar[i], indent));
45074             // text + trailing indent 
45075             if (indent !== false
45076                 && ar[i].nodeType == 3
45077                 && ar[i].nodeValue.length > 0
45078                 && ar[i].nodeValue.match(/\s+$/)
45079             ){
45080                 ret.push("\n" + indent); 
45081             }
45082             
45083             
45084             
45085             
45086         }
45087         // what if all text?
45088         
45089         
45090         return ret.join('');
45091     },
45092     
45093          
45094         
45095     attr : function(node)
45096     {
45097         var attr = [];
45098         for(i = 0; i < node.attributes.length;i++) {
45099             
45100             // skip empty values?
45101             if (!node.attributes.item(i).value.length) {
45102                 continue;
45103             }
45104             attr.push(  node.attributes.item(i).name + '="' +
45105                     Roo.util.Format.htmlEncode(node.attributes.item(i).value) + '"'
45106             );
45107         }
45108         return attr.length ? (' ' + attr.join(' ') ) : '';
45109         
45110     }
45111     
45112     
45113     
45114 }
45115 /**
45116  * @class Roo.htmleditor.KeyEnter
45117  * Handle Enter press..
45118  * @cfg {Roo.HtmlEditorCore} core the editor.
45119  * @constructor
45120  * Create a new Filter.
45121  * @param {Object} config Configuration options
45122  */
45123
45124
45125
45126 Roo.htmleditor.KeyEnter = function(cfg) {
45127     Roo.apply(this, cfg);
45128     // this does not actually call walk as it's really just a abstract class
45129  
45130     Roo.get(this.core.doc.body).on('keypress', this.keypress, this);
45131 }
45132
45133
45134 Roo.htmleditor.KeyEnter.prototype = {
45135     
45136     core : false,
45137     
45138     keypress : function(e) {
45139         if (e.charCode != 13) {
45140             return true;
45141         }
45142         e.preventDefault();
45143         // https://stackoverflow.com/questions/18552336/prevent-contenteditable-adding-div-on-enter-chrome
45144         var doc = this.core.doc;
45145         
45146         var docFragment = doc.createDocumentFragment();
45147     
45148         //add a new line
45149         var newEle = doc.createTextNode('\n');
45150         docFragment.appendChild(newEle);
45151     
45152         //add the br, or p, or something else
45153         newEle = doc.createElement('br');
45154         docFragment.appendChild(newEle);
45155     
45156         //make the br replace selection
45157         var range = this.core.win.getSelection().getRangeAt(0);
45158         range.deleteContents();
45159         range.insertNode(docFragment);
45160     
45161         //create a new range
45162         range = doc.createRange();
45163         range.setStartAfter(newEle);
45164         range.collapse(true);
45165     
45166         //make the cursor there
45167         var sel = this.core.win.getSelection();
45168         sel.removeAllRanges();
45169         sel.addRange(range);
45170     
45171         return false;
45172          
45173     }
45174 };
45175     /**
45176  *  
45177  * <figure data-block="BlockFigure" contenteditable="false" role="group" style="text-align:left">' + 
45178         <img data-name="image" src="{SRC}">' + 
45179         <figcaption data-name="caption" contenteditable="true" style="text-align:left">XXXX</figcaption>
45180     </figure>
45181     <br/>
45182     
45183     usage:
45184      -- add to document..
45185     new Roo.htmleditor.BlockFigure{
45186         image_src : 'http://www.google.com',
45187         caption : 'test',
45188     }
45189      -- load document, and search for elements of this...
45190     Roo.DomQuery.select('*[data-block])
45191     // loop each and call ctor ({node : xxx})
45192     -- html editor click
45193     ** see if parent has Element.findParent(*[data-block]);
45194     use ?? to 
45195     
45196  */
45197
45198 Roo.htmleditor.BlockFigure = function(cfg)
45199 {
45200     if (cfg.node) {
45201         this.readElement(cfg.node);
45202         this.updateElement(cfg.node);
45203     }
45204     Roo.apply(this, cfg);
45205 }
45206
45207 Roo.htmleditor.BlockFigure.prototype = {
45208     
45209     // setable values.
45210     image_src: '',
45211     
45212     align: 'left',
45213     caption : '',
45214     text_align: 'left',
45215     
45216     image_width : '',
45217     image_height : '',
45218     
45219     // used by context menu
45220     
45221     context : { // ?? static really
45222         image_width : {
45223             title: "Width",
45224             width: 40
45225         },
45226         image_height:  {
45227             title: "Height",
45228             width: 40
45229         },
45230         align: {
45231             title: "Align",
45232             opts : [ [""],[ "left"],[ "right"],[ "center"],[ "top"]],
45233             width : 80
45234             
45235         },
45236         text_align: {
45237             title: "Caption Align",
45238             opts : [ [""],[ "left"],[ "right"],[ "center"],[ "top"]],
45239             width : 80
45240         },
45241         
45242        
45243         image_src : {
45244             title: "Src",
45245             width: 220
45246         }
45247     },
45248     /**
45249      * create a DomHelper friendly object - for use with
45250      * Roo.DomHelper.markup / overwrite / etc..
45251      */
45252     toObject : function()
45253     {
45254         
45255         var img = {
45256             tag : 'img',
45257             src : this.image_src,
45258             alt : this.caption 
45259         };
45260         if ((''+this.image_width).length) {
45261             img.width = this.image_width;
45262         }
45263         if ((''+ this.height).length) {
45264             img.height = this.image_height;
45265         }
45266         return {
45267             tag: 'figure',
45268             'data-block' : 'Figure',
45269             contenteditable : 'false',
45270             style : 'text-align:' + this.align,
45271             cn : [
45272                 img,
45273                 {
45274                     tag: 'figcaption',
45275                     contenteditable : true,
45276                     style : 'text-align:left',
45277                     html : this.caption 
45278                 }
45279             ]
45280         };
45281     },
45282     
45283     readElement : function(node)
45284     {
45285         this.image_src = this.getVal(node, 'img', 'src');
45286         this.align = this.getVal(node, 'figure', 'style', 'text-align');
45287         this.caption = this.getVal(node, 'figcaption', 'html');
45288         this.text_align = this.getVal(node, 'figcaption', 'style','text-align');
45289     },
45290     
45291     updateElement : function(node)
45292     {
45293         Roo.DomHelper.overwrite(node, this.toObject());
45294     },
45295     /**
45296      * convert to plain HTML for calling insertAtCursor..
45297      */
45298     toHTML : function()
45299     {
45300         return Roo.DomHelper.markup(this.toObject());
45301     },
45302     
45303     getVal : function(node, tag, attr, style)
45304     {
45305         var n = node;
45306         if (n.tagName != tag.toUpperCase()) {
45307             // in theory we could do figure[3] << 3rd figure? or some more complex search..?
45308             // but kiss for now.
45309             n = node.getElementsByTagName(tag).item(0);
45310         }
45311         if (attr == 'html') {
45312             return n.innerHTML;
45313         }
45314         if (attr == 'style') {
45315             return Roo.get(n).getStyle(style);
45316         }
45317         
45318         return Roo.get(n).attr(attr);
45319             
45320     }
45321     
45322     
45323     
45324     
45325     
45326     
45327 }
45328
45329 //<script type="text/javascript">
45330
45331 /*
45332  * Based  Ext JS Library 1.1.1
45333  * Copyright(c) 2006-2007, Ext JS, LLC.
45334  * LGPL
45335  *
45336  */
45337  
45338 /**
45339  * @class Roo.HtmlEditorCore
45340  * @extends Roo.Component
45341  * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
45342  *
45343  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
45344  */
45345
45346 Roo.HtmlEditorCore = function(config){
45347     
45348     
45349     Roo.HtmlEditorCore.superclass.constructor.call(this, config);
45350     
45351     
45352     this.addEvents({
45353         /**
45354          * @event initialize
45355          * Fires when the editor is fully initialized (including the iframe)
45356          * @param {Roo.HtmlEditorCore} this
45357          */
45358         initialize: true,
45359         /**
45360          * @event activate
45361          * Fires when the editor is first receives the focus. Any insertion must wait
45362          * until after this event.
45363          * @param {Roo.HtmlEditorCore} this
45364          */
45365         activate: true,
45366          /**
45367          * @event beforesync
45368          * Fires before the textarea is updated with content from the editor iframe. Return false
45369          * to cancel the sync.
45370          * @param {Roo.HtmlEditorCore} this
45371          * @param {String} html
45372          */
45373         beforesync: true,
45374          /**
45375          * @event beforepush
45376          * Fires before the iframe editor is updated with content from the textarea. Return false
45377          * to cancel the push.
45378          * @param {Roo.HtmlEditorCore} this
45379          * @param {String} html
45380          */
45381         beforepush: true,
45382          /**
45383          * @event sync
45384          * Fires when the textarea is updated with content from the editor iframe.
45385          * @param {Roo.HtmlEditorCore} this
45386          * @param {String} html
45387          */
45388         sync: true,
45389          /**
45390          * @event push
45391          * Fires when the iframe editor is updated with content from the textarea.
45392          * @param {Roo.HtmlEditorCore} this
45393          * @param {String} html
45394          */
45395         push: true,
45396         
45397         /**
45398          * @event editorevent
45399          * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
45400          * @param {Roo.HtmlEditorCore} this
45401          */
45402         editorevent: true
45403         
45404     });
45405     
45406     // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
45407     
45408     // defaults : white / black...
45409     this.applyBlacklists();
45410     
45411     
45412     
45413 };
45414
45415
45416 Roo.extend(Roo.HtmlEditorCore, Roo.Component,  {
45417
45418
45419      /**
45420      * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field 
45421      */
45422     
45423     owner : false,
45424     
45425      /**
45426      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
45427      *                        Roo.resizable.
45428      */
45429     resizable : false,
45430      /**
45431      * @cfg {Number} height (in pixels)
45432      */   
45433     height: 300,
45434    /**
45435      * @cfg {Number} width (in pixels)
45436      */   
45437     width: 500,
45438     
45439     /**
45440      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
45441      * 
45442      */
45443     stylesheets: false,
45444     
45445     /**
45446      * @cfg {boolean} allowComments - default false - allow comments in HTML source - by default they are stripped - if you are editing email you may need this.
45447      */
45448     allowComments: false,
45449     // id of frame..
45450     frameId: false,
45451     
45452     // private properties
45453     validationEvent : false,
45454     deferHeight: true,
45455     initialized : false,
45456     activated : false,
45457     sourceEditMode : false,
45458     onFocus : Roo.emptyFn,
45459     iframePad:3,
45460     hideMode:'offsets',
45461     
45462     clearUp: true,
45463     
45464     // blacklist + whitelisted elements..
45465     black: false,
45466     white: false,
45467      
45468     bodyCls : '',
45469
45470     /**
45471      * Protected method that will not generally be called directly. It
45472      * is called when the editor initializes the iframe with HTML contents. Override this method if you
45473      * want to change the initialization markup of the iframe (e.g. to add stylesheets).
45474      */
45475     getDocMarkup : function(){
45476         // body styles..
45477         var st = '';
45478         
45479         // inherit styels from page...?? 
45480         if (this.stylesheets === false) {
45481             
45482             Roo.get(document.head).select('style').each(function(node) {
45483                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
45484             });
45485             
45486             Roo.get(document.head).select('link').each(function(node) { 
45487                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
45488             });
45489             
45490         } else if (!this.stylesheets.length) {
45491                 // simple..
45492                 st = '<style type="text/css">' +
45493                     'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
45494                    '</style>';
45495         } else {
45496             for (var i in this.stylesheets) {
45497                 if (typeof(this.stylesheets[i]) != 'string') {
45498                     continue;
45499                 }
45500                 st += '<link rel="stylesheet" href="' + this.stylesheets[i] +'" type="text/css">';
45501             }
45502             
45503         }
45504         
45505         st +=  '<style type="text/css">' +
45506             'IMG { cursor: pointer } ' +
45507         '</style>';
45508
45509         var cls = 'roo-htmleditor-body';
45510         
45511         if(this.bodyCls.length){
45512             cls += ' ' + this.bodyCls;
45513         }
45514         
45515         return '<html><head>' + st  +
45516             //<style type="text/css">' +
45517             //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
45518             //'</style>' +
45519             ' </head><body contenteditable="true" data-enable-grammerly="true" class="' +  cls + '"></body></html>';
45520     },
45521
45522     // private
45523     onRender : function(ct, position)
45524     {
45525         var _t = this;
45526         //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
45527         this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
45528         
45529         
45530         this.el.dom.style.border = '0 none';
45531         this.el.dom.setAttribute('tabIndex', -1);
45532         this.el.addClass('x-hidden hide');
45533         
45534         
45535         
45536         if(Roo.isIE){ // fix IE 1px bogus margin
45537             this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
45538         }
45539        
45540         
45541         this.frameId = Roo.id();
45542         
45543          
45544         
45545         var iframe = this.owner.wrap.createChild({
45546             tag: 'iframe',
45547             cls: 'form-control', // bootstrap..
45548             id: this.frameId,
45549             name: this.frameId,
45550             frameBorder : 'no',
45551             'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL  :  "javascript:false"
45552         }, this.el
45553         );
45554         
45555         
45556         this.iframe = iframe.dom;
45557
45558         this.assignDocWin();
45559         
45560         this.doc.designMode = 'on';
45561        
45562         this.doc.open();
45563         this.doc.write(this.getDocMarkup());
45564         this.doc.close();
45565
45566         
45567         var task = { // must defer to wait for browser to be ready
45568             run : function(){
45569                 //console.log("run task?" + this.doc.readyState);
45570                 this.assignDocWin();
45571                 if(this.doc.body || this.doc.readyState == 'complete'){
45572                     try {
45573                         this.doc.designMode="on";
45574                     } catch (e) {
45575                         return;
45576                     }
45577                     Roo.TaskMgr.stop(task);
45578                     this.initEditor.defer(10, this);
45579                 }
45580             },
45581             interval : 10,
45582             duration: 10000,
45583             scope: this
45584         };
45585         Roo.TaskMgr.start(task);
45586
45587     },
45588
45589     // private
45590     onResize : function(w, h)
45591     {
45592          Roo.log('resize: ' +w + ',' + h );
45593         //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
45594         if(!this.iframe){
45595             return;
45596         }
45597         if(typeof w == 'number'){
45598             
45599             this.iframe.style.width = w + 'px';
45600         }
45601         if(typeof h == 'number'){
45602             
45603             this.iframe.style.height = h + 'px';
45604             if(this.doc){
45605                 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
45606             }
45607         }
45608         
45609     },
45610
45611     /**
45612      * Toggles the editor between standard and source edit mode.
45613      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
45614      */
45615     toggleSourceEdit : function(sourceEditMode){
45616         
45617         this.sourceEditMode = sourceEditMode === true;
45618         
45619         if(this.sourceEditMode){
45620  
45621             Roo.get(this.iframe).addClass(['x-hidden','hide', 'd-none']);     //FIXME - what's the BS styles for these
45622             
45623         }else{
45624             Roo.get(this.iframe).removeClass(['x-hidden','hide', 'd-none']);
45625             //this.iframe.className = '';
45626             this.deferFocus();
45627         }
45628         //this.setSize(this.owner.wrap.getSize());
45629         //this.fireEvent('editmodechange', this, this.sourceEditMode);
45630     },
45631
45632     
45633   
45634
45635     /**
45636      * Protected method that will not generally be called directly. If you need/want
45637      * custom HTML cleanup, this is the method you should override.
45638      * @param {String} html The HTML to be cleaned
45639      * return {String} The cleaned HTML
45640      */
45641     cleanHtml : function(html){
45642         html = String(html);
45643         if(html.length > 5){
45644             if(Roo.isSafari){ // strip safari nonsense
45645                 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
45646             }
45647         }
45648         if(html == '&nbsp;'){
45649             html = '';
45650         }
45651         return html;
45652     },
45653
45654     /**
45655      * HTML Editor -> Textarea
45656      * Protected method that will not generally be called directly. Syncs the contents
45657      * of the editor iframe with the textarea.
45658      */
45659     syncValue : function()
45660     {
45661         Roo.log("HtmlEditorCore:syncValue (EDITOR->TEXT)");
45662         if(this.initialized){
45663             var bd = (this.doc.body || this.doc.documentElement);
45664             //this.cleanUpPaste(); -- this is done else where and causes havoc..
45665             
45666             var div = document.createElement('div');
45667             div.innerHTML = bd.innerHTML;
45668             // remove content editable. (blocks)
45669             new Roo.htmleditor.FilterAttributes({node : div, attrib_black: [ 'contenteditable' ] });
45670             //?? tidy?
45671             var html = div.innerHTML;
45672             if(Roo.isSafari){
45673                 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
45674                 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
45675                 if(m && m[1]){
45676                     html = '<div style="'+m[0]+'">' + html + '</div>';
45677                 }
45678             }
45679             html = this.cleanHtml(html);
45680             // fix up the special chars.. normaly like back quotes in word...
45681             // however we do not want to do this with chinese..
45682             html = html.replace(/[\uD800-\uDBFF][\uDC00-\uDFFF]|[\u0080-\uFFFF]/g, function(match) {
45683                 
45684                 var cc = match.charCodeAt();
45685
45686                 // Get the character value, handling surrogate pairs
45687                 if (match.length == 2) {
45688                     // It's a surrogate pair, calculate the Unicode code point
45689                     var high = match.charCodeAt(0) - 0xD800;
45690                     var low  = match.charCodeAt(1) - 0xDC00;
45691                     cc = (high * 0x400) + low + 0x10000;
45692                 }  else if (
45693                     (cc >= 0x4E00 && cc < 0xA000 ) ||
45694                     (cc >= 0x3400 && cc < 0x4E00 ) ||
45695                     (cc >= 0xf900 && cc < 0xfb00 )
45696                 ) {
45697                         return match;
45698                 }  
45699          
45700                 // No, use a numeric entity. Here we brazenly (and possibly mistakenly)
45701                 return "&#" + cc + ";";
45702                 
45703                 
45704             });
45705             
45706             
45707              
45708             if(this.owner.fireEvent('beforesync', this, html) !== false){
45709                 this.el.dom.value = html;
45710                 this.owner.fireEvent('sync', this, html);
45711             }
45712         }
45713     },
45714
45715     /**
45716      * TEXTAREA -> EDITABLE
45717      * Protected method that will not generally be called directly. Pushes the value of the textarea
45718      * into the iframe editor.
45719      */
45720     pushValue : function()
45721     {
45722         Roo.log("HtmlEditorCore:pushValue (TEXT->EDITOR)");
45723         if(this.initialized){
45724             var v = this.el.dom.value.trim();
45725             
45726             
45727             if(this.owner.fireEvent('beforepush', this, v) !== false){
45728                 var d = (this.doc.body || this.doc.documentElement);
45729                 d.innerHTML = v;
45730                 //this.cleanUpPaste();
45731                 this.el.dom.value = d.innerHTML;
45732                 this.owner.fireEvent('push', this, v);
45733             }
45734             
45735             Roo.each(Roo.get(this.doc.body).query('*[data-block]'), function(e) {
45736                 var cls = Roo.htmleditor['Block' + Roo.get(e).attr('data-block')];
45737                 if (typeof(cls) == 'undefined') {
45738                     Roo.log("OOps missing block : " + 'Block' + Roo.get(e).attr('data-block'));
45739                     return;
45740                 }
45741                 new cls(e);  /// should trigger update element
45742             },this)
45743             
45744             
45745         }
45746     },
45747
45748     // private
45749     deferFocus : function(){
45750         this.focus.defer(10, this);
45751     },
45752
45753     // doc'ed in Field
45754     focus : function(){
45755         if(this.win && !this.sourceEditMode){
45756             this.win.focus();
45757         }else{
45758             this.el.focus();
45759         }
45760     },
45761     
45762     assignDocWin: function()
45763     {
45764         var iframe = this.iframe;
45765         
45766          if(Roo.isIE){
45767             this.doc = iframe.contentWindow.document;
45768             this.win = iframe.contentWindow;
45769         } else {
45770 //            if (!Roo.get(this.frameId)) {
45771 //                return;
45772 //            }
45773 //            this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
45774 //            this.win = Roo.get(this.frameId).dom.contentWindow;
45775             
45776             if (!Roo.get(this.frameId) && !iframe.contentDocument) {
45777                 return;
45778             }
45779             
45780             this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
45781             this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
45782         }
45783     },
45784     
45785     // private
45786     initEditor : function(){
45787         //console.log("INIT EDITOR");
45788         this.assignDocWin();
45789         
45790         
45791         
45792         this.doc.designMode="on";
45793         this.doc.open();
45794         this.doc.write(this.getDocMarkup());
45795         this.doc.close();
45796         
45797         var dbody = (this.doc.body || this.doc.documentElement);
45798         //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
45799         // this copies styles from the containing element into thsi one..
45800         // not sure why we need all of this..
45801         //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
45802         
45803         //var ss = this.el.getStyles( 'background-image', 'background-repeat');
45804         //ss['background-attachment'] = 'fixed'; // w3c
45805         dbody.bgProperties = 'fixed'; // ie
45806         //Roo.DomHelper.applyStyles(dbody, ss);
45807         Roo.EventManager.on(this.doc, {
45808             //'mousedown': this.onEditorEvent,
45809             'mouseup': this.onEditorEvent,
45810             'dblclick': this.onEditorEvent,
45811             'click': this.onEditorEvent,
45812             'keyup': this.onEditorEvent,
45813             'paste': this.onPasteEvent,
45814             buffer:100,
45815             scope: this
45816         });
45817         if(Roo.isGecko){
45818             Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
45819         }
45820         if(Roo.isIE || Roo.isSafari || Roo.isOpera){
45821             Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
45822         }
45823         this.initialized = true;
45824
45825         
45826         // initialize special key events - enter
45827         new Roo.htmleditor.KeyEnter({core : this});
45828         
45829          
45830         
45831         this.owner.fireEvent('initialize', this);
45832         this.pushValue();
45833     },
45834     
45835     onPasteEvent : function(e,v)  {
45836          // default behaveiour should be our local cleanup paste? (optional?)
45837          // for simple editor - we want to hammer the paste and get rid of everything... - so over-rideable..
45838          this.owner.fireEvent('paste', e, v);
45839     },
45840     // private
45841     onDestroy : function(){
45842         
45843         
45844         
45845         if(this.rendered){
45846             
45847             //for (var i =0; i < this.toolbars.length;i++) {
45848             //    // fixme - ask toolbars for heights?
45849             //    this.toolbars[i].onDestroy();
45850            // }
45851             
45852             //this.wrap.dom.innerHTML = '';
45853             //this.wrap.remove();
45854         }
45855     },
45856
45857     // private
45858     onFirstFocus : function(){
45859         
45860         this.assignDocWin();
45861         
45862         
45863         this.activated = true;
45864          
45865     
45866         if(Roo.isGecko){ // prevent silly gecko errors
45867             this.win.focus();
45868             var s = this.win.getSelection();
45869             if(!s.focusNode || s.focusNode.nodeType != 3){
45870                 var r = s.getRangeAt(0);
45871                 r.selectNodeContents((this.doc.body || this.doc.documentElement));
45872                 r.collapse(true);
45873                 this.deferFocus();
45874             }
45875             try{
45876                 this.execCmd('useCSS', true);
45877                 this.execCmd('styleWithCSS', false);
45878             }catch(e){}
45879         }
45880         this.owner.fireEvent('activate', this);
45881     },
45882
45883     // private
45884     adjustFont: function(btn){
45885         var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
45886         //if(Roo.isSafari){ // safari
45887         //    adjust *= 2;
45888        // }
45889         var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
45890         if(Roo.isSafari){ // safari
45891             var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
45892             v =  (v < 10) ? 10 : v;
45893             v =  (v > 48) ? 48 : v;
45894             v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
45895             
45896         }
45897         
45898         
45899         v = Math.max(1, v+adjust);
45900         
45901         this.execCmd('FontSize', v  );
45902     },
45903
45904     onEditorEvent : function(e)
45905     {
45906         this.owner.fireEvent('editorevent', this, e);
45907       //  this.updateToolbar();
45908         this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
45909     },
45910
45911     insertTag : function(tg)
45912     {
45913         // could be a bit smarter... -> wrap the current selected tRoo..
45914         if (tg.toLowerCase() == 'span' ||
45915             tg.toLowerCase() == 'code' ||
45916             tg.toLowerCase() == 'sup' ||
45917             tg.toLowerCase() == 'sub' 
45918             ) {
45919             
45920             range = this.createRange(this.getSelection());
45921             var wrappingNode = this.doc.createElement(tg.toLowerCase());
45922             wrappingNode.appendChild(range.extractContents());
45923             range.insertNode(wrappingNode);
45924
45925             return;
45926             
45927             
45928             
45929         }
45930         this.execCmd("formatblock",   tg);
45931         
45932     },
45933     
45934     insertText : function(txt)
45935     {
45936         
45937         
45938         var range = this.createRange();
45939         range.deleteContents();
45940                //alert(Sender.getAttribute('label'));
45941                
45942         range.insertNode(this.doc.createTextNode(txt));
45943     } ,
45944     
45945      
45946
45947     /**
45948      * Executes a Midas editor command on the editor document and performs necessary focus and
45949      * toolbar updates. <b>This should only be called after the editor is initialized.</b>
45950      * @param {String} cmd The Midas command
45951      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
45952      */
45953     relayCmd : function(cmd, value){
45954         this.win.focus();
45955         this.execCmd(cmd, value);
45956         this.owner.fireEvent('editorevent', this);
45957         //this.updateToolbar();
45958         this.owner.deferFocus();
45959     },
45960
45961     /**
45962      * Executes a Midas editor command directly on the editor document.
45963      * For visual commands, you should use {@link #relayCmd} instead.
45964      * <b>This should only be called after the editor is initialized.</b>
45965      * @param {String} cmd The Midas command
45966      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
45967      */
45968     execCmd : function(cmd, value){
45969         this.doc.execCommand(cmd, false, value === undefined ? null : value);
45970         this.syncValue();
45971     },
45972  
45973  
45974    
45975     /**
45976      * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
45977      * to insert tRoo.
45978      * @param {String} text | dom node.. 
45979      */
45980     insertAtCursor : function(text)
45981     {
45982         
45983         if(!this.activated){
45984             return;
45985         }
45986         /*
45987         if(Roo.isIE){
45988             this.win.focus();
45989             var r = this.doc.selection.createRange();
45990             if(r){
45991                 r.collapse(true);
45992                 r.pasteHTML(text);
45993                 this.syncValue();
45994                 this.deferFocus();
45995             
45996             }
45997             return;
45998         }
45999         */
46000         if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
46001             this.win.focus();
46002             
46003             
46004             // from jquery ui (MIT licenced)
46005             var range, node;
46006             var win = this.win;
46007             
46008             if (win.getSelection && win.getSelection().getRangeAt) {
46009                 range = win.getSelection().getRangeAt(0);
46010                 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
46011                 range.insertNode(node);
46012             } else if (win.document.selection && win.document.selection.createRange) {
46013                 // no firefox support
46014                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
46015                 win.document.selection.createRange().pasteHTML(txt);
46016             } else {
46017                 // no firefox support
46018                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
46019                 this.execCmd('InsertHTML', txt);
46020             } 
46021             
46022             this.syncValue();
46023             
46024             this.deferFocus();
46025         }
46026     },
46027  // private
46028     mozKeyPress : function(e){
46029         if(e.ctrlKey){
46030             var c = e.getCharCode(), cmd;
46031           
46032             if(c > 0){
46033                 c = String.fromCharCode(c).toLowerCase();
46034                 switch(c){
46035                     case 'b':
46036                         cmd = 'bold';
46037                         break;
46038                     case 'i':
46039                         cmd = 'italic';
46040                         break;
46041                     
46042                     case 'u':
46043                         cmd = 'underline';
46044                         break;
46045                     
46046                     //case 'v':
46047                       //  this.cleanUpPaste.defer(100, this);
46048                       //  return;
46049                         
46050                 }
46051                 if(cmd){
46052                     this.win.focus();
46053                     this.execCmd(cmd);
46054                     this.deferFocus();
46055                     e.preventDefault();
46056                 }
46057                 
46058             }
46059         }
46060     },
46061
46062     // private
46063     fixKeys : function(){ // load time branching for fastest keydown performance
46064         if(Roo.isIE){
46065             return function(e){
46066                 var k = e.getKey(), r;
46067                 if(k == e.TAB){
46068                     e.stopEvent();
46069                     r = this.doc.selection.createRange();
46070                     if(r){
46071                         r.collapse(true);
46072                         r.pasteHTML('&#160;&#160;&#160;&#160;');
46073                         this.deferFocus();
46074                     }
46075                     return;
46076                 }
46077                 
46078                 if(k == e.ENTER){
46079                     r = this.doc.selection.createRange();
46080                     if(r){
46081                         var target = r.parentElement();
46082                         if(!target || target.tagName.toLowerCase() != 'li'){
46083                             e.stopEvent();
46084                             r.pasteHTML('<br/>');
46085                             r.collapse(false);
46086                             r.select();
46087                         }
46088                     }
46089                 }
46090                 //if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
46091                 //    this.cleanUpPaste.defer(100, this);
46092                 //    return;
46093                 //}
46094                 
46095                 
46096             };
46097         }else if(Roo.isOpera){
46098             return function(e){
46099                 var k = e.getKey();
46100                 if(k == e.TAB){
46101                     e.stopEvent();
46102                     this.win.focus();
46103                     this.execCmd('InsertHTML','&#160;&#160;&#160;&#160;');
46104                     this.deferFocus();
46105                 }
46106                 //if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
46107                 //    this.cleanUpPaste.defer(100, this);
46108                  //   return;
46109                 //}
46110                 
46111             };
46112         }else if(Roo.isSafari){
46113             return function(e){
46114                 var k = e.getKey();
46115                 
46116                 if(k == e.TAB){
46117                     e.stopEvent();
46118                     this.execCmd('InsertText','\t');
46119                     this.deferFocus();
46120                     return;
46121                 }
46122                //if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
46123                  //   this.cleanUpPaste.defer(100, this);
46124                  //   return;
46125                // }
46126                 
46127              };
46128         }
46129     }(),
46130     
46131     getAllAncestors: function()
46132     {
46133         var p = this.getSelectedNode();
46134         var a = [];
46135         if (!p) {
46136             a.push(p); // push blank onto stack..
46137             p = this.getParentElement();
46138         }
46139         
46140         
46141         while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
46142             a.push(p);
46143             p = p.parentNode;
46144         }
46145         a.push(this.doc.body);
46146         return a;
46147     },
46148     lastSel : false,
46149     lastSelNode : false,
46150     
46151     
46152     getSelection : function() 
46153     {
46154         this.assignDocWin();
46155         return Roo.isIE ? this.doc.selection : this.win.getSelection();
46156     },
46157     
46158     getSelectedNode: function() 
46159     {
46160         // this may only work on Gecko!!!
46161         
46162         // should we cache this!!!!
46163         
46164         
46165         
46166          
46167         var range = this.createRange(this.getSelection()).cloneRange();
46168         
46169         if (Roo.isIE) {
46170             var parent = range.parentElement();
46171             while (true) {
46172                 var testRange = range.duplicate();
46173                 testRange.moveToElementText(parent);
46174                 if (testRange.inRange(range)) {
46175                     break;
46176                 }
46177                 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
46178                     break;
46179                 }
46180                 parent = parent.parentElement;
46181             }
46182             return parent;
46183         }
46184         
46185         // is ancestor a text element.
46186         var ac =  range.commonAncestorContainer;
46187         if (ac.nodeType == 3) {
46188             ac = ac.parentNode;
46189         }
46190         
46191         var ar = ac.childNodes;
46192          
46193         var nodes = [];
46194         var other_nodes = [];
46195         var has_other_nodes = false;
46196         for (var i=0;i<ar.length;i++) {
46197             if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ? 
46198                 continue;
46199             }
46200             // fullly contained node.
46201             
46202             if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
46203                 nodes.push(ar[i]);
46204                 continue;
46205             }
46206             
46207             // probably selected..
46208             if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
46209                 other_nodes.push(ar[i]);
46210                 continue;
46211             }
46212             // outer..
46213             if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0))  {
46214                 continue;
46215             }
46216             
46217             
46218             has_other_nodes = true;
46219         }
46220         if (!nodes.length && other_nodes.length) {
46221             nodes= other_nodes;
46222         }
46223         if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
46224             return false;
46225         }
46226         
46227         return nodes[0];
46228     },
46229     createRange: function(sel)
46230     {
46231         // this has strange effects when using with 
46232         // top toolbar - not sure if it's a great idea.
46233         //this.editor.contentWindow.focus();
46234         if (typeof sel != "undefined") {
46235             try {
46236                 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
46237             } catch(e) {
46238                 return this.doc.createRange();
46239             }
46240         } else {
46241             return this.doc.createRange();
46242         }
46243     },
46244     getParentElement: function()
46245     {
46246         
46247         this.assignDocWin();
46248         var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
46249         
46250         var range = this.createRange(sel);
46251          
46252         try {
46253             var p = range.commonAncestorContainer;
46254             while (p.nodeType == 3) { // text node
46255                 p = p.parentNode;
46256             }
46257             return p;
46258         } catch (e) {
46259             return null;
46260         }
46261     
46262     },
46263     /***
46264      *
46265      * Range intersection.. the hard stuff...
46266      *  '-1' = before
46267      *  '0' = hits..
46268      *  '1' = after.
46269      *         [ -- selected range --- ]
46270      *   [fail]                        [fail]
46271      *
46272      *    basically..
46273      *      if end is before start or  hits it. fail.
46274      *      if start is after end or hits it fail.
46275      *
46276      *   if either hits (but other is outside. - then it's not 
46277      *   
46278      *    
46279      **/
46280     
46281     
46282     // @see http://www.thismuchiknow.co.uk/?p=64.
46283     rangeIntersectsNode : function(range, node)
46284     {
46285         var nodeRange = node.ownerDocument.createRange();
46286         try {
46287             nodeRange.selectNode(node);
46288         } catch (e) {
46289             nodeRange.selectNodeContents(node);
46290         }
46291     
46292         var rangeStartRange = range.cloneRange();
46293         rangeStartRange.collapse(true);
46294     
46295         var rangeEndRange = range.cloneRange();
46296         rangeEndRange.collapse(false);
46297     
46298         var nodeStartRange = nodeRange.cloneRange();
46299         nodeStartRange.collapse(true);
46300     
46301         var nodeEndRange = nodeRange.cloneRange();
46302         nodeEndRange.collapse(false);
46303     
46304         return rangeStartRange.compareBoundaryPoints(
46305                  Range.START_TO_START, nodeEndRange) == -1 &&
46306                rangeEndRange.compareBoundaryPoints(
46307                  Range.START_TO_START, nodeStartRange) == 1;
46308         
46309          
46310     },
46311     rangeCompareNode : function(range, node)
46312     {
46313         var nodeRange = node.ownerDocument.createRange();
46314         try {
46315             nodeRange.selectNode(node);
46316         } catch (e) {
46317             nodeRange.selectNodeContents(node);
46318         }
46319         
46320         
46321         range.collapse(true);
46322     
46323         nodeRange.collapse(true);
46324      
46325         var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
46326         var ee = range.compareBoundaryPoints(  Range.END_TO_END, nodeRange);
46327          
46328         //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
46329         
46330         var nodeIsBefore   =  ss == 1;
46331         var nodeIsAfter    = ee == -1;
46332         
46333         if (nodeIsBefore && nodeIsAfter) {
46334             return 0; // outer
46335         }
46336         if (!nodeIsBefore && nodeIsAfter) {
46337             return 1; //right trailed.
46338         }
46339         
46340         if (nodeIsBefore && !nodeIsAfter) {
46341             return 2;  // left trailed.
46342         }
46343         // fully contined.
46344         return 3;
46345     },
46346 /*
46347     // private? - in a new class?
46348     cleanUpPaste :  function()
46349     {
46350         // cleans up the whole document..
46351         Roo.log('cleanuppaste');
46352         
46353         this.cleanUpChild(this.doc.body);
46354         var clean = this.cleanWordChars(this.doc.body.innerHTML);
46355         if (clean != this.doc.body.innerHTML) {
46356             this.doc.body.innerHTML = clean;
46357         }
46358         
46359     },
46360     */
46361     cleanWordChars : function(input) {// change the chars to hex code
46362         var he = Roo.HtmlEditorCore;
46363         
46364         var output = input;
46365         Roo.each(he.swapCodes, function(sw) { 
46366             var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
46367             
46368             output = output.replace(swapper, sw[1]);
46369         });
46370         
46371         return output;
46372     },
46373     
46374      
46375     
46376         
46377     
46378     cleanUpChild : function (node)
46379     {
46380         
46381         new Roo.htmleditor.FilterComment({node : node});
46382         new Roo.htmleditor.FilterAttributes({
46383                 node : node,
46384                 attrib_black : this.ablack,
46385                 attrib_clean : this.aclean,
46386                 style_white : this.cwhite,
46387                 style_black : this.cblack
46388         });
46389         new Roo.htmleditor.FilterBlack({ node : node, tag : this.black});
46390         new Roo.htmleditor.FilterKeepChildren({node : node, tag : this.tag_remove} );
46391          
46392         
46393     },
46394     
46395     /**
46396      * Clean up MS wordisms...
46397      * @deprecated - use filter directly
46398      */
46399     cleanWord : function(node)
46400     {
46401         new Roo.htmleditor.FilterWord({ node : node ? node : this.doc.body });
46402         
46403     },
46404    
46405     
46406     /**
46407
46408      * @deprecated - use filters
46409      */
46410     cleanTableWidths : function(node)
46411     {
46412         new Roo.htmleditor.FilterTableWidth({ node : node ? node : this.doc.body});
46413         
46414  
46415     },
46416     
46417      
46418         
46419     applyBlacklists : function()
46420     {
46421         var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white  : [];
46422         var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black :  [];
46423         
46424         this.aclean = typeof(this.owner.aclean) != 'undefined' && this.owner.aclean ? this.owner.aclean :  Roo.HtmlEditorCore.aclean;
46425         this.ablack = typeof(this.owner.ablack) != 'undefined' && this.owner.ablack ? this.owner.ablack :  Roo.HtmlEditorCore.ablack;
46426         this.tag_remove = typeof(this.owner.tag_remove) != 'undefined' && this.owner.tag_remove ? this.owner.tag_remove :  Roo.HtmlEditorCore.tag_remove;
46427         
46428         this.white = [];
46429         this.black = [];
46430         Roo.each(Roo.HtmlEditorCore.white, function(tag) {
46431             if (b.indexOf(tag) > -1) {
46432                 return;
46433             }
46434             this.white.push(tag);
46435             
46436         }, this);
46437         
46438         Roo.each(w, function(tag) {
46439             if (b.indexOf(tag) > -1) {
46440                 return;
46441             }
46442             if (this.white.indexOf(tag) > -1) {
46443                 return;
46444             }
46445             this.white.push(tag);
46446             
46447         }, this);
46448         
46449         
46450         Roo.each(Roo.HtmlEditorCore.black, function(tag) {
46451             if (w.indexOf(tag) > -1) {
46452                 return;
46453             }
46454             this.black.push(tag);
46455             
46456         }, this);
46457         
46458         Roo.each(b, function(tag) {
46459             if (w.indexOf(tag) > -1) {
46460                 return;
46461             }
46462             if (this.black.indexOf(tag) > -1) {
46463                 return;
46464             }
46465             this.black.push(tag);
46466             
46467         }, this);
46468         
46469         
46470         w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite  : [];
46471         b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack :  [];
46472         
46473         this.cwhite = [];
46474         this.cblack = [];
46475         Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
46476             if (b.indexOf(tag) > -1) {
46477                 return;
46478             }
46479             this.cwhite.push(tag);
46480             
46481         }, this);
46482         
46483         Roo.each(w, function(tag) {
46484             if (b.indexOf(tag) > -1) {
46485                 return;
46486             }
46487             if (this.cwhite.indexOf(tag) > -1) {
46488                 return;
46489             }
46490             this.cwhite.push(tag);
46491             
46492         }, this);
46493         
46494         
46495         Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
46496             if (w.indexOf(tag) > -1) {
46497                 return;
46498             }
46499             this.cblack.push(tag);
46500             
46501         }, this);
46502         
46503         Roo.each(b, function(tag) {
46504             if (w.indexOf(tag) > -1) {
46505                 return;
46506             }
46507             if (this.cblack.indexOf(tag) > -1) {
46508                 return;
46509             }
46510             this.cblack.push(tag);
46511             
46512         }, this);
46513     },
46514     
46515     setStylesheets : function(stylesheets)
46516     {
46517         if(typeof(stylesheets) == 'string'){
46518             Roo.get(this.iframe.contentDocument.head).createChild({
46519                 tag : 'link',
46520                 rel : 'stylesheet',
46521                 type : 'text/css',
46522                 href : stylesheets
46523             });
46524             
46525             return;
46526         }
46527         var _this = this;
46528      
46529         Roo.each(stylesheets, function(s) {
46530             if(!s.length){
46531                 return;
46532             }
46533             
46534             Roo.get(_this.iframe.contentDocument.head).createChild({
46535                 tag : 'link',
46536                 rel : 'stylesheet',
46537                 type : 'text/css',
46538                 href : s
46539             });
46540         });
46541
46542         
46543     },
46544     
46545     removeStylesheets : function()
46546     {
46547         var _this = this;
46548         
46549         Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
46550             s.remove();
46551         });
46552     },
46553     
46554     setStyle : function(style)
46555     {
46556         Roo.get(this.iframe.contentDocument.head).createChild({
46557             tag : 'style',
46558             type : 'text/css',
46559             html : style
46560         });
46561
46562         return;
46563     }
46564     
46565     // hide stuff that is not compatible
46566     /**
46567      * @event blur
46568      * @hide
46569      */
46570     /**
46571      * @event change
46572      * @hide
46573      */
46574     /**
46575      * @event focus
46576      * @hide
46577      */
46578     /**
46579      * @event specialkey
46580      * @hide
46581      */
46582     /**
46583      * @cfg {String} fieldClass @hide
46584      */
46585     /**
46586      * @cfg {String} focusClass @hide
46587      */
46588     /**
46589      * @cfg {String} autoCreate @hide
46590      */
46591     /**
46592      * @cfg {String} inputType @hide
46593      */
46594     /**
46595      * @cfg {String} invalidClass @hide
46596      */
46597     /**
46598      * @cfg {String} invalidText @hide
46599      */
46600     /**
46601      * @cfg {String} msgFx @hide
46602      */
46603     /**
46604      * @cfg {String} validateOnBlur @hide
46605      */
46606 });
46607
46608 Roo.HtmlEditorCore.white = [
46609         'area', 'br', 'img', 'input', 'hr', 'wbr',
46610         
46611        'address', 'blockquote', 'center', 'dd',      'dir',       'div', 
46612        'dl',      'dt',         'h1',     'h2',      'h3',        'h4', 
46613        'h5',      'h6',         'hr',     'isindex', 'listing',   'marquee', 
46614        'menu',    'multicol',   'ol',     'p',       'plaintext', 'pre', 
46615        'table',   'ul',         'xmp', 
46616        
46617        'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th', 
46618       'thead',   'tr', 
46619      
46620       'dir', 'menu', 'ol', 'ul', 'dl',
46621        
46622       'embed',  'object'
46623 ];
46624
46625
46626 Roo.HtmlEditorCore.black = [
46627     //    'embed',  'object', // enable - backend responsiblity to clean thiese
46628         'applet', // 
46629         'base',   'basefont', 'bgsound', 'blink',  'body', 
46630         'frame',  'frameset', 'head',    'html',   'ilayer', 
46631         'iframe', 'layer',  'link',     'meta',    'object',   
46632         'script', 'style' ,'title',  'xml' // clean later..
46633 ];
46634 Roo.HtmlEditorCore.clean = [
46635     'script', 'style', 'title', 'xml'
46636 ];
46637 Roo.HtmlEditorCore.tag_remove = [
46638     'font'
46639 ];
46640 // attributes..
46641
46642 Roo.HtmlEditorCore.ablack = [
46643     'on'
46644 ];
46645     
46646 Roo.HtmlEditorCore.aclean = [ 
46647     'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc' 
46648 ];
46649
46650 // protocols..
46651 Roo.HtmlEditorCore.pwhite= [
46652         'http',  'https',  'mailto'
46653 ];
46654
46655 // white listed style attributes.
46656 Roo.HtmlEditorCore.cwhite= [
46657       //  'text-align', /// default is to allow most things..
46658       
46659          
46660 //        'font-size'//??
46661 ];
46662
46663 // black listed style attributes.
46664 Roo.HtmlEditorCore.cblack= [
46665       //  'font-size' -- this can be set by the project 
46666 ];
46667
46668
46669 Roo.HtmlEditorCore.swapCodes   =[ 
46670     [    8211, "&#8211;" ], 
46671     [    8212, "&#8212;" ], 
46672     [    8216,  "'" ],  
46673     [    8217, "'" ],  
46674     [    8220, '"' ],  
46675     [    8221, '"' ],  
46676     [    8226, "*" ],  
46677     [    8230, "..." ]
46678 ]; 
46679
46680     //<script type="text/javascript">
46681
46682 /*
46683  * Ext JS Library 1.1.1
46684  * Copyright(c) 2006-2007, Ext JS, LLC.
46685  * Licence LGPL
46686  * 
46687  */
46688  
46689  
46690 Roo.form.HtmlEditor = function(config){
46691     
46692     
46693     
46694     Roo.form.HtmlEditor.superclass.constructor.call(this, config);
46695     
46696     if (!this.toolbars) {
46697         this.toolbars = [];
46698     }
46699     this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
46700     
46701     
46702 };
46703
46704 /**
46705  * @class Roo.form.HtmlEditor
46706  * @extends Roo.form.Field
46707  * Provides a lightweight HTML Editor component.
46708  *
46709  * This has been tested on Fireforx / Chrome.. IE may not be so great..
46710  * 
46711  * <br><br><b>Note: The focus/blur and validation marking functionality inherited from Ext.form.Field is NOT
46712  * supported by this editor.</b><br/><br/>
46713  * An Editor is a sensitive component that can't be used in all spots standard fields can be used. Putting an Editor within
46714  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
46715  */
46716 Roo.extend(Roo.form.HtmlEditor, Roo.form.Field, {
46717     /**
46718      * @cfg {Boolean} clearUp
46719      */
46720     clearUp : true,
46721       /**
46722      * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
46723      */
46724     toolbars : false,
46725    
46726      /**
46727      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
46728      *                        Roo.resizable.
46729      */
46730     resizable : false,
46731      /**
46732      * @cfg {Number} height (in pixels)
46733      */   
46734     height: 300,
46735    /**
46736      * @cfg {Number} width (in pixels)
46737      */   
46738     width: 500,
46739     
46740     /**
46741      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets - this is usally a good idea  rootURL + '/roojs1/css/undoreset.css',   .
46742      * 
46743      */
46744     stylesheets: false,
46745     
46746     
46747      /**
46748      * @cfg {Array} blacklist of css styles style attributes (blacklist overrides whitelist)
46749      * 
46750      */
46751     cblack: false,
46752     /**
46753      * @cfg {Array} whitelist of css styles style attributes (blacklist overrides whitelist)
46754      * 
46755      */
46756     cwhite: false,
46757     
46758      /**
46759      * @cfg {Array} blacklist of html tags - in addition to standard blacklist.
46760      * 
46761      */
46762     black: false,
46763     /**
46764      * @cfg {Array} whitelist of html tags - in addition to statndard whitelist
46765      * 
46766      */
46767     white: false,
46768     /**
46769      * @cfg {boolean} allowComments - default false - allow comments in HTML source - by default they are stripped - if you are editing email you may need this.
46770      */
46771     allowComments: false,
46772     /**
46773      * @cfg {string} bodyCls- default '' default classes to add to body of editable area - usually undoreset is a good start..
46774      */
46775     
46776     
46777      bodyCls : '',
46778     
46779     // id of frame..
46780     frameId: false,
46781     
46782     // private properties
46783     validationEvent : false,
46784     deferHeight: true,
46785     initialized : false,
46786     activated : false,
46787     
46788     onFocus : Roo.emptyFn,
46789     iframePad:3,
46790     hideMode:'offsets',
46791     
46792     actionMode : 'container', // defaults to hiding it...
46793     
46794     defaultAutoCreate : { // modified by initCompnoent..
46795         tag: "textarea",
46796         style:"width:500px;height:300px;",
46797         autocomplete: "new-password"
46798     },
46799
46800     // private
46801     initComponent : function(){
46802         this.addEvents({
46803             /**
46804              * @event initialize
46805              * Fires when the editor is fully initialized (including the iframe)
46806              * @param {HtmlEditor} this
46807              */
46808             initialize: true,
46809             /**
46810              * @event activate
46811              * Fires when the editor is first receives the focus. Any insertion must wait
46812              * until after this event.
46813              * @param {HtmlEditor} this
46814              */
46815             activate: true,
46816              /**
46817              * @event beforesync
46818              * Fires before the textarea is updated with content from the editor iframe. Return false
46819              * to cancel the sync.
46820              * @param {HtmlEditor} this
46821              * @param {String} html
46822              */
46823             beforesync: true,
46824              /**
46825              * @event beforepush
46826              * Fires before the iframe editor is updated with content from the textarea. Return false
46827              * to cancel the push.
46828              * @param {HtmlEditor} this
46829              * @param {String} html
46830              */
46831             beforepush: true,
46832              /**
46833              * @event sync
46834              * Fires when the textarea is updated with content from the editor iframe.
46835              * @param {HtmlEditor} this
46836              * @param {String} html
46837              */
46838             sync: true,
46839              /**
46840              * @event push
46841              * Fires when the iframe editor is updated with content from the textarea.
46842              * @param {HtmlEditor} this
46843              * @param {String} html
46844              */
46845             push: true,
46846              /**
46847              * @event editmodechange
46848              * Fires when the editor switches edit modes
46849              * @param {HtmlEditor} this
46850              * @param {Boolean} sourceEdit True if source edit, false if standard editing.
46851              */
46852             editmodechange: true,
46853             /**
46854              * @event editorevent
46855              * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
46856              * @param {HtmlEditor} this
46857              */
46858             editorevent: true,
46859             /**
46860              * @event firstfocus
46861              * Fires when on first focus - needed by toolbars..
46862              * @param {HtmlEditor} this
46863              */
46864             firstfocus: true,
46865             /**
46866              * @event autosave
46867              * Auto save the htmlEditor value as a file into Events
46868              * @param {HtmlEditor} this
46869              */
46870             autosave: true,
46871             /**
46872              * @event savedpreview
46873              * preview the saved version of htmlEditor
46874              * @param {HtmlEditor} this
46875              */
46876             savedpreview: true,
46877             
46878             /**
46879             * @event stylesheetsclick
46880             * Fires when press the Sytlesheets button
46881             * @param {Roo.HtmlEditorCore} this
46882             */
46883             stylesheetsclick: true,
46884             /**
46885             * @event paste
46886             * Fires when press user pastes into the editor
46887             * @param {Roo.HtmlEditorCore} this
46888             */
46889             paste: true 
46890         });
46891         this.defaultAutoCreate =  {
46892             tag: "textarea",
46893             style:'width: ' + this.width + 'px;height: ' + this.height + 'px;',
46894             autocomplete: "new-password"
46895         };
46896     },
46897
46898     /**
46899      * Protected method that will not generally be called directly. It
46900      * is called when the editor creates its toolbar. Override this method if you need to
46901      * add custom toolbar buttons.
46902      * @param {HtmlEditor} editor
46903      */
46904     createToolbar : function(editor){
46905         Roo.log("create toolbars");
46906         if (!editor.toolbars || !editor.toolbars.length) {
46907             editor.toolbars = [ new Roo.form.HtmlEditor.ToolbarStandard() ]; // can be empty?
46908         }
46909         
46910         for (var i =0 ; i < editor.toolbars.length;i++) {
46911             editor.toolbars[i] = Roo.factory(
46912                     typeof(editor.toolbars[i]) == 'string' ?
46913                         { xtype: editor.toolbars[i]} : editor.toolbars[i],
46914                 Roo.form.HtmlEditor);
46915             editor.toolbars[i].init(editor);
46916         }
46917          
46918         
46919     },
46920
46921      
46922     // private
46923     onRender : function(ct, position)
46924     {
46925         var _t = this;
46926         Roo.form.HtmlEditor.superclass.onRender.call(this, ct, position);
46927         
46928         this.wrap = this.el.wrap({
46929             cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
46930         });
46931         
46932         this.editorcore.onRender(ct, position);
46933          
46934         if (this.resizable) {
46935             this.resizeEl = new Roo.Resizable(this.wrap, {
46936                 pinned : true,
46937                 wrap: true,
46938                 dynamic : true,
46939                 minHeight : this.height,
46940                 height: this.height,
46941                 handles : this.resizable,
46942                 width: this.width,
46943                 listeners : {
46944                     resize : function(r, w, h) {
46945                         _t.onResize(w,h); // -something
46946                     }
46947                 }
46948             });
46949             
46950         }
46951         this.createToolbar(this);
46952        
46953         
46954         if(!this.width){
46955             this.setSize(this.wrap.getSize());
46956         }
46957         if (this.resizeEl) {
46958             this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
46959             // should trigger onReize..
46960         }
46961         
46962         this.keyNav = new Roo.KeyNav(this.el, {
46963             
46964             "tab" : function(e){
46965                 e.preventDefault();
46966                 
46967                 var value = this.getValue();
46968                 
46969                 var start = this.el.dom.selectionStart;
46970                 var end = this.el.dom.selectionEnd;
46971                 
46972                 if(!e.shiftKey){
46973                     
46974                     this.setValue(value.substring(0, start) + "\t" + value.substring(end));
46975                     this.el.dom.setSelectionRange(end + 1, end + 1);
46976                     return;
46977                 }
46978                 
46979                 var f = value.substring(0, start).split("\t");
46980                 
46981                 if(f.pop().length != 0){
46982                     return;
46983                 }
46984                 
46985                 this.setValue(f.join("\t") + value.substring(end));
46986                 this.el.dom.setSelectionRange(start - 1, start - 1);
46987                 
46988             },
46989             
46990             "home" : function(e){
46991                 e.preventDefault();
46992                 
46993                 var curr = this.el.dom.selectionStart;
46994                 var lines = this.getValue().split("\n");
46995                 
46996                 if(!lines.length){
46997                     return;
46998                 }
46999                 
47000                 if(e.ctrlKey){
47001                     this.el.dom.setSelectionRange(0, 0);
47002                     return;
47003                 }
47004                 
47005                 var pos = 0;
47006                 
47007                 for (var i = 0; i < lines.length;i++) {
47008                     pos += lines[i].length;
47009                     
47010                     if(i != 0){
47011                         pos += 1;
47012                     }
47013                     
47014                     if(pos < curr){
47015                         continue;
47016                     }
47017                     
47018                     pos -= lines[i].length;
47019                     
47020                     break;
47021                 }
47022                 
47023                 if(!e.shiftKey){
47024                     this.el.dom.setSelectionRange(pos, pos);
47025                     return;
47026                 }
47027                 
47028                 this.el.dom.selectionStart = pos;
47029                 this.el.dom.selectionEnd = curr;
47030             },
47031             
47032             "end" : function(e){
47033                 e.preventDefault();
47034                 
47035                 var curr = this.el.dom.selectionStart;
47036                 var lines = this.getValue().split("\n");
47037                 
47038                 if(!lines.length){
47039                     return;
47040                 }
47041                 
47042                 if(e.ctrlKey){
47043                     this.el.dom.setSelectionRange(this.getValue().length, this.getValue().length);
47044                     return;
47045                 }
47046                 
47047                 var pos = 0;
47048                 
47049                 for (var i = 0; i < lines.length;i++) {
47050                     
47051                     pos += lines[i].length;
47052                     
47053                     if(i != 0){
47054                         pos += 1;
47055                     }
47056                     
47057                     if(pos < curr){
47058                         continue;
47059                     }
47060                     
47061                     break;
47062                 }
47063                 
47064                 if(!e.shiftKey){
47065                     this.el.dom.setSelectionRange(pos, pos);
47066                     return;
47067                 }
47068                 
47069                 this.el.dom.selectionStart = curr;
47070                 this.el.dom.selectionEnd = pos;
47071             },
47072
47073             scope : this,
47074
47075             doRelay : function(foo, bar, hname){
47076                 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
47077             },
47078
47079             forceKeyDown: true
47080         });
47081         
47082 //        if(this.autosave && this.w){
47083 //            this.autoSaveFn = setInterval(this.autosave, 1000);
47084 //        }
47085     },
47086
47087     // private
47088     onResize : function(w, h)
47089     {
47090         Roo.form.HtmlEditor.superclass.onResize.apply(this, arguments);
47091         var ew = false;
47092         var eh = false;
47093         
47094         if(this.el ){
47095             if(typeof w == 'number'){
47096                 var aw = w - this.wrap.getFrameWidth('lr');
47097                 this.el.setWidth(this.adjustWidth('textarea', aw));
47098                 ew = aw;
47099             }
47100             if(typeof h == 'number'){
47101                 var tbh = 0;
47102                 for (var i =0; i < this.toolbars.length;i++) {
47103                     // fixme - ask toolbars for heights?
47104                     tbh += this.toolbars[i].tb.el.getHeight();
47105                     if (this.toolbars[i].footer) {
47106                         tbh += this.toolbars[i].footer.el.getHeight();
47107                     }
47108                 }
47109                 
47110                 
47111                 
47112                 
47113                 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
47114                 ah -= 5; // knock a few pixes off for look..
47115 //                Roo.log(ah);
47116                 this.el.setHeight(this.adjustWidth('textarea', ah));
47117                 var eh = ah;
47118             }
47119         }
47120         Roo.log('onResize:' + [w,h,ew,eh].join(',') );
47121         this.editorcore.onResize(ew,eh);
47122         
47123     },
47124
47125     /**
47126      * Toggles the editor between standard and source edit mode.
47127      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
47128      */
47129     toggleSourceEdit : function(sourceEditMode)
47130     {
47131         this.editorcore.toggleSourceEdit(sourceEditMode);
47132         
47133         if(this.editorcore.sourceEditMode){
47134             Roo.log('editor - showing textarea');
47135             
47136 //            Roo.log('in');
47137 //            Roo.log(this.syncValue());
47138             this.editorcore.syncValue();
47139             this.el.removeClass('x-hidden');
47140             this.el.dom.removeAttribute('tabIndex');
47141             this.el.focus();
47142             this.el.dom.scrollTop = 0;
47143             
47144             
47145             for (var i = 0; i < this.toolbars.length; i++) {
47146                 if(this.toolbars[i] instanceof Roo.form.HtmlEditor.ToolbarContext){
47147                     this.toolbars[i].tb.hide();
47148                     this.toolbars[i].footer.hide();
47149                 }
47150             }
47151             
47152         }else{
47153             Roo.log('editor - hiding textarea');
47154 //            Roo.log('out')
47155 //            Roo.log(this.pushValue()); 
47156             this.editorcore.pushValue();
47157             
47158             this.el.addClass('x-hidden');
47159             this.el.dom.setAttribute('tabIndex', -1);
47160             
47161             for (var i = 0; i < this.toolbars.length; i++) {
47162                 if(this.toolbars[i] instanceof Roo.form.HtmlEditor.ToolbarContext){
47163                     this.toolbars[i].tb.show();
47164                     this.toolbars[i].footer.show();
47165                 }
47166             }
47167             
47168             //this.deferFocus();
47169         }
47170         
47171         this.setSize(this.wrap.getSize());
47172         this.onResize(this.wrap.getSize().width, this.wrap.getSize().height);
47173         
47174         this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
47175     },
47176  
47177     // private (for BoxComponent)
47178     adjustSize : Roo.BoxComponent.prototype.adjustSize,
47179
47180     // private (for BoxComponent)
47181     getResizeEl : function(){
47182         return this.wrap;
47183     },
47184
47185     // private (for BoxComponent)
47186     getPositionEl : function(){
47187         return this.wrap;
47188     },
47189
47190     // private
47191     initEvents : function(){
47192         this.originalValue = this.getValue();
47193     },
47194
47195     /**
47196      * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
47197      * @method
47198      */
47199     markInvalid : Roo.emptyFn,
47200     /**
47201      * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
47202      * @method
47203      */
47204     clearInvalid : Roo.emptyFn,
47205
47206     setValue : function(v){
47207         Roo.form.HtmlEditor.superclass.setValue.call(this, v);
47208         this.editorcore.pushValue();
47209     },
47210
47211      
47212     // private
47213     deferFocus : function(){
47214         this.focus.defer(10, this);
47215     },
47216
47217     // doc'ed in Field
47218     focus : function(){
47219         this.editorcore.focus();
47220         
47221     },
47222       
47223
47224     // private
47225     onDestroy : function(){
47226         
47227         
47228         
47229         if(this.rendered){
47230             
47231             for (var i =0; i < this.toolbars.length;i++) {
47232                 // fixme - ask toolbars for heights?
47233                 this.toolbars[i].onDestroy();
47234             }
47235             
47236             this.wrap.dom.innerHTML = '';
47237             this.wrap.remove();
47238         }
47239     },
47240
47241     // private
47242     onFirstFocus : function(){
47243         //Roo.log("onFirstFocus");
47244         this.editorcore.onFirstFocus();
47245          for (var i =0; i < this.toolbars.length;i++) {
47246             this.toolbars[i].onFirstFocus();
47247         }
47248         
47249     },
47250     
47251     // private
47252     syncValue : function()
47253     {
47254         this.editorcore.syncValue();
47255     },
47256     
47257     pushValue : function()
47258     {
47259         this.editorcore.pushValue();
47260     },
47261     
47262     setStylesheets : function(stylesheets)
47263     {
47264         this.editorcore.setStylesheets(stylesheets);
47265     },
47266     
47267     removeStylesheets : function()
47268     {
47269         this.editorcore.removeStylesheets();
47270     }
47271      
47272     
47273     // hide stuff that is not compatible
47274     /**
47275      * @event blur
47276      * @hide
47277      */
47278     /**
47279      * @event change
47280      * @hide
47281      */
47282     /**
47283      * @event focus
47284      * @hide
47285      */
47286     /**
47287      * @event specialkey
47288      * @hide
47289      */
47290     /**
47291      * @cfg {String} fieldClass @hide
47292      */
47293     /**
47294      * @cfg {String} focusClass @hide
47295      */
47296     /**
47297      * @cfg {String} autoCreate @hide
47298      */
47299     /**
47300      * @cfg {String} inputType @hide
47301      */
47302     /**
47303      * @cfg {String} invalidClass @hide
47304      */
47305     /**
47306      * @cfg {String} invalidText @hide
47307      */
47308     /**
47309      * @cfg {String} msgFx @hide
47310      */
47311     /**
47312      * @cfg {String} validateOnBlur @hide
47313      */
47314 });
47315  
47316     // <script type="text/javascript">
47317 /*
47318  * Based on
47319  * Ext JS Library 1.1.1
47320  * Copyright(c) 2006-2007, Ext JS, LLC.
47321  *  
47322  
47323  */
47324
47325 /**
47326  * @class Roo.form.HtmlEditorToolbar1
47327  * Basic Toolbar
47328  * 
47329  * Usage:
47330  *
47331  new Roo.form.HtmlEditor({
47332     ....
47333     toolbars : [
47334         new Roo.form.HtmlEditorToolbar1({
47335             disable : { fonts: 1 , format: 1, ..., ... , ...],
47336             btns : [ .... ]
47337         })
47338     }
47339      
47340  * 
47341  * @cfg {Object} disable List of elements to disable..
47342  * @cfg {Array} btns List of additional buttons.
47343  * 
47344  * 
47345  * NEEDS Extra CSS? 
47346  * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
47347  */
47348  
47349 Roo.form.HtmlEditor.ToolbarStandard = function(config)
47350 {
47351     
47352     Roo.apply(this, config);
47353     
47354     // default disabled, based on 'good practice'..
47355     this.disable = this.disable || {};
47356     Roo.applyIf(this.disable, {
47357         fontSize : true,
47358         colors : true,
47359         specialElements : true
47360     });
47361     
47362     
47363     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
47364     // dont call parent... till later.
47365 }
47366
47367 Roo.apply(Roo.form.HtmlEditor.ToolbarStandard.prototype,  {
47368     
47369     tb: false,
47370     
47371     rendered: false,
47372     
47373     editor : false,
47374     editorcore : false,
47375     /**
47376      * @cfg {Object} disable  List of toolbar elements to disable
47377          
47378      */
47379     disable : false,
47380     
47381     
47382      /**
47383      * @cfg {String} createLinkText The default text for the create link prompt
47384      */
47385     createLinkText : 'Please enter the URL for the link:',
47386     /**
47387      * @cfg {String} defaultLinkValue The default value for the create link prompt (defaults to http:/ /)
47388      */
47389     defaultLinkValue : 'http:/'+'/',
47390    
47391     
47392       /**
47393      * @cfg {Array} fontFamilies An array of available font families
47394      */
47395     fontFamilies : [
47396         'Arial',
47397         'Courier New',
47398         'Tahoma',
47399         'Times New Roman',
47400         'Verdana'
47401     ],
47402     
47403     specialChars : [
47404            "&#169;",
47405           "&#174;",     
47406           "&#8482;",    
47407           "&#163;" ,    
47408          // "&#8212;",    
47409           "&#8230;",    
47410           "&#247;" ,    
47411         //  "&#225;" ,     ?? a acute?
47412            "&#8364;"    , //Euro
47413        //   "&#8220;"    ,
47414         //  "&#8221;"    ,
47415         //  "&#8226;"    ,
47416           "&#176;"  //   , // degrees
47417
47418          // "&#233;"     , // e ecute
47419          // "&#250;"     , // u ecute?
47420     ],
47421     
47422     specialElements : [
47423         {
47424             text: "Insert Table",
47425             xtype: 'MenuItem',
47426             xns : Roo.Menu,
47427             ihtml :  '<table><tr><td>Cell</td></tr></table>' 
47428                 
47429         },
47430         {    
47431             text: "Insert Image",
47432             xtype: 'MenuItem',
47433             xns : Roo.Menu,
47434             ihtml : '<img src="about:blank"/>'
47435             
47436         }
47437         
47438          
47439     ],
47440     
47441     
47442     inputElements : [ 
47443             "form", "input:text", "input:hidden", "input:checkbox", "input:radio", "input:password", 
47444             "input:submit", "input:button", "select", "textarea", "label" ],
47445     formats : [
47446         ["p"] ,  
47447         ["h1"],["h2"],["h3"],["h4"],["h5"],["h6"], 
47448         ["pre"],[ "code"], 
47449         ["abbr"],[ "acronym"],[ "address"],[ "cite"],[ "samp"],[ "var"],
47450         ['div'],['span'],
47451         ['sup'],['sub']
47452     ],
47453     
47454     cleanStyles : [
47455         "font-size"
47456     ],
47457      /**
47458      * @cfg {String} defaultFont default font to use.
47459      */
47460     defaultFont: 'tahoma',
47461    
47462     fontSelect : false,
47463     
47464     
47465     formatCombo : false,
47466     
47467     init : function(editor)
47468     {
47469         this.editor = editor;
47470         this.editorcore = editor.editorcore ? editor.editorcore : editor;
47471         var editorcore = this.editorcore;
47472         
47473         var _t = this;
47474         
47475         var fid = editorcore.frameId;
47476         var etb = this;
47477         function btn(id, toggle, handler){
47478             var xid = fid + '-'+ id ;
47479             return {
47480                 id : xid,
47481                 cmd : id,
47482                 cls : 'x-btn-icon x-edit-'+id,
47483                 enableToggle:toggle !== false,
47484                 scope: _t, // was editor...
47485                 handler:handler||_t.relayBtnCmd,
47486                 clickEvent:'mousedown',
47487                 tooltip: etb.buttonTips[id] || undefined, ///tips ???
47488                 tabIndex:-1
47489             };
47490         }
47491         
47492         
47493         
47494         var tb = new Roo.Toolbar(editor.wrap.dom.firstChild);
47495         this.tb = tb;
47496          // stop form submits
47497         tb.el.on('click', function(e){
47498             e.preventDefault(); // what does this do?
47499         });
47500
47501         if(!this.disable.font) { // && !Roo.isSafari){
47502             /* why no safari for fonts 
47503             editor.fontSelect = tb.el.createChild({
47504                 tag:'select',
47505                 tabIndex: -1,
47506                 cls:'x-font-select',
47507                 html: this.createFontOptions()
47508             });
47509             
47510             editor.fontSelect.on('change', function(){
47511                 var font = editor.fontSelect.dom.value;
47512                 editor.relayCmd('fontname', font);
47513                 editor.deferFocus();
47514             }, editor);
47515             
47516             tb.add(
47517                 editor.fontSelect.dom,
47518                 '-'
47519             );
47520             */
47521             
47522         };
47523         if(!this.disable.formats){
47524             this.formatCombo = new Roo.form.ComboBox({
47525                 store: new Roo.data.SimpleStore({
47526                     id : 'tag',
47527                     fields: ['tag'],
47528                     data : this.formats // from states.js
47529                 }),
47530                 blockFocus : true,
47531                 name : '',
47532                 //autoCreate : {tag: "div",  size: "20"},
47533                 displayField:'tag',
47534                 typeAhead: false,
47535                 mode: 'local',
47536                 editable : false,
47537                 triggerAction: 'all',
47538                 emptyText:'Add tag',
47539                 selectOnFocus:true,
47540                 width:135,
47541                 listeners : {
47542                     'select': function(c, r, i) {
47543                         editorcore.insertTag(r.get('tag'));
47544                         editor.focus();
47545                     }
47546                 }
47547
47548             });
47549             tb.addField(this.formatCombo);
47550             
47551         }
47552         
47553         if(!this.disable.format){
47554             tb.add(
47555                 btn('bold'),
47556                 btn('italic'),
47557                 btn('underline'),
47558                 btn('strikethrough')
47559             );
47560         };
47561         if(!this.disable.fontSize){
47562             tb.add(
47563                 '-',
47564                 
47565                 
47566                 btn('increasefontsize', false, editorcore.adjustFont),
47567                 btn('decreasefontsize', false, editorcore.adjustFont)
47568             );
47569         };
47570         
47571         
47572         if(!this.disable.colors){
47573             tb.add(
47574                 '-', {
47575                     id:editorcore.frameId +'-forecolor',
47576                     cls:'x-btn-icon x-edit-forecolor',
47577                     clickEvent:'mousedown',
47578                     tooltip: this.buttonTips['forecolor'] || undefined,
47579                     tabIndex:-1,
47580                     menu : new Roo.menu.ColorMenu({
47581                         allowReselect: true,
47582                         focus: Roo.emptyFn,
47583                         value:'000000',
47584                         plain:true,
47585                         selectHandler: function(cp, color){
47586                             editorcore.execCmd('forecolor', Roo.isSafari || Roo.isIE ? '#'+color : color);
47587                             editor.deferFocus();
47588                         },
47589                         scope: editorcore,
47590                         clickEvent:'mousedown'
47591                     })
47592                 }, {
47593                     id:editorcore.frameId +'backcolor',
47594                     cls:'x-btn-icon x-edit-backcolor',
47595                     clickEvent:'mousedown',
47596                     tooltip: this.buttonTips['backcolor'] || undefined,
47597                     tabIndex:-1,
47598                     menu : new Roo.menu.ColorMenu({
47599                         focus: Roo.emptyFn,
47600                         value:'FFFFFF',
47601                         plain:true,
47602                         allowReselect: true,
47603                         selectHandler: function(cp, color){
47604                             if(Roo.isGecko){
47605                                 editorcore.execCmd('useCSS', false);
47606                                 editorcore.execCmd('hilitecolor', color);
47607                                 editorcore.execCmd('useCSS', true);
47608                                 editor.deferFocus();
47609                             }else{
47610                                 editorcore.execCmd(Roo.isOpera ? 'hilitecolor' : 'backcolor', 
47611                                     Roo.isSafari || Roo.isIE ? '#'+color : color);
47612                                 editor.deferFocus();
47613                             }
47614                         },
47615                         scope:editorcore,
47616                         clickEvent:'mousedown'
47617                     })
47618                 }
47619             );
47620         };
47621         // now add all the items...
47622         
47623
47624         if(!this.disable.alignments){
47625             tb.add(
47626                 '-',
47627                 btn('justifyleft'),
47628                 btn('justifycenter'),
47629                 btn('justifyright')
47630             );
47631         };
47632
47633         //if(!Roo.isSafari){
47634             if(!this.disable.links){
47635                 tb.add(
47636                     '-',
47637                     btn('createlink', false, this.createLink)    /// MOVE TO HERE?!!?!?!?!
47638                 );
47639             };
47640
47641             if(!this.disable.lists){
47642                 tb.add(
47643                     '-',
47644                     btn('insertorderedlist'),
47645                     btn('insertunorderedlist')
47646                 );
47647             }
47648             if(!this.disable.sourceEdit){
47649                 tb.add(
47650                     '-',
47651                     btn('sourceedit', true, function(btn){
47652                         this.toggleSourceEdit(btn.pressed);
47653                     })
47654                 );
47655             }
47656         //}
47657         
47658         var smenu = { };
47659         // special menu.. - needs to be tidied up..
47660         if (!this.disable.special) {
47661             smenu = {
47662                 text: "&#169;",
47663                 cls: 'x-edit-none',
47664                 
47665                 menu : {
47666                     items : []
47667                 }
47668             };
47669             for (var i =0; i < this.specialChars.length; i++) {
47670                 smenu.menu.items.push({
47671                     
47672                     html: this.specialChars[i],
47673                     handler: function(a,b) {
47674                         editorcore.insertAtCursor(String.fromCharCode(a.html.replace('&#','').replace(';', '')));
47675                         //editor.insertAtCursor(a.html);
47676                         
47677                     },
47678                     tabIndex:-1
47679                 });
47680             }
47681             
47682             
47683             tb.add(smenu);
47684             
47685             
47686         }
47687         
47688         var cmenu = { };
47689         if (!this.disable.cleanStyles) {
47690             cmenu = {
47691                 cls: 'x-btn-icon x-btn-clear',
47692                 
47693                 menu : {
47694                     items : []
47695                 }
47696             };
47697             for (var i =0; i < this.cleanStyles.length; i++) {
47698                 cmenu.menu.items.push({
47699                     actiontype : this.cleanStyles[i],
47700                     html: 'Remove ' + this.cleanStyles[i],
47701                     handler: function(a,b) {
47702 //                        Roo.log(a);
47703 //                        Roo.log(b);
47704                         var c = Roo.get(editorcore.doc.body);
47705                         c.select('[style]').each(function(s) {
47706                             s.dom.style.removeProperty(a.actiontype);
47707                         });
47708                         editorcore.syncValue();
47709                     },
47710                     tabIndex:-1
47711                 });
47712             }
47713             cmenu.menu.items.push({
47714                 actiontype : 'tablewidths',
47715                 html: 'Remove Table Widths',
47716                 handler: function(a,b) {
47717                     editorcore.cleanTableWidths();
47718                     editorcore.syncValue();
47719                 },
47720                 tabIndex:-1
47721             });
47722             cmenu.menu.items.push({
47723                 actiontype : 'word',
47724                 html: 'Remove MS Word Formating',
47725                 handler: function(a,b) {
47726                     editorcore.cleanWord();
47727                     editorcore.syncValue();
47728                 },
47729                 tabIndex:-1
47730             });
47731             
47732             cmenu.menu.items.push({
47733                 actiontype : 'all',
47734                 html: 'Remove All Styles',
47735                 handler: function(a,b) {
47736                     
47737                     var c = Roo.get(editorcore.doc.body);
47738                     c.select('[style]').each(function(s) {
47739                         s.dom.removeAttribute('style');
47740                     });
47741                     editorcore.syncValue();
47742                 },
47743                 tabIndex:-1
47744             });
47745             
47746             cmenu.menu.items.push({
47747                 actiontype : 'all',
47748                 html: 'Remove All CSS Classes',
47749                 handler: function(a,b) {
47750                     
47751                     var c = Roo.get(editorcore.doc.body);
47752                     c.select('[class]').each(function(s) {
47753                         s.dom.removeAttribute('class');
47754                     });
47755                     editorcore.cleanWord();
47756                     editorcore.syncValue();
47757                 },
47758                 tabIndex:-1
47759             });
47760             
47761              cmenu.menu.items.push({
47762                 actiontype : 'tidy',
47763                 html: 'Tidy HTML Source',
47764                 handler: function(a,b) {
47765                     new Roo.htmleditor.Tidy(editorcore.doc.body);
47766                     editorcore.syncValue();
47767                 },
47768                 tabIndex:-1
47769             });
47770             
47771             
47772             tb.add(cmenu);
47773         }
47774          
47775         if (!this.disable.specialElements) {
47776             var semenu = {
47777                 text: "Other;",
47778                 cls: 'x-edit-none',
47779                 menu : {
47780                     items : []
47781                 }
47782             };
47783             for (var i =0; i < this.specialElements.length; i++) {
47784                 semenu.menu.items.push(
47785                     Roo.apply({ 
47786                         handler: function(a,b) {
47787                             editor.insertAtCursor(this.ihtml);
47788                         }
47789                     }, this.specialElements[i])
47790                 );
47791                     
47792             }
47793             
47794             tb.add(semenu);
47795             
47796             
47797         }
47798          
47799         
47800         if (this.btns) {
47801             for(var i =0; i< this.btns.length;i++) {
47802                 var b = Roo.factory(this.btns[i],Roo.form);
47803                 b.cls =  'x-edit-none';
47804                 
47805                 if(typeof(this.btns[i].cls) != 'undefined' && this.btns[i].cls.indexOf('x-init-enable') !== -1){
47806                     b.cls += ' x-init-enable';
47807                 }
47808                 
47809                 b.scope = editorcore;
47810                 tb.add(b);
47811             }
47812         
47813         }
47814         
47815         
47816         
47817         // disable everything...
47818         
47819         this.tb.items.each(function(item){
47820             
47821            if(
47822                 item.id != editorcore.frameId+ '-sourceedit' && 
47823                 (typeof(item.cls) != 'undefined' && item.cls.indexOf('x-init-enable') === -1)
47824             ){
47825                 
47826                 item.disable();
47827             }
47828         });
47829         this.rendered = true;
47830         
47831         // the all the btns;
47832         editor.on('editorevent', this.updateToolbar, this);
47833         // other toolbars need to implement this..
47834         //editor.on('editmodechange', this.updateToolbar, this);
47835     },
47836     
47837     
47838     relayBtnCmd : function(btn) {
47839         this.editorcore.relayCmd(btn.cmd);
47840     },
47841     // private used internally
47842     createLink : function(){
47843         Roo.log("create link?");
47844         var url = prompt(this.createLinkText, this.defaultLinkValue);
47845         if(url && url != 'http:/'+'/'){
47846             this.editorcore.relayCmd('createlink', url);
47847         }
47848     },
47849
47850     
47851     /**
47852      * Protected method that will not generally be called directly. It triggers
47853      * a toolbar update by reading the markup state of the current selection in the editor.
47854      */
47855     updateToolbar: function(){
47856
47857         if(!this.editorcore.activated){
47858             this.editor.onFirstFocus();
47859             return;
47860         }
47861
47862         var btns = this.tb.items.map, 
47863             doc = this.editorcore.doc,
47864             frameId = this.editorcore.frameId;
47865
47866         if(!this.disable.font && !Roo.isSafari){
47867             /*
47868             var name = (doc.queryCommandValue('FontName')||this.editor.defaultFont).toLowerCase();
47869             if(name != this.fontSelect.dom.value){
47870                 this.fontSelect.dom.value = name;
47871             }
47872             */
47873         }
47874         if(!this.disable.format){
47875             btns[frameId + '-bold'].toggle(doc.queryCommandState('bold'));
47876             btns[frameId + '-italic'].toggle(doc.queryCommandState('italic'));
47877             btns[frameId + '-underline'].toggle(doc.queryCommandState('underline'));
47878             btns[frameId + '-strikethrough'].toggle(doc.queryCommandState('strikethrough'));
47879         }
47880         if(!this.disable.alignments){
47881             btns[frameId + '-justifyleft'].toggle(doc.queryCommandState('justifyleft'));
47882             btns[frameId + '-justifycenter'].toggle(doc.queryCommandState('justifycenter'));
47883             btns[frameId + '-justifyright'].toggle(doc.queryCommandState('justifyright'));
47884         }
47885         if(!Roo.isSafari && !this.disable.lists){
47886             btns[frameId + '-insertorderedlist'].toggle(doc.queryCommandState('insertorderedlist'));
47887             btns[frameId + '-insertunorderedlist'].toggle(doc.queryCommandState('insertunorderedlist'));
47888         }
47889         
47890         var ans = this.editorcore.getAllAncestors();
47891         if (this.formatCombo) {
47892             
47893             
47894             var store = this.formatCombo.store;
47895             this.formatCombo.setValue("");
47896             for (var i =0; i < ans.length;i++) {
47897                 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
47898                     // select it..
47899                     this.formatCombo.setValue(ans[i].tagName.toLowerCase());
47900                     break;
47901                 }
47902             }
47903         }
47904         
47905         
47906         
47907         // hides menus... - so this cant be on a menu...
47908         Roo.menu.MenuMgr.hideAll();
47909
47910         //this.editorsyncValue();
47911     },
47912    
47913     
47914     createFontOptions : function(){
47915         var buf = [], fs = this.fontFamilies, ff, lc;
47916         
47917         
47918         
47919         for(var i = 0, len = fs.length; i< len; i++){
47920             ff = fs[i];
47921             lc = ff.toLowerCase();
47922             buf.push(
47923                 '<option value="',lc,'" style="font-family:',ff,';"',
47924                     (this.defaultFont == lc ? ' selected="true">' : '>'),
47925                     ff,
47926                 '</option>'
47927             );
47928         }
47929         return buf.join('');
47930     },
47931     
47932     toggleSourceEdit : function(sourceEditMode){
47933         
47934         Roo.log("toolbar toogle");
47935         if(sourceEditMode === undefined){
47936             sourceEditMode = !this.sourceEditMode;
47937         }
47938         this.sourceEditMode = sourceEditMode === true;
47939         var btn = this.tb.items.get(this.editorcore.frameId +'-sourceedit');
47940         // just toggle the button?
47941         if(btn.pressed !== this.sourceEditMode){
47942             btn.toggle(this.sourceEditMode);
47943             return;
47944         }
47945         
47946         if(sourceEditMode){
47947             Roo.log("disabling buttons");
47948             this.tb.items.each(function(item){
47949                 if(item.cmd != 'sourceedit' && (typeof(item.cls) != 'undefined' && item.cls.indexOf('x-init-enable') === -1)){
47950                     item.disable();
47951                 }
47952             });
47953           
47954         }else{
47955             Roo.log("enabling buttons");
47956             if(this.editorcore.initialized){
47957                 this.tb.items.each(function(item){
47958                     item.enable();
47959                 });
47960             }
47961             
47962         }
47963         Roo.log("calling toggole on editor");
47964         // tell the editor that it's been pressed..
47965         this.editor.toggleSourceEdit(sourceEditMode);
47966        
47967     },
47968      /**
47969      * Object collection of toolbar tooltips for the buttons in the editor. The key
47970      * is the command id associated with that button and the value is a valid QuickTips object.
47971      * For example:
47972 <pre><code>
47973 {
47974     bold : {
47975         title: 'Bold (Ctrl+B)',
47976         text: 'Make the selected text bold.',
47977         cls: 'x-html-editor-tip'
47978     },
47979     italic : {
47980         title: 'Italic (Ctrl+I)',
47981         text: 'Make the selected text italic.',
47982         cls: 'x-html-editor-tip'
47983     },
47984     ...
47985 </code></pre>
47986     * @type Object
47987      */
47988     buttonTips : {
47989         bold : {
47990             title: 'Bold (Ctrl+B)',
47991             text: 'Make the selected text bold.',
47992             cls: 'x-html-editor-tip'
47993         },
47994         italic : {
47995             title: 'Italic (Ctrl+I)',
47996             text: 'Make the selected text italic.',
47997             cls: 'x-html-editor-tip'
47998         },
47999         underline : {
48000             title: 'Underline (Ctrl+U)',
48001             text: 'Underline the selected text.',
48002             cls: 'x-html-editor-tip'
48003         },
48004         strikethrough : {
48005             title: 'Strikethrough',
48006             text: 'Strikethrough the selected text.',
48007             cls: 'x-html-editor-tip'
48008         },
48009         increasefontsize : {
48010             title: 'Grow Text',
48011             text: 'Increase the font size.',
48012             cls: 'x-html-editor-tip'
48013         },
48014         decreasefontsize : {
48015             title: 'Shrink Text',
48016             text: 'Decrease the font size.',
48017             cls: 'x-html-editor-tip'
48018         },
48019         backcolor : {
48020             title: 'Text Highlight Color',
48021             text: 'Change the background color of the selected text.',
48022             cls: 'x-html-editor-tip'
48023         },
48024         forecolor : {
48025             title: 'Font Color',
48026             text: 'Change the color of the selected text.',
48027             cls: 'x-html-editor-tip'
48028         },
48029         justifyleft : {
48030             title: 'Align Text Left',
48031             text: 'Align text to the left.',
48032             cls: 'x-html-editor-tip'
48033         },
48034         justifycenter : {
48035             title: 'Center Text',
48036             text: 'Center text in the editor.',
48037             cls: 'x-html-editor-tip'
48038         },
48039         justifyright : {
48040             title: 'Align Text Right',
48041             text: 'Align text to the right.',
48042             cls: 'x-html-editor-tip'
48043         },
48044         insertunorderedlist : {
48045             title: 'Bullet List',
48046             text: 'Start a bulleted list.',
48047             cls: 'x-html-editor-tip'
48048         },
48049         insertorderedlist : {
48050             title: 'Numbered List',
48051             text: 'Start a numbered list.',
48052             cls: 'x-html-editor-tip'
48053         },
48054         createlink : {
48055             title: 'Hyperlink',
48056             text: 'Make the selected text a hyperlink.',
48057             cls: 'x-html-editor-tip'
48058         },
48059         sourceedit : {
48060             title: 'Source Edit',
48061             text: 'Switch to source editing mode.',
48062             cls: 'x-html-editor-tip'
48063         }
48064     },
48065     // private
48066     onDestroy : function(){
48067         if(this.rendered){
48068             
48069             this.tb.items.each(function(item){
48070                 if(item.menu){
48071                     item.menu.removeAll();
48072                     if(item.menu.el){
48073                         item.menu.el.destroy();
48074                     }
48075                 }
48076                 item.destroy();
48077             });
48078              
48079         }
48080     },
48081     onFirstFocus: function() {
48082         this.tb.items.each(function(item){
48083            item.enable();
48084         });
48085     }
48086 });
48087
48088
48089
48090
48091 // <script type="text/javascript">
48092 /*
48093  * Based on
48094  * Ext JS Library 1.1.1
48095  * Copyright(c) 2006-2007, Ext JS, LLC.
48096  *  
48097  
48098  */
48099
48100  
48101 /**
48102  * @class Roo.form.HtmlEditor.ToolbarContext
48103  * Context Toolbar
48104  * 
48105  * Usage:
48106  *
48107  new Roo.form.HtmlEditor({
48108     ....
48109     toolbars : [
48110         { xtype: 'ToolbarStandard', styles : {} }
48111         { xtype: 'ToolbarContext', disable : {} }
48112     ]
48113 })
48114
48115      
48116  * 
48117  * @config : {Object} disable List of elements to disable.. (not done yet.)
48118  * @config : {Object} styles  Map of styles available.
48119  * 
48120  */
48121
48122 Roo.form.HtmlEditor.ToolbarContext = function(config)
48123 {
48124     
48125     Roo.apply(this, config);
48126     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
48127     // dont call parent... till later.
48128     this.styles = this.styles || {};
48129 }
48130
48131  
48132
48133 Roo.form.HtmlEditor.ToolbarContext.types = {
48134     'IMG' : {
48135         width : {
48136             title: "Width",
48137             width: 40
48138         },
48139         height:  {
48140             title: "Height",
48141             width: 40
48142         },
48143         align: {
48144             title: "Align",
48145             opts : [ [""],[ "left"],[ "right"],[ "center"],[ "top"]],
48146             width : 80
48147             
48148         },
48149         border: {
48150             title: "Border",
48151             width: 40
48152         },
48153         alt: {
48154             title: "Alt",
48155             width: 120
48156         },
48157         src : {
48158             title: "Src",
48159             width: 220
48160         }
48161         
48162     },
48163     
48164     'FIGURE' : {
48165          align: {
48166             title: "Align",
48167             opts : [ [""],[ "left"],[ "right"],[ "center"],[ "top"]],
48168             width : 80  
48169         }
48170     },
48171     'A' : {
48172         name : {
48173             title: "Name",
48174             width: 50
48175         },
48176         target:  {
48177             title: "Target",
48178             width: 120
48179         },
48180         href:  {
48181             title: "Href",
48182             width: 220
48183         } // border?
48184         
48185     },
48186     'TABLE' : {
48187         rows : {
48188             title: "Rows",
48189             width: 20
48190         },
48191         cols : {
48192             title: "Cols",
48193             width: 20
48194         },
48195         width : {
48196             title: "Width",
48197             width: 40
48198         },
48199         height : {
48200             title: "Height",
48201             width: 40
48202         },
48203         border : {
48204             title: "Border",
48205             width: 20
48206         }
48207     },
48208     'TD' : {
48209         width : {
48210             title: "Width",
48211             width: 40
48212         },
48213         height : {
48214             title: "Height",
48215             width: 40
48216         },   
48217         align: {
48218             title: "Align",
48219             opts : [[""],[ "left"],[ "center"],[ "right"],[ "justify"],[ "char"]],
48220             width: 80
48221         },
48222         valign: {
48223             title: "Valign",
48224             opts : [[""],[ "top"],[ "middle"],[ "bottom"],[ "baseline"]],
48225             width: 80
48226         },
48227         colspan: {
48228             title: "Colspan",
48229             width: 20
48230             
48231         },
48232          'font-family'  : {
48233             title : "Font",
48234             style : 'fontFamily',
48235             displayField: 'display',
48236             optname : 'font-family',
48237             width: 140
48238         }
48239     },
48240     'INPUT' : {
48241         name : {
48242             title: "name",
48243             width: 120
48244         },
48245         value : {
48246             title: "Value",
48247             width: 120
48248         },
48249         width : {
48250             title: "Width",
48251             width: 40
48252         }
48253     },
48254     'LABEL' : {
48255         'for' : {
48256             title: "For",
48257             width: 120
48258         }
48259     },
48260     'TEXTAREA' : {
48261         name : {
48262             title: "name",
48263             width: 120
48264         },
48265         rows : {
48266             title: "Rows",
48267             width: 20
48268         },
48269         cols : {
48270             title: "Cols",
48271             width: 20
48272         }
48273     },
48274     'SELECT' : {
48275         name : {
48276             title: "name",
48277             width: 120
48278         },
48279         selectoptions : {
48280             title: "Options",
48281             width: 200
48282         }
48283     },
48284     
48285     // should we really allow this??
48286     // should this just be 
48287     'BODY' : {
48288         title : {
48289             title: "Title",
48290             width: 200,
48291             disabled : true
48292         }
48293     },
48294     /*
48295     'SPAN' : {
48296         'font-family'  : {
48297             title : "Font",
48298             style : 'fontFamily',
48299             displayField: 'display',
48300             optname : 'font-family',
48301             width: 140
48302         }
48303     },
48304     'DIV' : {
48305         'font-family'  : {
48306             title : "Font",
48307             style : 'fontFamily',
48308             displayField: 'display',
48309             optname : 'font-family',
48310             width: 140
48311         }
48312     },
48313      'P' : {
48314         'font-family'  : {
48315             title : "Font",
48316             style : 'fontFamily',
48317             displayField: 'display',
48318             optname : 'font-family',
48319             width: 140
48320         }
48321     },
48322     */
48323     '*' : {
48324         // empty..
48325     }
48326
48327 };
48328
48329 // this should be configurable.. - you can either set it up using stores, or modify options somehwere..
48330 Roo.form.HtmlEditor.ToolbarContext.stores = false;
48331
48332 Roo.form.HtmlEditor.ToolbarContext.options = {
48333         'font-family'  : [ 
48334                 [ 'Helvetica,Arial,sans-serif', 'Helvetica'],
48335                 [ 'Courier New', 'Courier New'],
48336                 [ 'Tahoma', 'Tahoma'],
48337                 [ 'Times New Roman,serif', 'Times'],
48338                 [ 'Verdana','Verdana' ]
48339         ]
48340 };
48341
48342 // fixme - these need to be configurable..
48343  
48344
48345 //Roo.form.HtmlEditor.ToolbarContext.types
48346
48347
48348 Roo.apply(Roo.form.HtmlEditor.ToolbarContext.prototype,  {
48349     
48350     tb: false,
48351     
48352     rendered: false,
48353     
48354     editor : false,
48355     editorcore : false,
48356     /**
48357      * @cfg {Object} disable  List of toolbar elements to disable
48358          
48359      */
48360     disable : false,
48361     /**
48362      * @cfg {Object} styles List of styles 
48363      *    eg. { '*' : [ 'headline' ] , 'TD' : [ 'underline', 'double-underline' ] } 
48364      *
48365      * These must be defined in the page, so they get rendered correctly..
48366      * .headline { }
48367      * TD.underline { }
48368      * 
48369      */
48370     styles : false,
48371     
48372     options: false,
48373     
48374     toolbars : false,
48375     
48376     init : function(editor)
48377     {
48378         this.editor = editor;
48379         this.editorcore = editor.editorcore ? editor.editorcore : editor;
48380         var editorcore = this.editorcore;
48381         
48382         var fid = editorcore.frameId;
48383         var etb = this;
48384         function btn(id, toggle, handler){
48385             var xid = fid + '-'+ id ;
48386             return {
48387                 id : xid,
48388                 cmd : id,
48389                 cls : 'x-btn-icon x-edit-'+id,
48390                 enableToggle:toggle !== false,
48391                 scope: editorcore, // was editor...
48392                 handler:handler||editorcore.relayBtnCmd,
48393                 clickEvent:'mousedown',
48394                 tooltip: etb.buttonTips[id] || undefined, ///tips ???
48395                 tabIndex:-1
48396             };
48397         }
48398         // create a new element.
48399         var wdiv = editor.wrap.createChild({
48400                 tag: 'div'
48401             }, editor.wrap.dom.firstChild.nextSibling, true);
48402         
48403         // can we do this more than once??
48404         
48405          // stop form submits
48406       
48407  
48408         // disable everything...
48409         var ty= Roo.form.HtmlEditor.ToolbarContext.types;
48410         this.toolbars = {};
48411            
48412         for (var i in  ty) {
48413           
48414             this.toolbars[i] = this.buildToolbar(ty[i],i);
48415         }
48416         this.tb = this.toolbars.BODY;
48417         this.tb.el.show();
48418         this.buildFooter();
48419         this.footer.show();
48420         editor.on('hide', function( ) { this.footer.hide() }, this);
48421         editor.on('show', function( ) { this.footer.show() }, this);
48422         
48423          
48424         this.rendered = true;
48425         
48426         // the all the btns;
48427         editor.on('editorevent', this.updateToolbar, this);
48428         // other toolbars need to implement this..
48429         //editor.on('editmodechange', this.updateToolbar, this);
48430     },
48431     
48432     
48433     
48434     /**
48435      * Protected method that will not generally be called directly. It triggers
48436      * a toolbar update by reading the markup state of the current selection in the editor.
48437      *
48438      * Note you can force an update by calling on('editorevent', scope, false)
48439      */
48440     updateToolbar: function(editor,ev,sel){
48441
48442         //Roo.log(ev);
48443         // capture mouse up - this is handy for selecting images..
48444         // perhaps should go somewhere else...
48445         if(!this.editorcore.activated){
48446              this.editor.onFirstFocus();
48447             return;
48448         }
48449         
48450         
48451         
48452         // http://developer.yahoo.com/yui/docs/simple-editor.js.html
48453         // selectNode - might want to handle IE?
48454         if (ev &&
48455             (ev.type == 'mouseup' || ev.type == 'click' ) &&
48456             ev.target && ev.target.tagName == 'IMG') {
48457             // they have click on an image...
48458             // let's see if we can change the selection...
48459             sel = ev.target;
48460          
48461             var nodeRange = sel.ownerDocument.createRange();
48462             try {
48463                 nodeRange.selectNode(sel);
48464             } catch (e) {
48465                 nodeRange.selectNodeContents(sel);
48466             }
48467             //nodeRange.collapse(true);
48468             var s = this.editorcore.win.getSelection();
48469             s.removeAllRanges();
48470             s.addRange(nodeRange);
48471         }  
48472         
48473       
48474         //var updateFooter = sel ? false : true; 
48475         
48476         
48477         var ans = this.editorcore.getAllAncestors();
48478         
48479         // pick
48480         var ty= Roo.form.HtmlEditor.ToolbarContext.types;
48481         
48482         if (!sel) { 
48483             sel = ans.length ? (ans[0] ?  ans[0]  : ans[1]) : this.editorcore.doc.body;
48484             sel = sel ? sel : this.editorcore.doc.body;
48485             sel = sel.tagName.length ? sel : this.editorcore.doc.body;
48486             
48487         }
48488         // pick a menu that exists..
48489         var tn = sel.tagName.toUpperCase();
48490         //sel = typeof(ty[tn]) != 'undefined' ? sel : this.editor.doc.body;
48491         
48492         tn = sel.tagName.toUpperCase();
48493         
48494         var lastSel = this.tb.selectedNode;
48495         
48496         this.tb.selectedNode = sel;
48497         
48498         // if current menu does not match..
48499         
48500         if ((this.tb.name != tn) || (lastSel != this.tb.selectedNode) || ev === false) {
48501                 
48502             this.tb.el.hide();
48503             ///console.log("show: " + tn);
48504             this.tb =  typeof(ty[tn]) != 'undefined' ? this.toolbars[tn] : this.toolbars['*'];
48505             this.tb.el.show();
48506             // update name
48507             this.tb.items.first().el.innerHTML = tn + ':&nbsp;';
48508             
48509             
48510             // update attributes
48511             if (this.tb.fields) {
48512                 this.tb.fields.each(function(e) {
48513                     if (e.stylename) {
48514                         e.setValue(sel.style[e.stylename]);
48515                         return;
48516                     } 
48517                    e.setValue(sel.getAttribute(e.attrname));
48518                 });
48519             }
48520             
48521             var hasStyles = false;
48522             for(var i in this.styles) {
48523                 hasStyles = true;
48524                 break;
48525             }
48526             
48527             // update styles
48528             if (hasStyles) { 
48529                 var st = this.tb.fields.item(0);
48530                 
48531                 st.store.removeAll();
48532                
48533                 
48534                 var cn = sel.className.split(/\s+/);
48535                 
48536                 var avs = [];
48537                 if (this.styles['*']) {
48538                     
48539                     Roo.each(this.styles['*'], function(v) {
48540                         avs.push( [ v , cn.indexOf(v) > -1 ? 1 : 0 ] );         
48541                     });
48542                 }
48543                 if (this.styles[tn]) { 
48544                     Roo.each(this.styles[tn], function(v) {
48545                         avs.push( [ v , cn.indexOf(v) > -1 ? 1 : 0 ] );         
48546                     });
48547                 }
48548                 
48549                 st.store.loadData(avs);
48550                 st.collapse();
48551                 st.setValue(cn);
48552             }
48553             // flag our selected Node.
48554             this.tb.selectedNode = sel;
48555            
48556            
48557             Roo.menu.MenuMgr.hideAll();
48558
48559         }
48560         
48561         ///if (!updateFooter) {
48562             //this.footDisp.dom.innerHTML = ''; 
48563          //   return;
48564         //}
48565         // update the footer
48566         //
48567         var html = '';
48568         
48569         this.footerEls = ans.reverse();
48570         Roo.each(this.footerEls, function(a,i) {
48571             if (!a) { return; }
48572             html += html.length ? ' &gt; '  :  '';
48573             
48574             html += '<span class="x-ed-loc-' + i + '">' + a.tagName + '</span>';
48575             
48576         });
48577        
48578         // 
48579         var sz = this.footDisp.up('td').getSize();
48580         this.footDisp.dom.style.width = (sz.width -10) + 'px';
48581         this.footDisp.dom.style.marginLeft = '5px';
48582         
48583         this.footDisp.dom.style.overflow = 'hidden';
48584         
48585         this.footDisp.dom.innerHTML = html;
48586             
48587         //this.editorsyncValue();
48588     },
48589      
48590     
48591    
48592        
48593     // private
48594     onDestroy : function(){
48595         if(this.rendered){
48596             
48597             this.tb.items.each(function(item){
48598                 if(item.menu){
48599                     item.menu.removeAll();
48600                     if(item.menu.el){
48601                         item.menu.el.destroy();
48602                     }
48603                 }
48604                 item.destroy();
48605             });
48606              
48607         }
48608     },
48609     onFirstFocus: function() {
48610         // need to do this for all the toolbars..
48611         this.tb.items.each(function(item){
48612            item.enable();
48613         });
48614     },
48615     buildToolbar: function(tlist, nm)
48616     {
48617         var editor = this.editor;
48618         var editorcore = this.editorcore;
48619          // create a new element.
48620         var wdiv = editor.wrap.createChild({
48621                 tag: 'div'
48622             }, editor.wrap.dom.firstChild.nextSibling, true);
48623         
48624        
48625         var tb = new Roo.Toolbar(wdiv);
48626         // add the name..
48627         
48628         tb.add(nm+ ":&nbsp;");
48629         
48630         var styles = [];
48631         for(var i in this.styles) {
48632             styles.push(i);
48633         }
48634         
48635         // styles...
48636         if (styles && styles.length) {
48637             
48638             // this needs a multi-select checkbox...
48639             tb.addField( new Roo.form.ComboBox({
48640                 store: new Roo.data.SimpleStore({
48641                     id : 'val',
48642                     fields: ['val', 'selected'],
48643                     data : [] 
48644                 }),
48645                 name : '-roo-edit-className',
48646                 attrname : 'className',
48647                 displayField: 'val',
48648                 typeAhead: false,
48649                 mode: 'local',
48650                 editable : false,
48651                 triggerAction: 'all',
48652                 emptyText:'Select Style',
48653                 selectOnFocus:true,
48654                 width: 130,
48655                 listeners : {
48656                     'select': function(c, r, i) {
48657                         // initial support only for on class per el..
48658                         tb.selectedNode.className =  r ? r.get('val') : '';
48659                         editorcore.syncValue();
48660                     }
48661                 }
48662     
48663             }));
48664         }
48665         
48666         var tbc = Roo.form.HtmlEditor.ToolbarContext;
48667         var tbops = tbc.options;
48668         
48669         for (var i in tlist) {
48670             
48671             var item = tlist[i];
48672             tb.add(item.title + ":&nbsp;");
48673             
48674             
48675             //optname == used so you can configure the options available..
48676             var opts = item.opts ? item.opts : false;
48677             if (item.optname) {
48678                 opts = tbops[item.optname];
48679            
48680             }
48681             
48682             if (opts) {
48683                 // opts == pulldown..
48684                 tb.addField( new Roo.form.ComboBox({
48685                     store:   typeof(tbc.stores[i]) != 'undefined' ?  Roo.factory(tbc.stores[i],Roo.data) : new Roo.data.SimpleStore({
48686                         id : 'val',
48687                         fields: ['val', 'display'],
48688                         data : opts  
48689                     }),
48690                     name : '-roo-edit-' + i,
48691                     attrname : i,
48692                     stylename : item.style ? item.style : false,
48693                     displayField: item.displayField ? item.displayField : 'val',
48694                     valueField :  'val',
48695                     typeAhead: false,
48696                     mode: typeof(tbc.stores[i]) != 'undefined'  ? 'remote' : 'local',
48697                     editable : false,
48698                     triggerAction: 'all',
48699                     emptyText:'Select',
48700                     selectOnFocus:true,
48701                     width: item.width ? item.width  : 130,
48702                     listeners : {
48703                         'select': function(c, r, i) {
48704                             if (c.stylename) {
48705                                 tb.selectedNode.style[c.stylename] =  r.get('val');
48706                                 return;
48707                             }
48708                             if (r === false) {
48709                                 tb.selectedNode.removeAttribute(c.attrname);
48710                                 return;
48711                             }
48712                             tb.selectedNode.setAttribute(c.attrname, r.get('val'));
48713                         }
48714                     }
48715
48716                 }));
48717                 continue;
48718                     
48719                  
48720                 
48721                 tb.addField( new Roo.form.TextField({
48722                     name: i,
48723                     width: 100,
48724                     //allowBlank:false,
48725                     value: ''
48726                 }));
48727                 continue;
48728             }
48729             tb.addField( new Roo.form.TextField({
48730                 name: '-roo-edit-' + i,
48731                 attrname : i,
48732                 
48733                 width: item.width,
48734                 //allowBlank:true,
48735                 value: '',
48736                 listeners: {
48737                     'change' : function(f, nv, ov) {
48738                         tb.selectedNode.setAttribute(f.attrname, nv);
48739                         editorcore.syncValue();
48740                     }
48741                 }
48742             }));
48743              
48744         }
48745         
48746         var _this = this;
48747         
48748         if(nm == 'BODY'){
48749             tb.addSeparator();
48750         
48751             tb.addButton( {
48752                 text: 'Stylesheets',
48753
48754                 listeners : {
48755                     click : function ()
48756                     {
48757                         _this.editor.fireEvent('stylesheetsclick', _this.editor);
48758                     }
48759                 }
48760             });
48761         }
48762         
48763         tb.addFill();
48764         tb.addButton( {
48765             text: 'Remove Tag', // remove the tag, and puts the children outside...
48766     
48767             listeners : {
48768                 click : function ()
48769                 {
48770                     // remove
48771                     // undo does not work.
48772                      
48773                     var sn = tb.selectedNode;
48774                     
48775                     var pn = sn.parentNode;
48776                     
48777                     // what i'm going to select after deleting..
48778                     var stn =  sn.childNodes[0] || sn.nextSibling || sn.previousSibling || pn;
48779                     
48780                     if (!stn) {
48781                         stn = sn.nextSibling;
48782                     }
48783                     var en = sn.childNodes[sn.childNodes.length - 1 ];
48784                     while (sn.childNodes.length) {
48785                         var node = sn.childNodes[0];
48786                         sn.removeChild(node);
48787                         //Roo.log(node);
48788                         pn.insertBefore(node, sn);
48789                         
48790                     }
48791                     pn.removeChild(sn);
48792                     var range = editorcore.createRange();
48793         
48794                     range.setStart(stn,0);
48795                     range.setEnd(en,0); //????
48796                     //range.selectNode(sel);
48797                     
48798                     
48799                     var selection = editorcore.getSelection();
48800                     selection.removeAllRanges();
48801                     selection.addRange(range);
48802                     
48803                     
48804                     
48805                     //_this.updateToolbar(null, null, pn);
48806                     _this.updateToolbar(null, null, null);
48807                     _this.footDisp.dom.innerHTML = ''; 
48808                 }
48809             }
48810             
48811                     
48812                 
48813             
48814         });
48815         
48816         
48817         tb.el.on('click', function(e){
48818             e.preventDefault(); // what does this do?
48819         });
48820         tb.el.setVisibilityMode( Roo.Element.DISPLAY);
48821         tb.el.hide();
48822         tb.name = nm;
48823         // dont need to disable them... as they will get hidden
48824         return tb;
48825          
48826         
48827     },
48828     buildFooter : function()
48829     {
48830         
48831         var fel = this.editor.wrap.createChild();
48832         this.footer = new Roo.Toolbar(fel);
48833         // toolbar has scrolly on left / right?
48834         var footDisp= new Roo.Toolbar.Fill();
48835         var _t = this;
48836         this.footer.add(
48837             {
48838                 text : '&lt;',
48839                 xtype: 'Button',
48840                 handler : function() {
48841                     _t.footDisp.scrollTo('left',0,true)
48842                 }
48843             }
48844         );
48845         this.footer.add( footDisp );
48846         this.footer.add( 
48847             {
48848                 text : '&gt;',
48849                 xtype: 'Button',
48850                 handler : function() {
48851                     // no animation..
48852                     _t.footDisp.select('span').last().scrollIntoView(_t.footDisp,true);
48853                 }
48854             }
48855         );
48856         var fel = Roo.get(footDisp.el);
48857         fel.addClass('x-editor-context');
48858         this.footDispWrap = fel; 
48859         this.footDispWrap.overflow  = 'hidden';
48860         
48861         this.footDisp = fel.createChild();
48862         this.footDispWrap.on('click', this.onContextClick, this)
48863         
48864         
48865     },
48866     onContextClick : function (ev,dom)
48867     {
48868         ev.preventDefault();
48869         var  cn = dom.className;
48870         //Roo.log(cn);
48871         if (!cn.match(/x-ed-loc-/)) {
48872             return;
48873         }
48874         var n = cn.split('-').pop();
48875         var ans = this.footerEls;
48876         var sel = ans[n];
48877         
48878          // pick
48879         var range = this.editorcore.createRange();
48880         
48881         range.selectNodeContents(sel);
48882         //range.selectNode(sel);
48883         
48884         
48885         var selection = this.editorcore.getSelection();
48886         selection.removeAllRanges();
48887         selection.addRange(range);
48888         
48889         
48890         
48891         this.updateToolbar(null, null, sel);
48892         
48893         
48894     }
48895     
48896     
48897     
48898     
48899     
48900 });
48901
48902
48903
48904
48905
48906 /*
48907  * Based on:
48908  * Ext JS Library 1.1.1
48909  * Copyright(c) 2006-2007, Ext JS, LLC.
48910  *
48911  * Originally Released Under LGPL - original licence link has changed is not relivant.
48912  *
48913  * Fork - LGPL
48914  * <script type="text/javascript">
48915  */
48916  
48917 /**
48918  * @class Roo.form.BasicForm
48919  * @extends Roo.util.Observable
48920  * Supplies the functionality to do "actions" on forms and initialize Roo.form.Field types on existing markup.
48921  * @constructor
48922  * @param {String/HTMLElement/Roo.Element} el The form element or its id
48923  * @param {Object} config Configuration options
48924  */
48925 Roo.form.BasicForm = function(el, config){
48926     this.allItems = [];
48927     this.childForms = [];
48928     Roo.apply(this, config);
48929     /*
48930      * The Roo.form.Field items in this form.
48931      * @type MixedCollection
48932      */
48933      
48934      
48935     this.items = new Roo.util.MixedCollection(false, function(o){
48936         return o.id || (o.id = Roo.id());
48937     });
48938     this.addEvents({
48939         /**
48940          * @event beforeaction
48941          * Fires before any action is performed. Return false to cancel the action.
48942          * @param {Form} this
48943          * @param {Action} action The action to be performed
48944          */
48945         beforeaction: true,
48946         /**
48947          * @event actionfailed
48948          * Fires when an action fails.
48949          * @param {Form} this
48950          * @param {Action} action The action that failed
48951          */
48952         actionfailed : true,
48953         /**
48954          * @event actioncomplete
48955          * Fires when an action is completed.
48956          * @param {Form} this
48957          * @param {Action} action The action that completed
48958          */
48959         actioncomplete : true
48960     });
48961     if(el){
48962         this.initEl(el);
48963     }
48964     Roo.form.BasicForm.superclass.constructor.call(this);
48965     
48966     Roo.form.BasicForm.popover.apply();
48967 };
48968
48969 Roo.extend(Roo.form.BasicForm, Roo.util.Observable, {
48970     /**
48971      * @cfg {String} method
48972      * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
48973      */
48974     /**
48975      * @cfg {DataReader} reader
48976      * An Roo.data.DataReader (e.g. {@link Roo.data.XmlReader}) to be used to read data when executing "load" actions.
48977      * This is optional as there is built-in support for processing JSON.
48978      */
48979     /**
48980      * @cfg {DataReader} errorReader
48981      * An Roo.data.DataReader (e.g. {@link Roo.data.XmlReader}) to be used to read data when reading validation errors on "submit" actions.
48982      * This is completely optional as there is built-in support for processing JSON.
48983      */
48984     /**
48985      * @cfg {String} url
48986      * The URL to use for form actions if one isn't supplied in the action options.
48987      */
48988     /**
48989      * @cfg {Boolean} fileUpload
48990      * Set to true if this form is a file upload.
48991      */
48992      
48993     /**
48994      * @cfg {Object} baseParams
48995      * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
48996      */
48997      /**
48998      
48999     /**
49000      * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
49001      */
49002     timeout: 30,
49003
49004     // private
49005     activeAction : null,
49006
49007     /**
49008      * @cfg {Boolean} trackResetOnLoad If set to true, form.reset() resets to the last loaded
49009      * or setValues() data instead of when the form was first created.
49010      */
49011     trackResetOnLoad : false,
49012     
49013     
49014     /**
49015      * childForms - used for multi-tab forms
49016      * @type {Array}
49017      */
49018     childForms : false,
49019     
49020     /**
49021      * allItems - full list of fields.
49022      * @type {Array}
49023      */
49024     allItems : false,
49025     
49026     /**
49027      * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
49028      * element by passing it or its id or mask the form itself by passing in true.
49029      * @type Mixed
49030      */
49031     waitMsgTarget : false,
49032     
49033     /**
49034      * @type Boolean
49035      */
49036     disableMask : false,
49037     
49038     /**
49039      * @cfg {Boolean} errorMask (true|false) default false
49040      */
49041     errorMask : false,
49042     
49043     /**
49044      * @cfg {Number} maskOffset Default 100
49045      */
49046     maskOffset : 100,
49047
49048     // private
49049     initEl : function(el){
49050         this.el = Roo.get(el);
49051         this.id = this.el.id || Roo.id();
49052         this.el.on('submit', this.onSubmit, this);
49053         this.el.addClass('x-form');
49054     },
49055
49056     // private
49057     onSubmit : function(e){
49058         e.stopEvent();
49059     },
49060
49061     /**
49062      * Returns true if client-side validation on the form is successful.
49063      * @return Boolean
49064      */
49065     isValid : function(){
49066         var valid = true;
49067         var target = false;
49068         this.items.each(function(f){
49069             if(f.validate()){
49070                 return;
49071             }
49072             
49073             valid = false;
49074                 
49075             if(!target && f.el.isVisible(true)){
49076                 target = f;
49077             }
49078         });
49079         
49080         if(this.errorMask && !valid){
49081             Roo.form.BasicForm.popover.mask(this, target);
49082         }
49083         
49084         return valid;
49085     },
49086     /**
49087      * Returns array of invalid form fields.
49088      * @return Array
49089      */
49090     
49091     invalidFields : function()
49092     {
49093         var ret = [];
49094         this.items.each(function(f){
49095             if(f.validate()){
49096                 return;
49097             }
49098             ret.push(f);
49099             
49100         });
49101         
49102         return ret;
49103     },
49104     
49105     
49106     /**
49107      * DEPRICATED Returns true if any fields in this form have changed since their original load. 
49108      * @return Boolean
49109      */
49110     isDirty : function(){
49111         var dirty = false;
49112         this.items.each(function(f){
49113            if(f.isDirty()){
49114                dirty = true;
49115                return false;
49116            }
49117         });
49118         return dirty;
49119     },
49120     
49121     /**
49122      * Returns true if any fields in this form have changed since their original load. (New version)
49123      * @return Boolean
49124      */
49125     
49126     hasChanged : function()
49127     {
49128         var dirty = false;
49129         this.items.each(function(f){
49130            if(f.hasChanged()){
49131                dirty = true;
49132                return false;
49133            }
49134         });
49135         return dirty;
49136         
49137     },
49138     /**
49139      * Resets all hasChanged to 'false' -
49140      * The old 'isDirty' used 'original value..' however this breaks reset() and a few other things.
49141      * So hasChanged storage is only to be used for this purpose
49142      * @return Boolean
49143      */
49144     resetHasChanged : function()
49145     {
49146         this.items.each(function(f){
49147            f.resetHasChanged();
49148         });
49149         
49150     },
49151     
49152     
49153     /**
49154      * Performs a predefined action (submit or load) or custom actions you define on this form.
49155      * @param {String} actionName The name of the action type
49156      * @param {Object} options (optional) The options to pass to the action.  All of the config options listed
49157      * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
49158      * accept other config options):
49159      * <pre>
49160 Property          Type             Description
49161 ----------------  ---------------  ----------------------------------------------------------------------------------
49162 url               String           The url for the action (defaults to the form's url)
49163 method            String           The form method to use (defaults to the form's method, or POST if not defined)
49164 params            String/Object    The params to pass (defaults to the form's baseParams, or none if not defined)
49165 clientValidation  Boolean          Applies to submit only.  Pass true to call form.isValid() prior to posting to
49166                                    validate the form on the client (defaults to false)
49167      * </pre>
49168      * @return {BasicForm} this
49169      */
49170     doAction : function(action, options){
49171         if(typeof action == 'string'){
49172             action = new Roo.form.Action.ACTION_TYPES[action](this, options);
49173         }
49174         if(this.fireEvent('beforeaction', this, action) !== false){
49175             this.beforeAction(action);
49176             action.run.defer(100, action);
49177         }
49178         return this;
49179     },
49180
49181     /**
49182      * Shortcut to do a submit action.
49183      * @param {Object} options The options to pass to the action (see {@link #doAction} for details)
49184      * @return {BasicForm} this
49185      */
49186     submit : function(options){
49187         this.doAction('submit', options);
49188         return this;
49189     },
49190
49191     /**
49192      * Shortcut to do a load action.
49193      * @param {Object} options The options to pass to the action (see {@link #doAction} for details)
49194      * @return {BasicForm} this
49195      */
49196     load : function(options){
49197         this.doAction('load', options);
49198         return this;
49199     },
49200
49201     /**
49202      * Persists the values in this form into the passed Roo.data.Record object in a beginEdit/endEdit block.
49203      * @param {Record} record The record to edit
49204      * @return {BasicForm} this
49205      */
49206     updateRecord : function(record){
49207         record.beginEdit();
49208         var fs = record.fields;
49209         fs.each(function(f){
49210             var field = this.findField(f.name);
49211             if(field){
49212                 record.set(f.name, field.getValue());
49213             }
49214         }, this);
49215         record.endEdit();
49216         return this;
49217     },
49218
49219     /**
49220      * Loads an Roo.data.Record into this form.
49221      * @param {Record} record The record to load
49222      * @return {BasicForm} this
49223      */
49224     loadRecord : function(record){
49225         this.setValues(record.data);
49226         return this;
49227     },
49228
49229     // private
49230     beforeAction : function(action){
49231         var o = action.options;
49232         
49233         if(!this.disableMask) {
49234             if(this.waitMsgTarget === true){
49235                 this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
49236             }else if(this.waitMsgTarget){
49237                 this.waitMsgTarget = Roo.get(this.waitMsgTarget);
49238                 this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
49239             }else {
49240                 Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
49241             }
49242         }
49243         
49244          
49245     },
49246
49247     // private
49248     afterAction : function(action, success){
49249         this.activeAction = null;
49250         var o = action.options;
49251         
49252         if(!this.disableMask) {
49253             if(this.waitMsgTarget === true){
49254                 this.el.unmask();
49255             }else if(this.waitMsgTarget){
49256                 this.waitMsgTarget.unmask();
49257             }else{
49258                 Roo.MessageBox.updateProgress(1);
49259                 Roo.MessageBox.hide();
49260             }
49261         }
49262         
49263         if(success){
49264             if(o.reset){
49265                 this.reset();
49266             }
49267             Roo.callback(o.success, o.scope, [this, action]);
49268             this.fireEvent('actioncomplete', this, action);
49269             
49270         }else{
49271             
49272             // failure condition..
49273             // we have a scenario where updates need confirming.
49274             // eg. if a locking scenario exists..
49275             // we look for { errors : { needs_confirm : true }} in the response.
49276             if (
49277                 (typeof(action.result) != 'undefined')  &&
49278                 (typeof(action.result.errors) != 'undefined')  &&
49279                 (typeof(action.result.errors.needs_confirm) != 'undefined')
49280            ){
49281                 var _t = this;
49282                 Roo.MessageBox.confirm(
49283                     "Change requires confirmation",
49284                     action.result.errorMsg,
49285                     function(r) {
49286                         if (r != 'yes') {
49287                             return;
49288                         }
49289                         _t.doAction('submit', { params :  { _submit_confirmed : 1 } }  );
49290                     }
49291                     
49292                 );
49293                 
49294                 
49295                 
49296                 return;
49297             }
49298             
49299             Roo.callback(o.failure, o.scope, [this, action]);
49300             // show an error message if no failed handler is set..
49301             if (!this.hasListener('actionfailed')) {
49302                 Roo.MessageBox.alert("Error",
49303                     (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
49304                         action.result.errorMsg :
49305                         "Saving Failed, please check your entries or try again"
49306                 );
49307             }
49308             
49309             this.fireEvent('actionfailed', this, action);
49310         }
49311         
49312     },
49313
49314     /**
49315      * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
49316      * @param {String} id The value to search for
49317      * @return Field
49318      */
49319     findField : function(id){
49320         var field = this.items.get(id);
49321         if(!field){
49322             this.items.each(function(f){
49323                 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
49324                     field = f;
49325                     return false;
49326                 }
49327             });
49328         }
49329         return field || null;
49330     },
49331
49332     /**
49333      * Add a secondary form to this one, 
49334      * Used to provide tabbed forms. One form is primary, with hidden values 
49335      * which mirror the elements from the other forms.
49336      * 
49337      * @param {Roo.form.Form} form to add.
49338      * 
49339      */
49340     addForm : function(form)
49341     {
49342        
49343         if (this.childForms.indexOf(form) > -1) {
49344             // already added..
49345             return;
49346         }
49347         this.childForms.push(form);
49348         var n = '';
49349         Roo.each(form.allItems, function (fe) {
49350             
49351             n = typeof(fe.getName) == 'undefined' ? fe.name : fe.getName();
49352             if (this.findField(n)) { // already added..
49353                 return;
49354             }
49355             var add = new Roo.form.Hidden({
49356                 name : n
49357             });
49358             add.render(this.el);
49359             
49360             this.add( add );
49361         }, this);
49362         
49363     },
49364     /**
49365      * Mark fields in this form invalid in bulk.
49366      * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
49367      * @return {BasicForm} this
49368      */
49369     markInvalid : function(errors){
49370         if(errors instanceof Array){
49371             for(var i = 0, len = errors.length; i < len; i++){
49372                 var fieldError = errors[i];
49373                 var f = this.findField(fieldError.id);
49374                 if(f){
49375                     f.markInvalid(fieldError.msg);
49376                 }
49377             }
49378         }else{
49379             var field, id;
49380             for(id in errors){
49381                 if(typeof errors[id] != 'function' && (field = this.findField(id))){
49382                     field.markInvalid(errors[id]);
49383                 }
49384             }
49385         }
49386         Roo.each(this.childForms || [], function (f) {
49387             f.markInvalid(errors);
49388         });
49389         
49390         return this;
49391     },
49392
49393     /**
49394      * Set values for fields in this form in bulk.
49395      * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
49396      * @return {BasicForm} this
49397      */
49398     setValues : function(values){
49399         if(values instanceof Array){ // array of objects
49400             for(var i = 0, len = values.length; i < len; i++){
49401                 var v = values[i];
49402                 var f = this.findField(v.id);
49403                 if(f){
49404                     f.setValue(v.value);
49405                     if(this.trackResetOnLoad){
49406                         f.originalValue = f.getValue();
49407                     }
49408                 }
49409             }
49410         }else{ // object hash
49411             var field, id;
49412             for(id in values){
49413                 if(typeof values[id] != 'function' && (field = this.findField(id))){
49414                     
49415                     if (field.setFromData && 
49416                         field.valueField && 
49417                         field.displayField &&
49418                         // combos' with local stores can 
49419                         // be queried via setValue()
49420                         // to set their value..
49421                         (field.store && !field.store.isLocal)
49422                         ) {
49423                         // it's a combo
49424                         var sd = { };
49425                         sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
49426                         sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
49427                         field.setFromData(sd);
49428                         
49429                     } else {
49430                         field.setValue(values[id]);
49431                     }
49432                     
49433                     
49434                     if(this.trackResetOnLoad){
49435                         field.originalValue = field.getValue();
49436                     }
49437                 }
49438             }
49439         }
49440         this.resetHasChanged();
49441         
49442         
49443         Roo.each(this.childForms || [], function (f) {
49444             f.setValues(values);
49445             f.resetHasChanged();
49446         });
49447                 
49448         return this;
49449     },
49450  
49451     /**
49452      * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
49453      * they are returned as an array.
49454      * @param {Boolean} asString
49455      * @return {Object}
49456      */
49457     getValues : function(asString){
49458         if (this.childForms) {
49459             // copy values from the child forms
49460             Roo.each(this.childForms, function (f) {
49461                 this.setValues(f.getValues());
49462             }, this);
49463         }
49464         
49465         // use formdata
49466         if (typeof(FormData) != 'undefined' && asString !== true) {
49467             // this relies on a 'recent' version of chrome apparently...
49468             try {
49469                 var fd = (new FormData(this.el.dom)).entries();
49470                 var ret = {};
49471                 var ent = fd.next();
49472                 while (!ent.done) {
49473                     ret[ent.value[0]] = ent.value[1]; // not sure how this will handle duplicates..
49474                     ent = fd.next();
49475                 };
49476                 return ret;
49477             } catch(e) {
49478                 
49479             }
49480             
49481         }
49482         
49483         
49484         var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
49485         if(asString === true){
49486             return fs;
49487         }
49488         return Roo.urlDecode(fs);
49489     },
49490     
49491     /**
49492      * Returns the fields in this form as an object with key/value pairs. 
49493      * This differs from getValues as it calls getValue on each child item, rather than using dom data.
49494      * @return {Object}
49495      */
49496     getFieldValues : function(with_hidden)
49497     {
49498         if (this.childForms) {
49499             // copy values from the child forms
49500             // should this call getFieldValues - probably not as we do not currently copy
49501             // hidden fields when we generate..
49502             Roo.each(this.childForms, function (f) {
49503                 this.setValues(f.getValues());
49504             }, this);
49505         }
49506         
49507         var ret = {};
49508         this.items.each(function(f){
49509             if (!f.getName()) {
49510                 return;
49511             }
49512             var v = f.getValue();
49513             if (f.inputType =='radio') {
49514                 if (typeof(ret[f.getName()]) == 'undefined') {
49515                     ret[f.getName()] = ''; // empty..
49516                 }
49517                 
49518                 if (!f.el.dom.checked) {
49519                     return;
49520                     
49521                 }
49522                 v = f.el.dom.value;
49523                 
49524             }
49525             
49526             // not sure if this supported any more..
49527             if ((typeof(v) == 'object') && f.getRawValue) {
49528                 v = f.getRawValue() ; // dates..
49529             }
49530             // combo boxes where name != hiddenName...
49531             if (f.name != f.getName()) {
49532                 ret[f.name] = f.getRawValue();
49533             }
49534             ret[f.getName()] = v;
49535         });
49536         
49537         return ret;
49538     },
49539
49540     /**
49541      * Clears all invalid messages in this form.
49542      * @return {BasicForm} this
49543      */
49544     clearInvalid : function(){
49545         this.items.each(function(f){
49546            f.clearInvalid();
49547         });
49548         
49549         Roo.each(this.childForms || [], function (f) {
49550             f.clearInvalid();
49551         });
49552         
49553         
49554         return this;
49555     },
49556
49557     /**
49558      * Resets this form.
49559      * @return {BasicForm} this
49560      */
49561     reset : function(){
49562         this.items.each(function(f){
49563             f.reset();
49564         });
49565         
49566         Roo.each(this.childForms || [], function (f) {
49567             f.reset();
49568         });
49569         this.resetHasChanged();
49570         
49571         return this;
49572     },
49573
49574     /**
49575      * Add Roo.form components to this form.
49576      * @param {Field} field1
49577      * @param {Field} field2 (optional)
49578      * @param {Field} etc (optional)
49579      * @return {BasicForm} this
49580      */
49581     add : function(){
49582         this.items.addAll(Array.prototype.slice.call(arguments, 0));
49583         return this;
49584     },
49585
49586
49587     /**
49588      * Removes a field from the items collection (does NOT remove its markup).
49589      * @param {Field} field
49590      * @return {BasicForm} this
49591      */
49592     remove : function(field){
49593         this.items.remove(field);
49594         return this;
49595     },
49596
49597     /**
49598      * Looks at the fields in this form, checks them for an id attribute,
49599      * and calls applyTo on the existing dom element with that id.
49600      * @return {BasicForm} this
49601      */
49602     render : function(){
49603         this.items.each(function(f){
49604             if(f.isFormField && !f.rendered && document.getElementById(f.id)){ // if the element exists
49605                 f.applyTo(f.id);
49606             }
49607         });
49608         return this;
49609     },
49610
49611     /**
49612      * Calls {@link Ext#apply} for all fields in this form with the passed object.
49613      * @param {Object} values
49614      * @return {BasicForm} this
49615      */
49616     applyToFields : function(o){
49617         this.items.each(function(f){
49618            Roo.apply(f, o);
49619         });
49620         return this;
49621     },
49622
49623     /**
49624      * Calls {@link Ext#applyIf} for all field in this form with the passed object.
49625      * @param {Object} values
49626      * @return {BasicForm} this
49627      */
49628     applyIfToFields : function(o){
49629         this.items.each(function(f){
49630            Roo.applyIf(f, o);
49631         });
49632         return this;
49633     }
49634 });
49635
49636 // back compat
49637 Roo.BasicForm = Roo.form.BasicForm;
49638
49639 Roo.apply(Roo.form.BasicForm, {
49640     
49641     popover : {
49642         
49643         padding : 5,
49644         
49645         isApplied : false,
49646         
49647         isMasked : false,
49648         
49649         form : false,
49650         
49651         target : false,
49652         
49653         intervalID : false,
49654         
49655         maskEl : false,
49656         
49657         apply : function()
49658         {
49659             if(this.isApplied){
49660                 return;
49661             }
49662             
49663             this.maskEl = {
49664                 top : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-top-mask" }, true),
49665                 left : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-left-mask" }, true),
49666                 bottom : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-bottom-mask" }, true),
49667                 right : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-right-mask" }, true)
49668             };
49669             
49670             this.maskEl.top.enableDisplayMode("block");
49671             this.maskEl.left.enableDisplayMode("block");
49672             this.maskEl.bottom.enableDisplayMode("block");
49673             this.maskEl.right.enableDisplayMode("block");
49674             
49675             Roo.get(document.body).on('click', function(){
49676                 this.unmask();
49677             }, this);
49678             
49679             Roo.get(document.body).on('touchstart', function(){
49680                 this.unmask();
49681             }, this);
49682             
49683             this.isApplied = true
49684         },
49685         
49686         mask : function(form, target)
49687         {
49688             this.form = form;
49689             
49690             this.target = target;
49691             
49692             if(!this.form.errorMask || !target.el){
49693                 return;
49694             }
49695             
49696             var scrollable = this.target.el.findScrollableParent() || this.target.el.findParent('div.x-layout-active-content', 100, true) || Roo.get(document.body);
49697             
49698             var ot = this.target.el.calcOffsetsTo(scrollable);
49699             
49700             var scrollTo = ot[1] - this.form.maskOffset;
49701             
49702             scrollTo = Math.min(scrollTo, scrollable.dom.scrollHeight);
49703             
49704             scrollable.scrollTo('top', scrollTo);
49705             
49706             var el = this.target.wrap || this.target.el;
49707             
49708             var box = el.getBox();
49709             
49710             this.maskEl.top.setStyle('position', 'absolute');
49711             this.maskEl.top.setStyle('z-index', 10000);
49712             this.maskEl.top.setSize(Roo.lib.Dom.getDocumentWidth(), box.y - this.padding);
49713             this.maskEl.top.setLeft(0);
49714             this.maskEl.top.setTop(0);
49715             this.maskEl.top.show();
49716             
49717             this.maskEl.left.setStyle('position', 'absolute');
49718             this.maskEl.left.setStyle('z-index', 10000);
49719             this.maskEl.left.setSize(box.x - this.padding, box.height + this.padding * 2);
49720             this.maskEl.left.setLeft(0);
49721             this.maskEl.left.setTop(box.y - this.padding);
49722             this.maskEl.left.show();
49723
49724             this.maskEl.bottom.setStyle('position', 'absolute');
49725             this.maskEl.bottom.setStyle('z-index', 10000);
49726             this.maskEl.bottom.setSize(Roo.lib.Dom.getDocumentWidth(), Roo.lib.Dom.getDocumentHeight() - box.bottom - this.padding);
49727             this.maskEl.bottom.setLeft(0);
49728             this.maskEl.bottom.setTop(box.bottom + this.padding);
49729             this.maskEl.bottom.show();
49730
49731             this.maskEl.right.setStyle('position', 'absolute');
49732             this.maskEl.right.setStyle('z-index', 10000);
49733             this.maskEl.right.setSize(Roo.lib.Dom.getDocumentWidth() - box.right - this.padding, box.height + this.padding * 2);
49734             this.maskEl.right.setLeft(box.right + this.padding);
49735             this.maskEl.right.setTop(box.y - this.padding);
49736             this.maskEl.right.show();
49737
49738             this.intervalID = window.setInterval(function() {
49739                 Roo.form.BasicForm.popover.unmask();
49740             }, 10000);
49741
49742             window.onwheel = function(){ return false;};
49743             
49744             (function(){ this.isMasked = true; }).defer(500, this);
49745             
49746         },
49747         
49748         unmask : function()
49749         {
49750             if(!this.isApplied || !this.isMasked || !this.form || !this.target || !this.form.errorMask){
49751                 return;
49752             }
49753             
49754             this.maskEl.top.setStyle('position', 'absolute');
49755             this.maskEl.top.setSize(0, 0).setXY([0, 0]);
49756             this.maskEl.top.hide();
49757
49758             this.maskEl.left.setStyle('position', 'absolute');
49759             this.maskEl.left.setSize(0, 0).setXY([0, 0]);
49760             this.maskEl.left.hide();
49761
49762             this.maskEl.bottom.setStyle('position', 'absolute');
49763             this.maskEl.bottom.setSize(0, 0).setXY([0, 0]);
49764             this.maskEl.bottom.hide();
49765
49766             this.maskEl.right.setStyle('position', 'absolute');
49767             this.maskEl.right.setSize(0, 0).setXY([0, 0]);
49768             this.maskEl.right.hide();
49769             
49770             window.onwheel = function(){ return true;};
49771             
49772             if(this.intervalID){
49773                 window.clearInterval(this.intervalID);
49774                 this.intervalID = false;
49775             }
49776             
49777             this.isMasked = false;
49778             
49779         }
49780         
49781     }
49782     
49783 });/*
49784  * Based on:
49785  * Ext JS Library 1.1.1
49786  * Copyright(c) 2006-2007, Ext JS, LLC.
49787  *
49788  * Originally Released Under LGPL - original licence link has changed is not relivant.
49789  *
49790  * Fork - LGPL
49791  * <script type="text/javascript">
49792  */
49793
49794 /**
49795  * @class Roo.form.Form
49796  * @extends Roo.form.BasicForm
49797  * @children Roo.form.Column Roo.form.FieldSet Roo.form.Row Roo.form.Field Roo.Button Roo.form.TextItem
49798  * Adds the ability to dynamically render forms with JavaScript to {@link Roo.form.BasicForm}.
49799  * @constructor
49800  * @param {Object} config Configuration options
49801  */
49802 Roo.form.Form = function(config){
49803     var xitems =  [];
49804     if (config.items) {
49805         xitems = config.items;
49806         delete config.items;
49807     }
49808    
49809     
49810     Roo.form.Form.superclass.constructor.call(this, null, config);
49811     this.url = this.url || this.action;
49812     if(!this.root){
49813         this.root = new Roo.form.Layout(Roo.applyIf({
49814             id: Roo.id()
49815         }, config));
49816     }
49817     this.active = this.root;
49818     /**
49819      * Array of all the buttons that have been added to this form via {@link addButton}
49820      * @type Array
49821      */
49822     this.buttons = [];
49823     this.allItems = [];
49824     this.addEvents({
49825         /**
49826          * @event clientvalidation
49827          * If the monitorValid config option is true, this event fires repetitively to notify of valid state
49828          * @param {Form} this
49829          * @param {Boolean} valid true if the form has passed client-side validation
49830          */
49831         clientvalidation: true,
49832         /**
49833          * @event rendered
49834          * Fires when the form is rendered
49835          * @param {Roo.form.Form} form
49836          */
49837         rendered : true
49838     });
49839     
49840     if (this.progressUrl) {
49841             // push a hidden field onto the list of fields..
49842             this.addxtype( {
49843                     xns: Roo.form, 
49844                     xtype : 'Hidden', 
49845                     name : 'UPLOAD_IDENTIFIER' 
49846             });
49847         }
49848         
49849     
49850     Roo.each(xitems, this.addxtype, this);
49851     
49852 };
49853
49854 Roo.extend(Roo.form.Form, Roo.form.BasicForm, {
49855      /**
49856      * @cfg {Roo.Button} buttons[] buttons at bottom of form
49857      */
49858     
49859     /**
49860      * @cfg {Number} labelWidth The width of labels. This property cascades to child containers.
49861      */
49862     /**
49863      * @cfg {String} itemCls A css class to apply to the x-form-item of fields. This property cascades to child containers.
49864      */
49865     /**
49866      * @cfg {String} buttonAlign Valid values are "left," "center" and "right" (defaults to "center")
49867      */
49868     buttonAlign:'center',
49869
49870     /**
49871      * @cfg {Number} minButtonWidth Minimum width of all buttons in pixels (defaults to 75)
49872      */
49873     minButtonWidth:75,
49874
49875     /**
49876      * @cfg {String} labelAlign Valid values are "left," "top" and "right" (defaults to "left").
49877      * This property cascades to child containers if not set.
49878      */
49879     labelAlign:'left',
49880
49881     /**
49882      * @cfg {Boolean} monitorValid If true the form monitors its valid state <b>client-side</b> and
49883      * fires a looping event with that state. This is required to bind buttons to the valid
49884      * state using the config value formBind:true on the button.
49885      */
49886     monitorValid : false,
49887
49888     /**
49889      * @cfg {Number} monitorPoll The milliseconds to poll valid state, ignored if monitorValid is not true (defaults to 200)
49890      */
49891     monitorPoll : 200,
49892     
49893     /**
49894      * @cfg {String} progressUrl - Url to return progress data 
49895      */
49896     
49897     progressUrl : false,
49898     /**
49899      * @cfg {boolean|FormData} formData - true to use new 'FormData' post, or set to a new FormData({dom form}) Object, if
49900      * sending a formdata with extra parameters - eg uploaded elements.
49901      */
49902     
49903     formData : false,
49904     
49905     /**
49906      * Opens a new {@link Roo.form.Column} container in the layout stack. If fields are passed after the config, the
49907      * fields are added and the column is closed. If no fields are passed the column remains open
49908      * until end() is called.
49909      * @param {Object} config The config to pass to the column
49910      * @param {Field} field1 (optional)
49911      * @param {Field} field2 (optional)
49912      * @param {Field} etc (optional)
49913      * @return Column The column container object
49914      */
49915     column : function(c){
49916         var col = new Roo.form.Column(c);
49917         this.start(col);
49918         if(arguments.length > 1){ // duplicate code required because of Opera
49919             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
49920             this.end();
49921         }
49922         return col;
49923     },
49924
49925     /**
49926      * Opens a new {@link Roo.form.FieldSet} container in the layout stack. If fields are passed after the config, the
49927      * fields are added and the fieldset is closed. If no fields are passed the fieldset remains open
49928      * until end() is called.
49929      * @param {Object} config The config to pass to the fieldset
49930      * @param {Field} field1 (optional)
49931      * @param {Field} field2 (optional)
49932      * @param {Field} etc (optional)
49933      * @return FieldSet The fieldset container object
49934      */
49935     fieldset : function(c){
49936         var fs = new Roo.form.FieldSet(c);
49937         this.start(fs);
49938         if(arguments.length > 1){ // duplicate code required because of Opera
49939             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
49940             this.end();
49941         }
49942         return fs;
49943     },
49944
49945     /**
49946      * Opens a new {@link Roo.form.Layout} container in the layout stack. If fields are passed after the config, the
49947      * fields are added and the container is closed. If no fields are passed the container remains open
49948      * until end() is called.
49949      * @param {Object} config The config to pass to the Layout
49950      * @param {Field} field1 (optional)
49951      * @param {Field} field2 (optional)
49952      * @param {Field} etc (optional)
49953      * @return Layout The container object
49954      */
49955     container : function(c){
49956         var l = new Roo.form.Layout(c);
49957         this.start(l);
49958         if(arguments.length > 1){ // duplicate code required because of Opera
49959             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
49960             this.end();
49961         }
49962         return l;
49963     },
49964
49965     /**
49966      * Opens the passed container in the layout stack. The container can be any {@link Roo.form.Layout} or subclass.
49967      * @param {Object} container A Roo.form.Layout or subclass of Layout
49968      * @return {Form} this
49969      */
49970     start : function(c){
49971         // cascade label info
49972         Roo.applyIf(c, {'labelAlign': this.active.labelAlign, 'labelWidth': this.active.labelWidth, 'itemCls': this.active.itemCls});
49973         this.active.stack.push(c);
49974         c.ownerCt = this.active;
49975         this.active = c;
49976         return this;
49977     },
49978
49979     /**
49980      * Closes the current open container
49981      * @return {Form} this
49982      */
49983     end : function(){
49984         if(this.active == this.root){
49985             return this;
49986         }
49987         this.active = this.active.ownerCt;
49988         return this;
49989     },
49990
49991     /**
49992      * Add Roo.form components to the current open container (e.g. column, fieldset, etc.).  Fields added via this method
49993      * can also be passed with an additional property of fieldLabel, which if supplied, will provide the text to display
49994      * as the label of the field.
49995      * @param {Field} field1
49996      * @param {Field} field2 (optional)
49997      * @param {Field} etc. (optional)
49998      * @return {Form} this
49999      */
50000     add : function(){
50001         this.active.stack.push.apply(this.active.stack, arguments);
50002         this.allItems.push.apply(this.allItems,arguments);
50003         var r = [];
50004         for(var i = 0, a = arguments, len = a.length; i < len; i++) {
50005             if(a[i].isFormField){
50006                 r.push(a[i]);
50007             }
50008         }
50009         if(r.length > 0){
50010             Roo.form.Form.superclass.add.apply(this, r);
50011         }
50012         return this;
50013     },
50014     
50015
50016     
50017     
50018     
50019      /**
50020      * Find any element that has been added to a form, using it's ID or name
50021      * This can include framesets, columns etc. along with regular fields..
50022      * @param {String} id - id or name to find.
50023      
50024      * @return {Element} e - or false if nothing found.
50025      */
50026     findbyId : function(id)
50027     {
50028         var ret = false;
50029         if (!id) {
50030             return ret;
50031         }
50032         Roo.each(this.allItems, function(f){
50033             if (f.id == id || f.name == id ){
50034                 ret = f;
50035                 return false;
50036             }
50037         });
50038         return ret;
50039     },
50040
50041     
50042     
50043     /**
50044      * Render this form into the passed container. This should only be called once!
50045      * @param {String/HTMLElement/Element} container The element this component should be rendered into
50046      * @return {Form} this
50047      */
50048     render : function(ct)
50049     {
50050         
50051         
50052         
50053         ct = Roo.get(ct);
50054         var o = this.autoCreate || {
50055             tag: 'form',
50056             method : this.method || 'POST',
50057             id : this.id || Roo.id()
50058         };
50059         this.initEl(ct.createChild(o));
50060
50061         this.root.render(this.el);
50062         
50063        
50064              
50065         this.items.each(function(f){
50066             f.render('x-form-el-'+f.id);
50067         });
50068
50069         if(this.buttons.length > 0){
50070             // tables are required to maintain order and for correct IE layout
50071             var tb = this.el.createChild({cls:'x-form-btns-ct', cn: {
50072                 cls:"x-form-btns x-form-btns-"+this.buttonAlign,
50073                 html:'<table cellspacing="0"><tbody><tr></tr></tbody></table><div class="x-clear"></div>'
50074             }}, null, true);
50075             var tr = tb.getElementsByTagName('tr')[0];
50076             for(var i = 0, len = this.buttons.length; i < len; i++) {
50077                 var b = this.buttons[i];
50078                 var td = document.createElement('td');
50079                 td.className = 'x-form-btn-td';
50080                 b.render(tr.appendChild(td));
50081             }
50082         }
50083         if(this.monitorValid){ // initialize after render
50084             this.startMonitoring();
50085         }
50086         this.fireEvent('rendered', this);
50087         return this;
50088     },
50089
50090     /**
50091      * Adds a button to the footer of the form - this <b>must</b> be called before the form is rendered.
50092      * @param {String/Object} config A string becomes the button text, an object can either be a Button config
50093      * object or a valid Roo.DomHelper element config
50094      * @param {Function} handler The function called when the button is clicked
50095      * @param {Object} scope (optional) The scope of the handler function
50096      * @return {Roo.Button}
50097      */
50098     addButton : function(config, handler, scope){
50099         var bc = {
50100             handler: handler,
50101             scope: scope,
50102             minWidth: this.minButtonWidth,
50103             hideParent:true
50104         };
50105         if(typeof config == "string"){
50106             bc.text = config;
50107         }else{
50108             Roo.apply(bc, config);
50109         }
50110         var btn = new Roo.Button(null, bc);
50111         this.buttons.push(btn);
50112         return btn;
50113     },
50114
50115      /**
50116      * Adds a series of form elements (using the xtype property as the factory method.
50117      * Valid xtypes are:  TextField, TextArea .... Button, Layout, FieldSet, Column, (and 'end' to close a block)
50118      * @param {Object} config 
50119      */
50120     
50121     addxtype : function()
50122     {
50123         var ar = Array.prototype.slice.call(arguments, 0);
50124         var ret = false;
50125         for(var i = 0; i < ar.length; i++) {
50126             if (!ar[i]) {
50127                 continue; // skip -- if this happends something invalid got sent, we 
50128                 // should ignore it, as basically that interface element will not show up
50129                 // and that should be pretty obvious!!
50130             }
50131             
50132             if (Roo.form[ar[i].xtype]) {
50133                 ar[i].form = this;
50134                 var fe = Roo.factory(ar[i], Roo.form);
50135                 if (!ret) {
50136                     ret = fe;
50137                 }
50138                 fe.form = this;
50139                 if (fe.store) {
50140                     fe.store.form = this;
50141                 }
50142                 if (fe.isLayout) {  
50143                          
50144                     this.start(fe);
50145                     this.allItems.push(fe);
50146                     if (fe.items && fe.addxtype) {
50147                         fe.addxtype.apply(fe, fe.items);
50148                         delete fe.items;
50149                     }
50150                      this.end();
50151                     continue;
50152                 }
50153                 
50154                 
50155                  
50156                 this.add(fe);
50157               //  console.log('adding ' + ar[i].xtype);
50158             }
50159             if (ar[i].xtype == 'Button') {  
50160                 //console.log('adding button');
50161                 //console.log(ar[i]);
50162                 this.addButton(ar[i]);
50163                 this.allItems.push(fe);
50164                 continue;
50165             }
50166             
50167             if (ar[i].xtype == 'end') { // so we can add fieldsets... / layout etc.
50168                 alert('end is not supported on xtype any more, use items');
50169             //    this.end();
50170             //    //console.log('adding end');
50171             }
50172             
50173         }
50174         return ret;
50175     },
50176     
50177     /**
50178      * Starts monitoring of the valid state of this form. Usually this is done by passing the config
50179      * option "monitorValid"
50180      */
50181     startMonitoring : function(){
50182         if(!this.bound){
50183             this.bound = true;
50184             Roo.TaskMgr.start({
50185                 run : this.bindHandler,
50186                 interval : this.monitorPoll || 200,
50187                 scope: this
50188             });
50189         }
50190     },
50191
50192     /**
50193      * Stops monitoring of the valid state of this form
50194      */
50195     stopMonitoring : function(){
50196         this.bound = false;
50197     },
50198
50199     // private
50200     bindHandler : function(){
50201         if(!this.bound){
50202             return false; // stops binding
50203         }
50204         var valid = true;
50205         this.items.each(function(f){
50206             if(!f.isValid(true)){
50207                 valid = false;
50208                 return false;
50209             }
50210         });
50211         for(var i = 0, len = this.buttons.length; i < len; i++){
50212             var btn = this.buttons[i];
50213             if(btn.formBind === true && btn.disabled === valid){
50214                 btn.setDisabled(!valid);
50215             }
50216         }
50217         this.fireEvent('clientvalidation', this, valid);
50218     }
50219     
50220     
50221     
50222     
50223     
50224     
50225     
50226     
50227 });
50228
50229
50230 // back compat
50231 Roo.Form = Roo.form.Form;
50232 /*
50233  * Based on:
50234  * Ext JS Library 1.1.1
50235  * Copyright(c) 2006-2007, Ext JS, LLC.
50236  *
50237  * Originally Released Under LGPL - original licence link has changed is not relivant.
50238  *
50239  * Fork - LGPL
50240  * <script type="text/javascript">
50241  */
50242
50243 // as we use this in bootstrap.
50244 Roo.namespace('Roo.form');
50245  /**
50246  * @class Roo.form.Action
50247  * Internal Class used to handle form actions
50248  * @constructor
50249  * @param {Roo.form.BasicForm} el The form element or its id
50250  * @param {Object} config Configuration options
50251  */
50252
50253  
50254  
50255 // define the action interface
50256 Roo.form.Action = function(form, options){
50257     this.form = form;
50258     this.options = options || {};
50259 };
50260 /**
50261  * Client Validation Failed
50262  * @const 
50263  */
50264 Roo.form.Action.CLIENT_INVALID = 'client';
50265 /**
50266  * Server Validation Failed
50267  * @const 
50268  */
50269 Roo.form.Action.SERVER_INVALID = 'server';
50270  /**
50271  * Connect to Server Failed
50272  * @const 
50273  */
50274 Roo.form.Action.CONNECT_FAILURE = 'connect';
50275 /**
50276  * Reading Data from Server Failed
50277  * @const 
50278  */
50279 Roo.form.Action.LOAD_FAILURE = 'load';
50280
50281 Roo.form.Action.prototype = {
50282     type : 'default',
50283     failureType : undefined,
50284     response : undefined,
50285     result : undefined,
50286
50287     // interface method
50288     run : function(options){
50289
50290     },
50291
50292     // interface method
50293     success : function(response){
50294
50295     },
50296
50297     // interface method
50298     handleResponse : function(response){
50299
50300     },
50301
50302     // default connection failure
50303     failure : function(response){
50304         
50305         this.response = response;
50306         this.failureType = Roo.form.Action.CONNECT_FAILURE;
50307         this.form.afterAction(this, false);
50308     },
50309
50310     processResponse : function(response){
50311         this.response = response;
50312         if(!response.responseText){
50313             return true;
50314         }
50315         this.result = this.handleResponse(response);
50316         return this.result;
50317     },
50318
50319     // utility functions used internally
50320     getUrl : function(appendParams){
50321         var url = this.options.url || this.form.url || this.form.el.dom.action;
50322         if(appendParams){
50323             var p = this.getParams();
50324             if(p){
50325                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
50326             }
50327         }
50328         return url;
50329     },
50330
50331     getMethod : function(){
50332         return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
50333     },
50334
50335     getParams : function(){
50336         var bp = this.form.baseParams;
50337         var p = this.options.params;
50338         if(p){
50339             if(typeof p == "object"){
50340                 p = Roo.urlEncode(Roo.applyIf(p, bp));
50341             }else if(typeof p == 'string' && bp){
50342                 p += '&' + Roo.urlEncode(bp);
50343             }
50344         }else if(bp){
50345             p = Roo.urlEncode(bp);
50346         }
50347         return p;
50348     },
50349
50350     createCallback : function(){
50351         return {
50352             success: this.success,
50353             failure: this.failure,
50354             scope: this,
50355             timeout: (this.form.timeout*1000),
50356             upload: this.form.fileUpload ? this.success : undefined
50357         };
50358     }
50359 };
50360
50361 Roo.form.Action.Submit = function(form, options){
50362     Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
50363 };
50364
50365 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
50366     type : 'submit',
50367
50368     haveProgress : false,
50369     uploadComplete : false,
50370     
50371     // uploadProgress indicator.
50372     uploadProgress : function()
50373     {
50374         if (!this.form.progressUrl) {
50375             return;
50376         }
50377         
50378         if (!this.haveProgress) {
50379             Roo.MessageBox.progress("Uploading", "Uploading");
50380         }
50381         if (this.uploadComplete) {
50382            Roo.MessageBox.hide();
50383            return;
50384         }
50385         
50386         this.haveProgress = true;
50387    
50388         var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
50389         
50390         var c = new Roo.data.Connection();
50391         c.request({
50392             url : this.form.progressUrl,
50393             params: {
50394                 id : uid
50395             },
50396             method: 'GET',
50397             success : function(req){
50398                //console.log(data);
50399                 var rdata = false;
50400                 var edata;
50401                 try  {
50402                    rdata = Roo.decode(req.responseText)
50403                 } catch (e) {
50404                     Roo.log("Invalid data from server..");
50405                     Roo.log(edata);
50406                     return;
50407                 }
50408                 if (!rdata || !rdata.success) {
50409                     Roo.log(rdata);
50410                     Roo.MessageBox.alert(Roo.encode(rdata));
50411                     return;
50412                 }
50413                 var data = rdata.data;
50414                 
50415                 if (this.uploadComplete) {
50416                    Roo.MessageBox.hide();
50417                    return;
50418                 }
50419                    
50420                 if (data){
50421                     Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
50422                        Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
50423                     );
50424                 }
50425                 this.uploadProgress.defer(2000,this);
50426             },
50427        
50428             failure: function(data) {
50429                 Roo.log('progress url failed ');
50430                 Roo.log(data);
50431             },
50432             scope : this
50433         });
50434            
50435     },
50436     
50437     
50438     run : function()
50439     {
50440         // run get Values on the form, so it syncs any secondary forms.
50441         this.form.getValues();
50442         
50443         var o = this.options;
50444         var method = this.getMethod();
50445         var isPost = method == 'POST';
50446         if(o.clientValidation === false || this.form.isValid()){
50447             
50448             if (this.form.progressUrl) {
50449                 this.form.findField('UPLOAD_IDENTIFIER').setValue(
50450                     (new Date() * 1) + '' + Math.random());
50451                     
50452             } 
50453             
50454             
50455             Roo.Ajax.request(Roo.apply(this.createCallback(), {
50456                 form:this.form.el.dom,
50457                 url:this.getUrl(!isPost),
50458                 method: method,
50459                 params:isPost ? this.getParams() : null,
50460                 isUpload: this.form.fileUpload,
50461                 formData : this.form.formData
50462             }));
50463             
50464             this.uploadProgress();
50465
50466         }else if (o.clientValidation !== false){ // client validation failed
50467             this.failureType = Roo.form.Action.CLIENT_INVALID;
50468             this.form.afterAction(this, false);
50469         }
50470     },
50471
50472     success : function(response)
50473     {
50474         this.uploadComplete= true;
50475         if (this.haveProgress) {
50476             Roo.MessageBox.hide();
50477         }
50478         
50479         
50480         var result = this.processResponse(response);
50481         if(result === true || result.success){
50482             this.form.afterAction(this, true);
50483             return;
50484         }
50485         if(result.errors){
50486             this.form.markInvalid(result.errors);
50487             this.failureType = Roo.form.Action.SERVER_INVALID;
50488         }
50489         this.form.afterAction(this, false);
50490     },
50491     failure : function(response)
50492     {
50493         this.uploadComplete= true;
50494         if (this.haveProgress) {
50495             Roo.MessageBox.hide();
50496         }
50497         
50498         this.response = response;
50499         this.failureType = Roo.form.Action.CONNECT_FAILURE;
50500         this.form.afterAction(this, false);
50501     },
50502     
50503     handleResponse : function(response){
50504         if(this.form.errorReader){
50505             var rs = this.form.errorReader.read(response);
50506             var errors = [];
50507             if(rs.records){
50508                 for(var i = 0, len = rs.records.length; i < len; i++) {
50509                     var r = rs.records[i];
50510                     errors[i] = r.data;
50511                 }
50512             }
50513             if(errors.length < 1){
50514                 errors = null;
50515             }
50516             return {
50517                 success : rs.success,
50518                 errors : errors
50519             };
50520         }
50521         var ret = false;
50522         try {
50523             ret = Roo.decode(response.responseText);
50524         } catch (e) {
50525             ret = {
50526                 success: false,
50527                 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
50528                 errors : []
50529             };
50530         }
50531         return ret;
50532         
50533     }
50534 });
50535
50536
50537 Roo.form.Action.Load = function(form, options){
50538     Roo.form.Action.Load.superclass.constructor.call(this, form, options);
50539     this.reader = this.form.reader;
50540 };
50541
50542 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
50543     type : 'load',
50544
50545     run : function(){
50546         
50547         Roo.Ajax.request(Roo.apply(
50548                 this.createCallback(), {
50549                     method:this.getMethod(),
50550                     url:this.getUrl(false),
50551                     params:this.getParams()
50552         }));
50553     },
50554
50555     success : function(response){
50556         
50557         var result = this.processResponse(response);
50558         if(result === true || !result.success || !result.data){
50559             this.failureType = Roo.form.Action.LOAD_FAILURE;
50560             this.form.afterAction(this, false);
50561             return;
50562         }
50563         this.form.clearInvalid();
50564         this.form.setValues(result.data);
50565         this.form.afterAction(this, true);
50566     },
50567
50568     handleResponse : function(response){
50569         if(this.form.reader){
50570             var rs = this.form.reader.read(response);
50571             var data = rs.records && rs.records[0] ? rs.records[0].data : null;
50572             return {
50573                 success : rs.success,
50574                 data : data
50575             };
50576         }
50577         return Roo.decode(response.responseText);
50578     }
50579 });
50580
50581 Roo.form.Action.ACTION_TYPES = {
50582     'load' : Roo.form.Action.Load,
50583     'submit' : Roo.form.Action.Submit
50584 };/*
50585  * Based on:
50586  * Ext JS Library 1.1.1
50587  * Copyright(c) 2006-2007, Ext JS, LLC.
50588  *
50589  * Originally Released Under LGPL - original licence link has changed is not relivant.
50590  *
50591  * Fork - LGPL
50592  * <script type="text/javascript">
50593  */
50594  
50595 /**
50596  * @class Roo.form.Layout
50597  * @extends Roo.Component
50598  * @children Roo.form.Column Roo.form.Row Roo.form.Field Roo.Button Roo.form.TextItem
50599  * Creates a container for layout and rendering of fields in an {@link Roo.form.Form}.
50600  * @constructor
50601  * @param {Object} config Configuration options
50602  */
50603 Roo.form.Layout = function(config){
50604     var xitems = [];
50605     if (config.items) {
50606         xitems = config.items;
50607         delete config.items;
50608     }
50609     Roo.form.Layout.superclass.constructor.call(this, config);
50610     this.stack = [];
50611     Roo.each(xitems, this.addxtype, this);
50612      
50613 };
50614
50615 Roo.extend(Roo.form.Layout, Roo.Component, {
50616     /**
50617      * @cfg {String/Object} autoCreate
50618      * A DomHelper element spec used to autocreate the layout (defaults to {tag: 'div', cls: 'x-form-ct'})
50619      */
50620     /**
50621      * @cfg {String/Object/Function} style
50622      * A style specification string, e.g. "width:100px", or object in the form {width:"100px"}, or
50623      * a function which returns such a specification.
50624      */
50625     /**
50626      * @cfg {String} labelAlign
50627      * Valid values are "left," "top" and "right" (defaults to "left")
50628      */
50629     /**
50630      * @cfg {Number} labelWidth
50631      * Fixed width in pixels of all field labels (defaults to undefined)
50632      */
50633     /**
50634      * @cfg {Boolean} clear
50635      * True to add a clearing element at the end of this layout, equivalent to CSS clear: both (defaults to true)
50636      */
50637     clear : true,
50638     /**
50639      * @cfg {String} labelSeparator
50640      * The separator to use after field labels (defaults to ':')
50641      */
50642     labelSeparator : ':',
50643     /**
50644      * @cfg {Boolean} hideLabels
50645      * True to suppress the display of field labels in this layout (defaults to false)
50646      */
50647     hideLabels : false,
50648
50649     // private
50650     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct'},
50651     
50652     isLayout : true,
50653     
50654     // private
50655     onRender : function(ct, position){
50656         if(this.el){ // from markup
50657             this.el = Roo.get(this.el);
50658         }else {  // generate
50659             var cfg = this.getAutoCreate();
50660             this.el = ct.createChild(cfg, position);
50661         }
50662         if(this.style){
50663             this.el.applyStyles(this.style);
50664         }
50665         if(this.labelAlign){
50666             this.el.addClass('x-form-label-'+this.labelAlign);
50667         }
50668         if(this.hideLabels){
50669             this.labelStyle = "display:none";
50670             this.elementStyle = "padding-left:0;";
50671         }else{
50672             if(typeof this.labelWidth == 'number'){
50673                 this.labelStyle = "width:"+this.labelWidth+"px;";
50674                 this.elementStyle = "padding-left:"+((this.labelWidth+(typeof this.labelPad == 'number' ? this.labelPad : 5))+'px')+";";
50675             }
50676             if(this.labelAlign == 'top'){
50677                 this.labelStyle = "width:auto;";
50678                 this.elementStyle = "padding-left:0;";
50679             }
50680         }
50681         var stack = this.stack;
50682         var slen = stack.length;
50683         if(slen > 0){
50684             if(!this.fieldTpl){
50685                 var t = new Roo.Template(
50686                     '<div class="x-form-item {5}">',
50687                         '<label for="{0}" style="{2}">{1}{4}</label>',
50688                         '<div class="x-form-element" id="x-form-el-{0}" style="{3}">',
50689                         '</div>',
50690                     '</div><div class="x-form-clear-left"></div>'
50691                 );
50692                 t.disableFormats = true;
50693                 t.compile();
50694                 Roo.form.Layout.prototype.fieldTpl = t;
50695             }
50696             for(var i = 0; i < slen; i++) {
50697                 if(stack[i].isFormField){
50698                     this.renderField(stack[i]);
50699                 }else{
50700                     this.renderComponent(stack[i]);
50701                 }
50702             }
50703         }
50704         if(this.clear){
50705             this.el.createChild({cls:'x-form-clear'});
50706         }
50707     },
50708
50709     // private
50710     renderField : function(f){
50711         f.fieldEl = Roo.get(this.fieldTpl.append(this.el, [
50712                f.id, //0
50713                f.fieldLabel, //1
50714                f.labelStyle||this.labelStyle||'', //2
50715                this.elementStyle||'', //3
50716                typeof f.labelSeparator == 'undefined' ? this.labelSeparator : f.labelSeparator, //4
50717                f.itemCls||this.itemCls||''  //5
50718        ], true).getPrevSibling());
50719     },
50720
50721     // private
50722     renderComponent : function(c){
50723         c.render(c.isLayout ? this.el : this.el.createChild());    
50724     },
50725     /**
50726      * Adds a object form elements (using the xtype property as the factory method.)
50727      * Valid xtypes are:  TextField, TextArea .... Button, Layout, FieldSet, Column
50728      * @param {Object} config 
50729      */
50730     addxtype : function(o)
50731     {
50732         // create the lement.
50733         o.form = this.form;
50734         var fe = Roo.factory(o, Roo.form);
50735         this.form.allItems.push(fe);
50736         this.stack.push(fe);
50737         
50738         if (fe.isFormField) {
50739             this.form.items.add(fe);
50740         }
50741          
50742         return fe;
50743     }
50744 });
50745
50746 /**
50747  * @class Roo.form.Column
50748  * @extends Roo.form.Layout
50749  * Creates a column container for layout and rendering of fields in an {@link Roo.form.Form}.
50750  * @constructor
50751  * @param {Object} config Configuration options
50752  */
50753 Roo.form.Column = function(config){
50754     Roo.form.Column.superclass.constructor.call(this, config);
50755 };
50756
50757 Roo.extend(Roo.form.Column, Roo.form.Layout, {
50758     /**
50759      * @cfg {Number/String} width
50760      * The fixed width of the column in pixels or CSS value (defaults to "auto")
50761      */
50762     /**
50763      * @cfg {String/Object} autoCreate
50764      * A DomHelper element spec used to autocreate the column (defaults to {tag: 'div', cls: 'x-form-ct x-form-column'})
50765      */
50766
50767     // private
50768     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct x-form-column'},
50769
50770     // private
50771     onRender : function(ct, position){
50772         Roo.form.Column.superclass.onRender.call(this, ct, position);
50773         if(this.width){
50774             this.el.setWidth(this.width);
50775         }
50776     }
50777 });
50778
50779
50780 /**
50781  * @class Roo.form.Row
50782  * @extends Roo.form.Layout
50783  * @children Roo.form.Column Roo.form.Row Roo.form.Field Roo.Button Roo.form.TextItem
50784  * Creates a row container for layout and rendering of fields in an {@link Roo.form.Form}.
50785  * @constructor
50786  * @param {Object} config Configuration options
50787  */
50788
50789  
50790 Roo.form.Row = function(config){
50791     Roo.form.Row.superclass.constructor.call(this, config);
50792 };
50793  
50794 Roo.extend(Roo.form.Row, Roo.form.Layout, {
50795       /**
50796      * @cfg {Number/String} width
50797      * The fixed width of the column in pixels or CSS value (defaults to "auto")
50798      */
50799     /**
50800      * @cfg {Number/String} height
50801      * The fixed height of the column in pixels or CSS value (defaults to "auto")
50802      */
50803     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct x-form-row'},
50804     
50805     padWidth : 20,
50806     // private
50807     onRender : function(ct, position){
50808         //console.log('row render');
50809         if(!this.rowTpl){
50810             var t = new Roo.Template(
50811                 '<div class="x-form-item {5}" style="float:left;width:{6}px">',
50812                     '<label for="{0}" style="{2}">{1}{4}</label>',
50813                     '<div class="x-form-element" id="x-form-el-{0}" style="{3}">',
50814                     '</div>',
50815                 '</div>'
50816             );
50817             t.disableFormats = true;
50818             t.compile();
50819             Roo.form.Layout.prototype.rowTpl = t;
50820         }
50821         this.fieldTpl = this.rowTpl;
50822         
50823         //console.log('lw' + this.labelWidth +', la:' + this.labelAlign);
50824         var labelWidth = 100;
50825         
50826         if ((this.labelAlign != 'top')) {
50827             if (typeof this.labelWidth == 'number') {
50828                 labelWidth = this.labelWidth
50829             }
50830             this.padWidth =  20 + labelWidth;
50831             
50832         }
50833         
50834         Roo.form.Column.superclass.onRender.call(this, ct, position);
50835         if(this.width){
50836             this.el.setWidth(this.width);
50837         }
50838         if(this.height){
50839             this.el.setHeight(this.height);
50840         }
50841     },
50842     
50843     // private
50844     renderField : function(f){
50845         f.fieldEl = this.fieldTpl.append(this.el, [
50846                f.id, f.fieldLabel,
50847                f.labelStyle||this.labelStyle||'',
50848                this.elementStyle||'',
50849                typeof f.labelSeparator == 'undefined' ? this.labelSeparator : f.labelSeparator,
50850                f.itemCls||this.itemCls||'',
50851                f.width ? f.width + this.padWidth : 160 + this.padWidth
50852        ],true);
50853     }
50854 });
50855  
50856
50857 /**
50858  * @class Roo.form.FieldSet
50859  * @extends Roo.form.Layout
50860  * @children Roo.form.Column Roo.form.Row Roo.form.Field Roo.Button Roo.form.TextItem
50861  * Creates a fieldset container for layout and rendering of fields in an {@link Roo.form.Form}.
50862  * @constructor
50863  * @param {Object} config Configuration options
50864  */
50865 Roo.form.FieldSet = function(config){
50866     Roo.form.FieldSet.superclass.constructor.call(this, config);
50867 };
50868
50869 Roo.extend(Roo.form.FieldSet, Roo.form.Layout, {
50870     /**
50871      * @cfg {String} legend
50872      * The text to display as the legend for the FieldSet (defaults to '')
50873      */
50874     /**
50875      * @cfg {String/Object} autoCreate
50876      * A DomHelper element spec used to autocreate the fieldset (defaults to {tag: 'fieldset', cn: {tag:'legend'}})
50877      */
50878
50879     // private
50880     defaultAutoCreate : {tag: 'fieldset', cn: {tag:'legend'}},
50881
50882     // private
50883     onRender : function(ct, position){
50884         Roo.form.FieldSet.superclass.onRender.call(this, ct, position);
50885         if(this.legend){
50886             this.setLegend(this.legend);
50887         }
50888     },
50889
50890     // private
50891     setLegend : function(text){
50892         if(this.rendered){
50893             this.el.child('legend').update(text);
50894         }
50895     }
50896 });/*
50897  * Based on:
50898  * Ext JS Library 1.1.1
50899  * Copyright(c) 2006-2007, Ext JS, LLC.
50900  *
50901  * Originally Released Under LGPL - original licence link has changed is not relivant.
50902  *
50903  * Fork - LGPL
50904  * <script type="text/javascript">
50905  */
50906 /**
50907  * @class Roo.form.VTypes
50908  * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
50909  * @static
50910  */
50911 Roo.form.VTypes = function(){
50912     // closure these in so they are only created once.
50913     var alpha = /^[a-zA-Z_]+$/;
50914     var alphanum = /^[a-zA-Z0-9_]+$/;
50915     var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,24}$/;
50916     var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
50917
50918     // All these messages and functions are configurable
50919     return {
50920         /**
50921          * The function used to validate email addresses
50922          * @param {String} value The email address
50923          */
50924         'email' : function(v){
50925             return email.test(v);
50926         },
50927         /**
50928          * The error text to display when the email validation function returns false
50929          * @type String
50930          */
50931         'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
50932         /**
50933          * The keystroke filter mask to be applied on email input
50934          * @type RegExp
50935          */
50936         'emailMask' : /[a-z0-9_\.\-@]/i,
50937
50938         /**
50939          * The function used to validate URLs
50940          * @param {String} value The URL
50941          */
50942         'url' : function(v){
50943             return url.test(v);
50944         },
50945         /**
50946          * The error text to display when the url validation function returns false
50947          * @type String
50948          */
50949         'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
50950         
50951         /**
50952          * The function used to validate alpha values
50953          * @param {String} value The value
50954          */
50955         'alpha' : function(v){
50956             return alpha.test(v);
50957         },
50958         /**
50959          * The error text to display when the alpha validation function returns false
50960          * @type String
50961          */
50962         'alphaText' : 'This field should only contain letters and _',
50963         /**
50964          * The keystroke filter mask to be applied on alpha input
50965          * @type RegExp
50966          */
50967         'alphaMask' : /[a-z_]/i,
50968
50969         /**
50970          * The function used to validate alphanumeric values
50971          * @param {String} value The value
50972          */
50973         'alphanum' : function(v){
50974             return alphanum.test(v);
50975         },
50976         /**
50977          * The error text to display when the alphanumeric validation function returns false
50978          * @type String
50979          */
50980         'alphanumText' : 'This field should only contain letters, numbers and _',
50981         /**
50982          * The keystroke filter mask to be applied on alphanumeric input
50983          * @type RegExp
50984          */
50985         'alphanumMask' : /[a-z0-9_]/i
50986     };
50987 }();//<script type="text/javascript">
50988
50989 /**
50990  * @class Roo.form.FCKeditor
50991  * @extends Roo.form.TextArea
50992  * Wrapper around the FCKEditor http://www.fckeditor.net
50993  * @constructor
50994  * Creates a new FCKeditor
50995  * @param {Object} config Configuration options
50996  */
50997 Roo.form.FCKeditor = function(config){
50998     Roo.form.FCKeditor.superclass.constructor.call(this, config);
50999     this.addEvents({
51000          /**
51001          * @event editorinit
51002          * Fired when the editor is initialized - you can add extra handlers here..
51003          * @param {FCKeditor} this
51004          * @param {Object} the FCK object.
51005          */
51006         editorinit : true
51007     });
51008     
51009     
51010 };
51011 Roo.form.FCKeditor.editors = { };
51012 Roo.extend(Roo.form.FCKeditor, Roo.form.TextArea,
51013 {
51014     //defaultAutoCreate : {
51015     //    tag : "textarea",style   : "width:100px;height:60px;" ,autocomplete    : "off"
51016     //},
51017     // private
51018     /**
51019      * @cfg {Object} fck options - see fck manual for details.
51020      */
51021     fckconfig : false,
51022     
51023     /**
51024      * @cfg {Object} fck toolbar set (Basic or Default)
51025      */
51026     toolbarSet : 'Basic',
51027     /**
51028      * @cfg {Object} fck BasePath
51029      */ 
51030     basePath : '/fckeditor/',
51031     
51032     
51033     frame : false,
51034     
51035     value : '',
51036     
51037    
51038     onRender : function(ct, position)
51039     {
51040         if(!this.el){
51041             this.defaultAutoCreate = {
51042                 tag: "textarea",
51043                 style:"width:300px;height:60px;",
51044                 autocomplete: "new-password"
51045             };
51046         }
51047         Roo.form.FCKeditor.superclass.onRender.call(this, ct, position);
51048         /*
51049         if(this.grow){
51050             this.textSizeEl = Roo.DomHelper.append(document.body, {tag: "pre", cls: "x-form-grow-sizer"});
51051             if(this.preventScrollbars){
51052                 this.el.setStyle("overflow", "hidden");
51053             }
51054             this.el.setHeight(this.growMin);
51055         }
51056         */
51057         //console.log('onrender' + this.getId() );
51058         Roo.form.FCKeditor.editors[this.getId()] = this;
51059          
51060
51061         this.replaceTextarea() ;
51062         
51063     },
51064     
51065     getEditor : function() {
51066         return this.fckEditor;
51067     },
51068     /**
51069      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
51070      * @param {Mixed} value The value to set
51071      */
51072     
51073     
51074     setValue : function(value)
51075     {
51076         //console.log('setValue: ' + value);
51077         
51078         if(typeof(value) == 'undefined') { // not sure why this is happending...
51079             return;
51080         }
51081         Roo.form.FCKeditor.superclass.setValue.apply(this,[value]);
51082         
51083         //if(!this.el || !this.getEditor()) {
51084         //    this.value = value;
51085             //this.setValue.defer(100,this,[value]);    
51086         //    return;
51087         //} 
51088         
51089         if(!this.getEditor()) {
51090             return;
51091         }
51092         
51093         this.getEditor().SetData(value);
51094         
51095         //
51096
51097     },
51098
51099     /**
51100      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
51101      * @return {Mixed} value The field value
51102      */
51103     getValue : function()
51104     {
51105         
51106         if (this.frame && this.frame.dom.style.display == 'none') {
51107             return Roo.form.FCKeditor.superclass.getValue.call(this);
51108         }
51109         
51110         if(!this.el || !this.getEditor()) {
51111            
51112            // this.getValue.defer(100,this); 
51113             return this.value;
51114         }
51115        
51116         
51117         var value=this.getEditor().GetData();
51118         Roo.form.FCKeditor.superclass.setValue.apply(this,[value]);
51119         return Roo.form.FCKeditor.superclass.getValue.call(this);
51120         
51121
51122     },
51123
51124     /**
51125      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
51126      * @return {Mixed} value The field value
51127      */
51128     getRawValue : function()
51129     {
51130         if (this.frame && this.frame.dom.style.display == 'none') {
51131             return Roo.form.FCKeditor.superclass.getRawValue.call(this);
51132         }
51133         
51134         if(!this.el || !this.getEditor()) {
51135             //this.getRawValue.defer(100,this); 
51136             return this.value;
51137             return;
51138         }
51139         
51140         
51141         
51142         var value=this.getEditor().GetData();
51143         Roo.form.FCKeditor.superclass.setRawValue.apply(this,[value]);
51144         return Roo.form.FCKeditor.superclass.getRawValue.call(this);
51145          
51146     },
51147     
51148     setSize : function(w,h) {
51149         
51150         
51151         
51152         //if (this.frame && this.frame.dom.style.display == 'none') {
51153         //    Roo.form.FCKeditor.superclass.setSize.apply(this, [w, h]);
51154         //    return;
51155         //}
51156         //if(!this.el || !this.getEditor()) {
51157         //    this.setSize.defer(100,this, [w,h]); 
51158         //    return;
51159         //}
51160         
51161         
51162         
51163         Roo.form.FCKeditor.superclass.setSize.apply(this, [w, h]);
51164         
51165         this.frame.dom.setAttribute('width', w);
51166         this.frame.dom.setAttribute('height', h);
51167         this.frame.setSize(w,h);
51168         
51169     },
51170     
51171     toggleSourceEdit : function(value) {
51172         
51173       
51174          
51175         this.el.dom.style.display = value ? '' : 'none';
51176         this.frame.dom.style.display = value ?  'none' : '';
51177         
51178     },
51179     
51180     
51181     focus: function(tag)
51182     {
51183         if (this.frame.dom.style.display == 'none') {
51184             return Roo.form.FCKeditor.superclass.focus.call(this);
51185         }
51186         if(!this.el || !this.getEditor()) {
51187             this.focus.defer(100,this, [tag]); 
51188             return;
51189         }
51190         
51191         
51192         
51193         
51194         var tgs = this.getEditor().EditorDocument.getElementsByTagName(tag);
51195         this.getEditor().Focus();
51196         if (tgs.length) {
51197             if (!this.getEditor().Selection.GetSelection()) {
51198                 this.focus.defer(100,this, [tag]); 
51199                 return;
51200             }
51201             
51202             
51203             var r = this.getEditor().EditorDocument.createRange();
51204             r.setStart(tgs[0],0);
51205             r.setEnd(tgs[0],0);
51206             this.getEditor().Selection.GetSelection().removeAllRanges();
51207             this.getEditor().Selection.GetSelection().addRange(r);
51208             this.getEditor().Focus();
51209         }
51210         
51211     },
51212     
51213     
51214     
51215     replaceTextarea : function()
51216     {
51217         if ( document.getElementById( this.getId() + '___Frame' ) ) {
51218             return ;
51219         }
51220         //if ( !this.checkBrowser || this._isCompatibleBrowser() )
51221         //{
51222             // We must check the elements firstly using the Id and then the name.
51223         var oTextarea = document.getElementById( this.getId() );
51224         
51225         var colElementsByName = document.getElementsByName( this.getId() ) ;
51226          
51227         oTextarea.style.display = 'none' ;
51228
51229         if ( oTextarea.tabIndex ) {            
51230             this.TabIndex = oTextarea.tabIndex ;
51231         }
51232         
51233         this._insertHtmlBefore( this._getConfigHtml(), oTextarea ) ;
51234         this._insertHtmlBefore( this._getIFrameHtml(), oTextarea ) ;
51235         this.frame = Roo.get(this.getId() + '___Frame')
51236     },
51237     
51238     _getConfigHtml : function()
51239     {
51240         var sConfig = '' ;
51241
51242         for ( var o in this.fckconfig ) {
51243             sConfig += sConfig.length > 0  ? '&amp;' : '';
51244             sConfig += encodeURIComponent( o ) + '=' + encodeURIComponent( this.fckconfig[o] ) ;
51245         }
51246
51247         return '<input type="hidden" id="' + this.getId() + '___Config" value="' + sConfig + '" style="display:none" />' ;
51248     },
51249     
51250     
51251     _getIFrameHtml : function()
51252     {
51253         var sFile = 'fckeditor.html' ;
51254         /* no idea what this is about..
51255         try
51256         {
51257             if ( (/fcksource=true/i).test( window.top.location.search ) )
51258                 sFile = 'fckeditor.original.html' ;
51259         }
51260         catch (e) { 
51261         */
51262
51263         var sLink = this.basePath + 'editor/' + sFile + '?InstanceName=' + encodeURIComponent( this.getId() ) ;
51264         sLink += this.toolbarSet ? ( '&amp;Toolbar=' + this.toolbarSet)  : '';
51265         
51266         
51267         var html = '<iframe id="' + this.getId() +
51268             '___Frame" src="' + sLink +
51269             '" width="' + this.width +
51270             '" height="' + this.height + '"' +
51271             (this.tabIndex ?  ' tabindex="' + this.tabIndex + '"' :'' ) +
51272             ' frameborder="0" scrolling="no"></iframe>' ;
51273
51274         return html ;
51275     },
51276     
51277     _insertHtmlBefore : function( html, element )
51278     {
51279         if ( element.insertAdjacentHTML )       {
51280             // IE
51281             element.insertAdjacentHTML( 'beforeBegin', html ) ;
51282         } else { // Gecko
51283             var oRange = document.createRange() ;
51284             oRange.setStartBefore( element ) ;
51285             var oFragment = oRange.createContextualFragment( html );
51286             element.parentNode.insertBefore( oFragment, element ) ;
51287         }
51288     }
51289     
51290     
51291   
51292     
51293     
51294     
51295     
51296
51297 });
51298
51299 //Roo.reg('fckeditor', Roo.form.FCKeditor);
51300
51301 function FCKeditor_OnComplete(editorInstance){
51302     var f = Roo.form.FCKeditor.editors[editorInstance.Name];
51303     f.fckEditor = editorInstance;
51304     //console.log("loaded");
51305     f.fireEvent('editorinit', f, editorInstance);
51306
51307   
51308
51309  
51310
51311
51312
51313
51314
51315
51316
51317
51318
51319
51320
51321
51322
51323
51324
51325 //<script type="text/javascript">
51326 /**
51327  * @class Roo.form.GridField
51328  * @extends Roo.form.Field
51329  * Embed a grid (or editable grid into a form)
51330  * STATUS ALPHA
51331  * 
51332  * This embeds a grid in a form, the value of the field should be the json encoded array of rows
51333  * it needs 
51334  * xgrid.store = Roo.data.Store
51335  * xgrid.store.proxy = Roo.data.MemoryProxy (data = [] )
51336  * xgrid.store.reader = Roo.data.JsonReader 
51337  * 
51338  * 
51339  * @constructor
51340  * Creates a new GridField
51341  * @param {Object} config Configuration options
51342  */
51343 Roo.form.GridField = function(config){
51344     Roo.form.GridField.superclass.constructor.call(this, config);
51345      
51346 };
51347
51348 Roo.extend(Roo.form.GridField, Roo.form.Field,  {
51349     /**
51350      * @cfg {Number} width  - used to restrict width of grid..
51351      */
51352     width : 100,
51353     /**
51354      * @cfg {Number} height - used to restrict height of grid..
51355      */
51356     height : 50,
51357      /**
51358      * @cfg {Object} xgrid (xtype'd description of grid) { xtype : 'Grid', dataSource: .... }
51359          * 
51360          *}
51361      */
51362     xgrid : false, 
51363     /**
51364      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
51365      * {tag: "input", type: "checkbox", autocomplete: "off"})
51366      */
51367    // defaultAutoCreate : { tag: 'div' },
51368     defaultAutoCreate : { tag: 'input', type: 'hidden', autocomplete: 'new-password'},
51369     /**
51370      * @cfg {String} addTitle Text to include for adding a title.
51371      */
51372     addTitle : false,
51373     //
51374     onResize : function(){
51375         Roo.form.Field.superclass.onResize.apply(this, arguments);
51376     },
51377
51378     initEvents : function(){
51379         // Roo.form.Checkbox.superclass.initEvents.call(this);
51380         // has no events...
51381        
51382     },
51383
51384
51385     getResizeEl : function(){
51386         return this.wrap;
51387     },
51388
51389     getPositionEl : function(){
51390         return this.wrap;
51391     },
51392
51393     // private
51394     onRender : function(ct, position){
51395         
51396         this.style = this.style || 'overflow: hidden; border:1px solid #c3daf9;';
51397         var style = this.style;
51398         delete this.style;
51399         
51400         Roo.form.GridField.superclass.onRender.call(this, ct, position);
51401         this.wrap = this.el.wrap({cls: ''}); // not sure why ive done thsi...
51402         this.viewEl = this.wrap.createChild({ tag: 'div' });
51403         if (style) {
51404             this.viewEl.applyStyles(style);
51405         }
51406         if (this.width) {
51407             this.viewEl.setWidth(this.width);
51408         }
51409         if (this.height) {
51410             this.viewEl.setHeight(this.height);
51411         }
51412         //if(this.inputValue !== undefined){
51413         //this.setValue(this.value);
51414         
51415         
51416         this.grid = new Roo.grid[this.xgrid.xtype](this.viewEl, this.xgrid);
51417         
51418         
51419         this.grid.render();
51420         this.grid.getDataSource().on('remove', this.refreshValue, this);
51421         this.grid.getDataSource().on('update', this.refreshValue, this);
51422         this.grid.on('afteredit', this.refreshValue, this);
51423  
51424     },
51425      
51426     
51427     /**
51428      * Sets the value of the item. 
51429      * @param {String} either an object  or a string..
51430      */
51431     setValue : function(v){
51432         //this.value = v;
51433         v = v || []; // empty set..
51434         // this does not seem smart - it really only affects memoryproxy grids..
51435         if (this.grid && this.grid.getDataSource() && typeof(v) != 'undefined') {
51436             var ds = this.grid.getDataSource();
51437             // assumes a json reader..
51438             var data = {}
51439             data[ds.reader.meta.root ] =  typeof(v) == 'string' ? Roo.decode(v) : v;
51440             ds.loadData( data);
51441         }
51442         // clear selection so it does not get stale.
51443         if (this.grid.sm) { 
51444             this.grid.sm.clearSelections();
51445         }
51446         
51447         Roo.form.GridField.superclass.setValue.call(this, v);
51448         this.refreshValue();
51449         // should load data in the grid really....
51450     },
51451     
51452     // private
51453     refreshValue: function() {
51454          var val = [];
51455         this.grid.getDataSource().each(function(r) {
51456             val.push(r.data);
51457         });
51458         this.el.dom.value = Roo.encode(val);
51459     }
51460     
51461      
51462     
51463     
51464 });/*
51465  * Based on:
51466  * Ext JS Library 1.1.1
51467  * Copyright(c) 2006-2007, Ext JS, LLC.
51468  *
51469  * Originally Released Under LGPL - original licence link has changed is not relivant.
51470  *
51471  * Fork - LGPL
51472  * <script type="text/javascript">
51473  */
51474 /**
51475  * @class Roo.form.DisplayField
51476  * @extends Roo.form.Field
51477  * A generic Field to display non-editable data.
51478  * @cfg {Boolean} closable (true|false) default false
51479  * @constructor
51480  * Creates a new Display Field item.
51481  * @param {Object} config Configuration options
51482  */
51483 Roo.form.DisplayField = function(config){
51484     Roo.form.DisplayField.superclass.constructor.call(this, config);
51485     
51486     this.addEvents({
51487         /**
51488          * @event close
51489          * Fires after the click the close btn
51490              * @param {Roo.form.DisplayField} this
51491              */
51492         close : true
51493     });
51494 };
51495
51496 Roo.extend(Roo.form.DisplayField, Roo.form.TextField,  {
51497     inputType:      'hidden',
51498     allowBlank:     true,
51499     readOnly:         true,
51500     
51501  
51502     /**
51503      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
51504      */
51505     focusClass : undefined,
51506     /**
51507      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
51508      */
51509     fieldClass: 'x-form-field',
51510     
51511      /**
51512      * @cfg {Function} valueRenderer The renderer for the field (so you can reformat output). should return raw HTML
51513      */
51514     valueRenderer: undefined,
51515     
51516     width: 100,
51517     /**
51518      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
51519      * {tag: "input", type: "checkbox", autocomplete: "off"})
51520      */
51521      
51522  //   defaultAutoCreate : { tag: 'input', type: 'hidden', autocomplete: 'off'},
51523  
51524     closable : false,
51525     
51526     onResize : function(){
51527         Roo.form.DisplayField.superclass.onResize.apply(this, arguments);
51528         
51529     },
51530
51531     initEvents : function(){
51532         // Roo.form.Checkbox.superclass.initEvents.call(this);
51533         // has no events...
51534         
51535         if(this.closable){
51536             this.closeEl.on('click', this.onClose, this);
51537         }
51538        
51539     },
51540
51541
51542     getResizeEl : function(){
51543         return this.wrap;
51544     },
51545
51546     getPositionEl : function(){
51547         return this.wrap;
51548     },
51549
51550     // private
51551     onRender : function(ct, position){
51552         
51553         Roo.form.DisplayField.superclass.onRender.call(this, ct, position);
51554         //if(this.inputValue !== undefined){
51555         this.wrap = this.el.wrap();
51556         
51557         this.viewEl = this.wrap.createChild({ tag: 'div', cls: 'x-form-displayfield'});
51558         
51559         if(this.closable){
51560             this.closeEl = this.wrap.createChild({ tag: 'div', cls: 'x-dlg-close'});
51561         }
51562         
51563         if (this.bodyStyle) {
51564             this.viewEl.applyStyles(this.bodyStyle);
51565         }
51566         //this.viewEl.setStyle('padding', '2px');
51567         
51568         this.setValue(this.value);
51569         
51570     },
51571 /*
51572     // private
51573     initValue : Roo.emptyFn,
51574
51575   */
51576
51577         // private
51578     onClick : function(){
51579         
51580     },
51581
51582     /**
51583      * Sets the checked state of the checkbox.
51584      * @param {Boolean/String} checked True, 'true', '1', or 'on' to check the checkbox, any other value will uncheck it.
51585      */
51586     setValue : function(v){
51587         this.value = v;
51588         var html = this.valueRenderer ?  this.valueRenderer(v) : String.format('{0}', v);
51589         // this might be called before we have a dom element..
51590         if (!this.viewEl) {
51591             return;
51592         }
51593         this.viewEl.dom.innerHTML = html;
51594         Roo.form.DisplayField.superclass.setValue.call(this, v);
51595
51596     },
51597     
51598     onClose : function(e)
51599     {
51600         e.preventDefault();
51601         
51602         this.fireEvent('close', this);
51603     }
51604 });/*
51605  * 
51606  * Licence- LGPL
51607  * 
51608  */
51609
51610 /**
51611  * @class Roo.form.DayPicker
51612  * @extends Roo.form.Field
51613  * A Day picker show [M] [T] [W] ....
51614  * @constructor
51615  * Creates a new Day Picker
51616  * @param {Object} config Configuration options
51617  */
51618 Roo.form.DayPicker= function(config){
51619     Roo.form.DayPicker.superclass.constructor.call(this, config);
51620      
51621 };
51622
51623 Roo.extend(Roo.form.DayPicker, Roo.form.Field,  {
51624     /**
51625      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
51626      */
51627     focusClass : undefined,
51628     /**
51629      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
51630      */
51631     fieldClass: "x-form-field",
51632    
51633     /**
51634      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
51635      * {tag: "input", type: "checkbox", autocomplete: "off"})
51636      */
51637     defaultAutoCreate : { tag: "input", type: 'hidden', autocomplete: "new-password"},
51638     
51639    
51640     actionMode : 'viewEl', 
51641     //
51642     // private
51643  
51644     inputType : 'hidden',
51645     
51646      
51647     inputElement: false, // real input element?
51648     basedOn: false, // ????
51649     
51650     isFormField: true, // not sure where this is needed!!!!
51651
51652     onResize : function(){
51653         Roo.form.Checkbox.superclass.onResize.apply(this, arguments);
51654         if(!this.boxLabel){
51655             this.el.alignTo(this.wrap, 'c-c');
51656         }
51657     },
51658
51659     initEvents : function(){
51660         Roo.form.Checkbox.superclass.initEvents.call(this);
51661         this.el.on("click", this.onClick,  this);
51662         this.el.on("change", this.onClick,  this);
51663     },
51664
51665
51666     getResizeEl : function(){
51667         return this.wrap;
51668     },
51669
51670     getPositionEl : function(){
51671         return this.wrap;
51672     },
51673
51674     
51675     // private
51676     onRender : function(ct, position){
51677         Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
51678        
51679         this.wrap = this.el.wrap({cls: 'x-form-daypick-item '});
51680         
51681         var r1 = '<table><tr>';
51682         var r2 = '<tr class="x-form-daypick-icons">';
51683         for (var i=0; i < 7; i++) {
51684             r1+= '<td><div>' + Date.dayNames[i].substring(0,3) + '</div></td>';
51685             r2+= '<td><img class="x-menu-item-icon" src="' + Roo.BLANK_IMAGE_URL  +'"></td>';
51686         }
51687         
51688         var viewEl = this.wrap.createChild( r1 + '</tr>' + r2 + '</tr></table>');
51689         viewEl.select('img').on('click', this.onClick, this);
51690         this.viewEl = viewEl;   
51691         
51692         
51693         // this will not work on Chrome!!!
51694         this.el.on('DOMAttrModified', this.setFromHidden,  this); //ff
51695         this.el.on('propertychange', this.setFromHidden,  this);  //ie
51696         
51697         
51698           
51699
51700     },
51701
51702     // private
51703     initValue : Roo.emptyFn,
51704
51705     /**
51706      * Returns the checked state of the checkbox.
51707      * @return {Boolean} True if checked, else false
51708      */
51709     getValue : function(){
51710         return this.el.dom.value;
51711         
51712     },
51713
51714         // private
51715     onClick : function(e){ 
51716         //this.setChecked(!this.checked);
51717         Roo.get(e.target).toggleClass('x-menu-item-checked');
51718         this.refreshValue();
51719         //if(this.el.dom.checked != this.checked){
51720         //    this.setValue(this.el.dom.checked);
51721        // }
51722     },
51723     
51724     // private
51725     refreshValue : function()
51726     {
51727         var val = '';
51728         this.viewEl.select('img',true).each(function(e,i,n)  {
51729             val += e.is(".x-menu-item-checked") ? String(n) : '';
51730         });
51731         this.setValue(val, true);
51732     },
51733
51734     /**
51735      * Sets the checked state of the checkbox.
51736      * On is always based on a string comparison between inputValue and the param.
51737      * @param {Boolean/String} value - the value to set 
51738      * @param {Boolean/String} suppressEvent - whether to suppress the checkchange event.
51739      */
51740     setValue : function(v,suppressEvent){
51741         if (!this.el.dom) {
51742             return;
51743         }
51744         var old = this.el.dom.value ;
51745         this.el.dom.value = v;
51746         if (suppressEvent) {
51747             return ;
51748         }
51749          
51750         // update display..
51751         this.viewEl.select('img',true).each(function(e,i,n)  {
51752             
51753             var on = e.is(".x-menu-item-checked");
51754             var newv = v.indexOf(String(n)) > -1;
51755             if (on != newv) {
51756                 e.toggleClass('x-menu-item-checked');
51757             }
51758             
51759         });
51760         
51761         
51762         this.fireEvent('change', this, v, old);
51763         
51764         
51765     },
51766    
51767     // handle setting of hidden value by some other method!!?!?
51768     setFromHidden: function()
51769     {
51770         if(!this.el){
51771             return;
51772         }
51773         //console.log("SET FROM HIDDEN");
51774         //alert('setFrom hidden');
51775         this.setValue(this.el.dom.value);
51776     },
51777     
51778     onDestroy : function()
51779     {
51780         if(this.viewEl){
51781             Roo.get(this.viewEl).remove();
51782         }
51783          
51784         Roo.form.DayPicker.superclass.onDestroy.call(this);
51785     }
51786
51787 });/*
51788  * RooJS Library 1.1.1
51789  * Copyright(c) 2008-2011  Alan Knowles
51790  *
51791  * License - LGPL
51792  */
51793  
51794
51795 /**
51796  * @class Roo.form.ComboCheck
51797  * @extends Roo.form.ComboBox
51798  * A combobox for multiple select items.
51799  *
51800  * FIXME - could do with a reset button..
51801  * 
51802  * @constructor
51803  * Create a new ComboCheck
51804  * @param {Object} config Configuration options
51805  */
51806 Roo.form.ComboCheck = function(config){
51807     Roo.form.ComboCheck.superclass.constructor.call(this, config);
51808     // should verify some data...
51809     // like
51810     // hiddenName = required..
51811     // displayField = required
51812     // valudField == required
51813     var req= [ 'hiddenName', 'displayField', 'valueField' ];
51814     var _t = this;
51815     Roo.each(req, function(e) {
51816         if ((typeof(_t[e]) == 'undefined' ) || !_t[e].length) {
51817             throw "Roo.form.ComboCheck : missing value for: " + e;
51818         }
51819     });
51820     
51821     
51822 };
51823
51824 Roo.extend(Roo.form.ComboCheck, Roo.form.ComboBox, {
51825      
51826      
51827     editable : false,
51828      
51829     selectedClass: 'x-menu-item-checked', 
51830     
51831     // private
51832     onRender : function(ct, position){
51833         var _t = this;
51834         
51835         
51836         
51837         if(!this.tpl){
51838             var cls = 'x-combo-list';
51839
51840             
51841             this.tpl =  new Roo.Template({
51842                 html :  '<div class="'+cls+'-item x-menu-check-item">' +
51843                    '<img class="x-menu-item-icon" style="margin: 0px;" src="' + Roo.BLANK_IMAGE_URL + '">' + 
51844                    '<span>{' + this.displayField + '}</span>' +
51845                     '</div>' 
51846                 
51847             });
51848         }
51849  
51850         
51851         Roo.form.ComboCheck.superclass.onRender.call(this, ct, position);
51852         this.view.singleSelect = false;
51853         this.view.multiSelect = true;
51854         this.view.toggleSelect = true;
51855         this.pageTb.add(new Roo.Toolbar.Fill(), {
51856             
51857             text: 'Done',
51858             handler: function()
51859             {
51860                 _t.collapse();
51861             }
51862         });
51863     },
51864     
51865     onViewOver : function(e, t){
51866         // do nothing...
51867         return;
51868         
51869     },
51870     
51871     onViewClick : function(doFocus,index){
51872         return;
51873         
51874     },
51875     select: function () {
51876         //Roo.log("SELECT CALLED");
51877     },
51878      
51879     selectByValue : function(xv, scrollIntoView){
51880         var ar = this.getValueArray();
51881         var sels = [];
51882         
51883         Roo.each(ar, function(v) {
51884             if(v === undefined || v === null){
51885                 return;
51886             }
51887             var r = this.findRecord(this.valueField, v);
51888             if(r){
51889                 sels.push(this.store.indexOf(r))
51890                 
51891             }
51892         },this);
51893         this.view.select(sels);
51894         return false;
51895     },
51896     
51897     
51898     
51899     onSelect : function(record, index){
51900        // Roo.log("onselect Called");
51901        // this is only called by the clear button now..
51902         this.view.clearSelections();
51903         this.setValue('[]');
51904         if (this.value != this.valueBefore) {
51905             this.fireEvent('change', this, this.value, this.valueBefore);
51906             this.valueBefore = this.value;
51907         }
51908     },
51909     getValueArray : function()
51910     {
51911         var ar = [] ;
51912         
51913         try {
51914             //Roo.log(this.value);
51915             if (typeof(this.value) == 'undefined') {
51916                 return [];
51917             }
51918             var ar = Roo.decode(this.value);
51919             return  ar instanceof Array ? ar : []; //?? valid?
51920             
51921         } catch(e) {
51922             Roo.log(e + "\nRoo.form.ComboCheck:getValueArray  invalid data:" + this.getValue());
51923             return [];
51924         }
51925          
51926     },
51927     expand : function ()
51928     {
51929         
51930         Roo.form.ComboCheck.superclass.expand.call(this);
51931         this.valueBefore = typeof(this.value) == 'undefined' ? '' : this.value;
51932         //this.valueBefore = typeof(this.valueBefore) == 'undefined' ? '' : this.valueBefore;
51933         
51934
51935     },
51936     
51937     collapse : function(){
51938         Roo.form.ComboCheck.superclass.collapse.call(this);
51939         var sl = this.view.getSelectedIndexes();
51940         var st = this.store;
51941         var nv = [];
51942         var tv = [];
51943         var r;
51944         Roo.each(sl, function(i) {
51945             r = st.getAt(i);
51946             nv.push(r.get(this.valueField));
51947         },this);
51948         this.setValue(Roo.encode(nv));
51949         if (this.value != this.valueBefore) {
51950
51951             this.fireEvent('change', this, this.value, this.valueBefore);
51952             this.valueBefore = this.value;
51953         }
51954         
51955     },
51956     
51957     setValue : function(v){
51958         // Roo.log(v);
51959         this.value = v;
51960         
51961         var vals = this.getValueArray();
51962         var tv = [];
51963         Roo.each(vals, function(k) {
51964             var r = this.findRecord(this.valueField, k);
51965             if(r){
51966                 tv.push(r.data[this.displayField]);
51967             }else if(this.valueNotFoundText !== undefined){
51968                 tv.push( this.valueNotFoundText );
51969             }
51970         },this);
51971        // Roo.log(tv);
51972         
51973         Roo.form.ComboBox.superclass.setValue.call(this, tv.join(', '));
51974         this.hiddenField.value = v;
51975         this.value = v;
51976     }
51977     
51978 });/*
51979  * Based on:
51980  * Ext JS Library 1.1.1
51981  * Copyright(c) 2006-2007, Ext JS, LLC.
51982  *
51983  * Originally Released Under LGPL - original licence link has changed is not relivant.
51984  *
51985  * Fork - LGPL
51986  * <script type="text/javascript">
51987  */
51988  
51989 /**
51990  * @class Roo.form.Signature
51991  * @extends Roo.form.Field
51992  * Signature field.  
51993  * @constructor
51994  * 
51995  * @param {Object} config Configuration options
51996  */
51997
51998 Roo.form.Signature = function(config){
51999     Roo.form.Signature.superclass.constructor.call(this, config);
52000     
52001     this.addEvents({// not in used??
52002          /**
52003          * @event confirm
52004          * Fires when the 'confirm' icon is pressed (add a listener to enable add button)
52005              * @param {Roo.form.Signature} combo This combo box
52006              */
52007         'confirm' : true,
52008         /**
52009          * @event reset
52010          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
52011              * @param {Roo.form.ComboBox} combo This combo box
52012              * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
52013              */
52014         'reset' : true
52015     });
52016 };
52017
52018 Roo.extend(Roo.form.Signature, Roo.form.Field,  {
52019     /**
52020      * @cfg {Object} labels Label to use when rendering a form.
52021      * defaults to 
52022      * labels : { 
52023      *      clear : "Clear",
52024      *      confirm : "Confirm"
52025      *  }
52026      */
52027     labels : { 
52028         clear : "Clear",
52029         confirm : "Confirm"
52030     },
52031     /**
52032      * @cfg {Number} width The signature panel width (defaults to 300)
52033      */
52034     width: 300,
52035     /**
52036      * @cfg {Number} height The signature panel height (defaults to 100)
52037      */
52038     height : 100,
52039     /**
52040      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to false)
52041      */
52042     allowBlank : false,
52043     
52044     //private
52045     // {Object} signPanel The signature SVG panel element (defaults to {})
52046     signPanel : {},
52047     // {Boolean} isMouseDown False to validate that the mouse down event (defaults to false)
52048     isMouseDown : false,
52049     // {Boolean} isConfirmed validate the signature is confirmed or not for submitting form (defaults to false)
52050     isConfirmed : false,
52051     // {String} signatureTmp SVG mapping string (defaults to empty string)
52052     signatureTmp : '',
52053     
52054     
52055     defaultAutoCreate : { // modified by initCompnoent..
52056         tag: "input",
52057         type:"hidden"
52058     },
52059
52060     // private
52061     onRender : function(ct, position){
52062         
52063         Roo.form.Signature.superclass.onRender.call(this, ct, position);
52064         
52065         this.wrap = this.el.wrap({
52066             cls:'x-form-signature-wrap', style : 'width: ' + this.width + 'px', cn:{cls:'x-form-signature'}
52067         });
52068         
52069         this.createToolbar(this);
52070         this.signPanel = this.wrap.createChild({
52071                 tag: 'div',
52072                 style: 'width: ' + this.width + 'px; height: ' + this.height + 'px; border: 0;'
52073             }, this.el
52074         );
52075             
52076         this.svgID = Roo.id();
52077         this.svgEl = this.signPanel.createChild({
52078               xmlns : 'http://www.w3.org/2000/svg',
52079               tag : 'svg',
52080               id : this.svgID + "-svg",
52081               width: this.width,
52082               height: this.height,
52083               viewBox: '0 0 '+this.width+' '+this.height,
52084               cn : [
52085                 {
52086                     tag: "rect",
52087                     id: this.svgID + "-svg-r",
52088                     width: this.width,
52089                     height: this.height,
52090                     fill: "#ffa"
52091                 },
52092                 {
52093                     tag: "line",
52094                     id: this.svgID + "-svg-l",
52095                     x1: "0", // start
52096                     y1: (this.height*0.8), // start set the line in 80% of height
52097                     x2: this.width, // end
52098                     y2: (this.height*0.8), // end set the line in 80% of height
52099                     'stroke': "#666",
52100                     'stroke-width': "1",
52101                     'stroke-dasharray': "3",
52102                     'shape-rendering': "crispEdges",
52103                     'pointer-events': "none"
52104                 },
52105                 {
52106                     tag: "path",
52107                     id: this.svgID + "-svg-p",
52108                     'stroke': "navy",
52109                     'stroke-width': "3",
52110                     'fill': "none",
52111                     'pointer-events': 'none'
52112                 }
52113               ]
52114         });
52115         this.createSVG();
52116         this.svgBox = this.svgEl.dom.getScreenCTM();
52117     },
52118     createSVG : function(){ 
52119         var svg = this.signPanel;
52120         var r = svg.select('#'+ this.svgID + '-svg-r', true).first().dom;
52121         var t = this;
52122
52123         r.addEventListener('mousedown', function(e) { return t.down(e); }, false);
52124         r.addEventListener('mousemove', function(e) { return t.move(e); }, false);
52125         r.addEventListener('mouseup', function(e) { return t.up(e); }, false);
52126         r.addEventListener('mouseout', function(e) { return t.up(e); }, false);
52127         r.addEventListener('touchstart', function(e) { return t.down(e); }, false);
52128         r.addEventListener('touchmove', function(e) { return t.move(e); }, false);
52129         r.addEventListener('touchend', function(e) { return t.up(e); }, false);
52130         
52131     },
52132     isTouchEvent : function(e){
52133         return e.type.match(/^touch/);
52134     },
52135     getCoords : function (e) {
52136         var pt    = this.svgEl.dom.createSVGPoint();
52137         pt.x = e.clientX; 
52138         pt.y = e.clientY;
52139         if (this.isTouchEvent(e)) {
52140             pt.x =  e.targetTouches[0].clientX;
52141             pt.y = e.targetTouches[0].clientY;
52142         }
52143         var a = this.svgEl.dom.getScreenCTM();
52144         var b = a.inverse();
52145         var mx = pt.matrixTransform(b);
52146         return mx.x + ',' + mx.y;
52147     },
52148     //mouse event headler 
52149     down : function (e) {
52150         this.signatureTmp += 'M' + this.getCoords(e) + ' ';
52151         this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr('d', this.signatureTmp);
52152         
52153         this.isMouseDown = true;
52154         
52155         e.preventDefault();
52156     },
52157     move : function (e) {
52158         if (this.isMouseDown) {
52159             this.signatureTmp += 'L' + this.getCoords(e) + ' ';
52160             this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr( 'd', this.signatureTmp);
52161         }
52162         
52163         e.preventDefault();
52164     },
52165     up : function (e) {
52166         this.isMouseDown = false;
52167         var sp = this.signatureTmp.split(' ');
52168         
52169         if(sp.length > 1){
52170             if(!sp[sp.length-2].match(/^L/)){
52171                 sp.pop();
52172                 sp.pop();
52173                 sp.push("");
52174                 this.signatureTmp = sp.join(" ");
52175             }
52176         }
52177         if(this.getValue() != this.signatureTmp){
52178             this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#ffa');
52179             this.isConfirmed = false;
52180         }
52181         e.preventDefault();
52182     },
52183     
52184     /**
52185      * Protected method that will not generally be called directly. It
52186      * is called when the editor creates its toolbar. Override this method if you need to
52187      * add custom toolbar buttons.
52188      * @param {HtmlEditor} editor
52189      */
52190     createToolbar : function(editor){
52191          function btn(id, toggle, handler){
52192             var xid = fid + '-'+ id ;
52193             return {
52194                 id : xid,
52195                 cmd : id,
52196                 cls : 'x-btn-icon x-edit-'+id,
52197                 enableToggle:toggle !== false,
52198                 scope: editor, // was editor...
52199                 handler:handler||editor.relayBtnCmd,
52200                 clickEvent:'mousedown',
52201                 tooltip: etb.buttonTips[id] || undefined, ///tips ???
52202                 tabIndex:-1
52203             };
52204         }
52205         
52206         
52207         var tb = new Roo.Toolbar(editor.wrap.dom.firstChild);
52208         this.tb = tb;
52209         this.tb.add(
52210            {
52211                 cls : ' x-signature-btn x-signature-'+id,
52212                 scope: editor, // was editor...
52213                 handler: this.reset,
52214                 clickEvent:'mousedown',
52215                 text: this.labels.clear
52216             },
52217             {
52218                  xtype : 'Fill',
52219                  xns: Roo.Toolbar
52220             }, 
52221             {
52222                 cls : '  x-signature-btn x-signature-'+id,
52223                 scope: editor, // was editor...
52224                 handler: this.confirmHandler,
52225                 clickEvent:'mousedown',
52226                 text: this.labels.confirm
52227             }
52228         );
52229     
52230     },
52231     //public
52232     /**
52233      * when user is clicked confirm then show this image.....
52234      * 
52235      * @return {String} Image Data URI
52236      */
52237     getImageDataURI : function(){
52238         var svg = this.svgEl.dom.parentNode.innerHTML;
52239         var src = 'data:image/svg+xml;base64,'+window.btoa(svg);
52240         return src; 
52241     },
52242     /**
52243      * 
52244      * @return {Boolean} this.isConfirmed
52245      */
52246     getConfirmed : function(){
52247         return this.isConfirmed;
52248     },
52249     /**
52250      * 
52251      * @return {Number} this.width
52252      */
52253     getWidth : function(){
52254         return this.width;
52255     },
52256     /**
52257      * 
52258      * @return {Number} this.height
52259      */
52260     getHeight : function(){
52261         return this.height;
52262     },
52263     // private
52264     getSignature : function(){
52265         return this.signatureTmp;
52266     },
52267     // private
52268     reset : function(){
52269         this.signatureTmp = '';
52270         this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#ffa');
52271         this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr( 'd', '');
52272         this.isConfirmed = false;
52273         Roo.form.Signature.superclass.reset.call(this);
52274     },
52275     setSignature : function(s){
52276         this.signatureTmp = s;
52277         this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#ffa');
52278         this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr( 'd', s);
52279         this.setValue(s);
52280         this.isConfirmed = false;
52281         Roo.form.Signature.superclass.reset.call(this);
52282     }, 
52283     test : function(){
52284 //        Roo.log(this.signPanel.dom.contentWindow.up())
52285     },
52286     //private
52287     setConfirmed : function(){
52288         
52289         
52290         
52291 //        Roo.log(Roo.get(this.signPanel.dom.contentWindow.r).attr('fill', '#cfc'));
52292     },
52293     // private
52294     confirmHandler : function(){
52295         if(!this.getSignature()){
52296             return;
52297         }
52298         
52299         this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#cfc');
52300         this.setValue(this.getSignature());
52301         this.isConfirmed = true;
52302         
52303         this.fireEvent('confirm', this);
52304     },
52305     // private
52306     // Subclasses should provide the validation implementation by overriding this
52307     validateValue : function(value){
52308         if(this.allowBlank){
52309             return true;
52310         }
52311         
52312         if(this.isConfirmed){
52313             return true;
52314         }
52315         return false;
52316     }
52317 });/*
52318  * Based on:
52319  * Ext JS Library 1.1.1
52320  * Copyright(c) 2006-2007, Ext JS, LLC.
52321  *
52322  * Originally Released Under LGPL - original licence link has changed is not relivant.
52323  *
52324  * Fork - LGPL
52325  * <script type="text/javascript">
52326  */
52327  
52328
52329 /**
52330  * @class Roo.form.ComboBox
52331  * @extends Roo.form.TriggerField
52332  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
52333  * @constructor
52334  * Create a new ComboBox.
52335  * @param {Object} config Configuration options
52336  */
52337 Roo.form.Select = function(config){
52338     Roo.form.Select.superclass.constructor.call(this, config);
52339      
52340 };
52341
52342 Roo.extend(Roo.form.Select , Roo.form.ComboBox, {
52343     /**
52344      * @cfg {String/HTMLElement/Element} transform The id, DOM node or element of an existing select to convert to a ComboBox
52345      */
52346     /**
52347      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
52348      * rendering into an Roo.Editor, defaults to false)
52349      */
52350     /**
52351      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
52352      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
52353      */
52354     /**
52355      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
52356      */
52357     /**
52358      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
52359      * the dropdown list (defaults to undefined, with no header element)
52360      */
52361
52362      /**
52363      * @cfg {String/Roo.Template} tpl The template to use to render the output
52364      */
52365      
52366     // private
52367     defaultAutoCreate : {tag: "select"  },
52368     /**
52369      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
52370      */
52371     listWidth: undefined,
52372     /**
52373      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
52374      * mode = 'remote' or 'text' if mode = 'local')
52375      */
52376     displayField: undefined,
52377     /**
52378      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
52379      * mode = 'remote' or 'value' if mode = 'local'). 
52380      * Note: use of a valueField requires the user make a selection
52381      * in order for a value to be mapped.
52382      */
52383     valueField: undefined,
52384     
52385     
52386     /**
52387      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
52388      * field's data value (defaults to the underlying DOM element's name)
52389      */
52390     hiddenName: undefined,
52391     /**
52392      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
52393      */
52394     listClass: '',
52395     /**
52396      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
52397      */
52398     selectedClass: 'x-combo-selected',
52399     /**
52400      * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
52401      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-arrow-trigger'
52402      * which displays a downward arrow icon).
52403      */
52404     triggerClass : 'x-form-arrow-trigger',
52405     /**
52406      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
52407      */
52408     shadow:'sides',
52409     /**
52410      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
52411      * anchor positions (defaults to 'tl-bl')
52412      */
52413     listAlign: 'tl-bl?',
52414     /**
52415      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
52416      */
52417     maxHeight: 300,
52418     /**
52419      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
52420      * query specified by the allQuery config option (defaults to 'query')
52421      */
52422     triggerAction: 'query',
52423     /**
52424      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
52425      * (defaults to 4, does not apply if editable = false)
52426      */
52427     minChars : 4,
52428     /**
52429      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
52430      * delay (typeAheadDelay) if it matches a known value (defaults to false)
52431      */
52432     typeAhead: false,
52433     /**
52434      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
52435      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
52436      */
52437     queryDelay: 500,
52438     /**
52439      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
52440      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
52441      */
52442     pageSize: 0,
52443     /**
52444      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
52445      * when editable = true (defaults to false)
52446      */
52447     selectOnFocus:false,
52448     /**
52449      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
52450      */
52451     queryParam: 'query',
52452     /**
52453      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
52454      * when mode = 'remote' (defaults to 'Loading...')
52455      */
52456     loadingText: 'Loading...',
52457     /**
52458      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
52459      */
52460     resizable: false,
52461     /**
52462      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
52463      */
52464     handleHeight : 8,
52465     /**
52466      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
52467      * traditional select (defaults to true)
52468      */
52469     editable: true,
52470     /**
52471      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
52472      */
52473     allQuery: '',
52474     /**
52475      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
52476      */
52477     mode: 'remote',
52478     /**
52479      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
52480      * listWidth has a higher value)
52481      */
52482     minListWidth : 70,
52483     /**
52484      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
52485      * allow the user to set arbitrary text into the field (defaults to false)
52486      */
52487     forceSelection:false,
52488     /**
52489      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
52490      * if typeAhead = true (defaults to 250)
52491      */
52492     typeAheadDelay : 250,
52493     /**
52494      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
52495      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
52496      */
52497     valueNotFoundText : undefined,
52498     
52499     /**
52500      * @cfg {String} defaultValue The value displayed after loading the store.
52501      */
52502     defaultValue: '',
52503     
52504     /**
52505      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
52506      */
52507     blockFocus : false,
52508     
52509     /**
52510      * @cfg {Boolean} disableClear Disable showing of clear button.
52511      */
52512     disableClear : false,
52513     /**
52514      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
52515      */
52516     alwaysQuery : false,
52517     
52518     //private
52519     addicon : false,
52520     editicon: false,
52521     
52522     // element that contains real text value.. (when hidden is used..)
52523      
52524     // private
52525     onRender : function(ct, position){
52526         Roo.form.Field.prototype.onRender.call(this, ct, position);
52527         
52528         if(this.store){
52529             this.store.on('beforeload', this.onBeforeLoad, this);
52530             this.store.on('load', this.onLoad, this);
52531             this.store.on('loadexception', this.onLoadException, this);
52532             this.store.load({});
52533         }
52534         
52535         
52536         
52537     },
52538
52539     // private
52540     initEvents : function(){
52541         //Roo.form.ComboBox.superclass.initEvents.call(this);
52542  
52543     },
52544
52545     onDestroy : function(){
52546        
52547         if(this.store){
52548             this.store.un('beforeload', this.onBeforeLoad, this);
52549             this.store.un('load', this.onLoad, this);
52550             this.store.un('loadexception', this.onLoadException, this);
52551         }
52552         //Roo.form.ComboBox.superclass.onDestroy.call(this);
52553     },
52554
52555     // private
52556     fireKey : function(e){
52557         if(e.isNavKeyPress() && !this.list.isVisible()){
52558             this.fireEvent("specialkey", this, e);
52559         }
52560     },
52561
52562     // private
52563     onResize: function(w, h){
52564         
52565         return; 
52566     
52567         
52568     },
52569
52570     /**
52571      * Allow or prevent the user from directly editing the field text.  If false is passed,
52572      * the user will only be able to select from the items defined in the dropdown list.  This method
52573      * is the runtime equivalent of setting the 'editable' config option at config time.
52574      * @param {Boolean} value True to allow the user to directly edit the field text
52575      */
52576     setEditable : function(value){
52577          
52578     },
52579
52580     // private
52581     onBeforeLoad : function(){
52582         
52583         Roo.log("Select before load");
52584         return;
52585     
52586         this.innerList.update(this.loadingText ?
52587                '<div class="loading-indicator">'+this.loadingText+'</div>' : '');
52588         //this.restrictHeight();
52589         this.selectedIndex = -1;
52590     },
52591
52592     // private
52593     onLoad : function(){
52594
52595     
52596         var dom = this.el.dom;
52597         dom.innerHTML = '';
52598          var od = dom.ownerDocument;
52599          
52600         if (this.emptyText) {
52601             var op = od.createElement('option');
52602             op.setAttribute('value', '');
52603             op.innerHTML = String.format('{0}', this.emptyText);
52604             dom.appendChild(op);
52605         }
52606         if(this.store.getCount() > 0){
52607            
52608             var vf = this.valueField;
52609             var df = this.displayField;
52610             this.store.data.each(function(r) {
52611                 // which colmsn to use... testing - cdoe / title..
52612                 var op = od.createElement('option');
52613                 op.setAttribute('value', r.data[vf]);
52614                 op.innerHTML = String.format('{0}', r.data[df]);
52615                 dom.appendChild(op);
52616             });
52617             if (typeof(this.defaultValue != 'undefined')) {
52618                 this.setValue(this.defaultValue);
52619             }
52620             
52621              
52622         }else{
52623             //this.onEmptyResults();
52624         }
52625         //this.el.focus();
52626     },
52627     // private
52628     onLoadException : function()
52629     {
52630         dom.innerHTML = '';
52631             
52632         Roo.log("Select on load exception");
52633         return;
52634     
52635         this.collapse();
52636         Roo.log(this.store.reader.jsonData);
52637         if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
52638             Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
52639         }
52640         
52641         
52642     },
52643     // private
52644     onTypeAhead : function(){
52645          
52646     },
52647
52648     // private
52649     onSelect : function(record, index){
52650         Roo.log('on select?');
52651         return;
52652         if(this.fireEvent('beforeselect', this, record, index) !== false){
52653             this.setFromData(index > -1 ? record.data : false);
52654             this.collapse();
52655             this.fireEvent('select', this, record, index);
52656         }
52657     },
52658
52659     /**
52660      * Returns the currently selected field value or empty string if no value is set.
52661      * @return {String} value The selected value
52662      */
52663     getValue : function(){
52664         var dom = this.el.dom;
52665         this.value = dom.options[dom.selectedIndex].value;
52666         return this.value;
52667         
52668     },
52669
52670     /**
52671      * Clears any text/value currently set in the field
52672      */
52673     clearValue : function(){
52674         this.value = '';
52675         this.el.dom.selectedIndex = this.emptyText ? 0 : -1;
52676         
52677     },
52678
52679     /**
52680      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
52681      * will be displayed in the field.  If the value does not match the data value of an existing item,
52682      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
52683      * Otherwise the field will be blank (although the value will still be set).
52684      * @param {String} value The value to match
52685      */
52686     setValue : function(v){
52687         var d = this.el.dom;
52688         for (var i =0; i < d.options.length;i++) {
52689             if (v == d.options[i].value) {
52690                 d.selectedIndex = i;
52691                 this.value = v;
52692                 return;
52693             }
52694         }
52695         this.clearValue();
52696     },
52697     /**
52698      * @property {Object} the last set data for the element
52699      */
52700     
52701     lastData : false,
52702     /**
52703      * Sets the value of the field based on a object which is related to the record format for the store.
52704      * @param {Object} value the value to set as. or false on reset?
52705      */
52706     setFromData : function(o){
52707         Roo.log('setfrom data?');
52708          
52709         
52710         
52711     },
52712     // private
52713     reset : function(){
52714         this.clearValue();
52715     },
52716     // private
52717     findRecord : function(prop, value){
52718         
52719         return false;
52720     
52721         var record;
52722         if(this.store.getCount() > 0){
52723             this.store.each(function(r){
52724                 if(r.data[prop] == value){
52725                     record = r;
52726                     return false;
52727                 }
52728                 return true;
52729             });
52730         }
52731         return record;
52732     },
52733     
52734     getName: function()
52735     {
52736         // returns hidden if it's set..
52737         if (!this.rendered) {return ''};
52738         return !this.hiddenName && this.el.dom.name  ? this.el.dom.name : (this.hiddenName || '');
52739         
52740     },
52741      
52742
52743     
52744
52745     // private
52746     onEmptyResults : function(){
52747         Roo.log('empty results');
52748         //this.collapse();
52749     },
52750
52751     /**
52752      * Returns true if the dropdown list is expanded, else false.
52753      */
52754     isExpanded : function(){
52755         return false;
52756     },
52757
52758     /**
52759      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
52760      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
52761      * @param {String} value The data value of the item to select
52762      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
52763      * selected item if it is not currently in view (defaults to true)
52764      * @return {Boolean} True if the value matched an item in the list, else false
52765      */
52766     selectByValue : function(v, scrollIntoView){
52767         Roo.log('select By Value');
52768         return false;
52769     
52770         if(v !== undefined && v !== null){
52771             var r = this.findRecord(this.valueField || this.displayField, v);
52772             if(r){
52773                 this.select(this.store.indexOf(r), scrollIntoView);
52774                 return true;
52775             }
52776         }
52777         return false;
52778     },
52779
52780     /**
52781      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
52782      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
52783      * @param {Number} index The zero-based index of the list item to select
52784      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
52785      * selected item if it is not currently in view (defaults to true)
52786      */
52787     select : function(index, scrollIntoView){
52788         Roo.log('select ');
52789         return  ;
52790         
52791         this.selectedIndex = index;
52792         this.view.select(index);
52793         if(scrollIntoView !== false){
52794             var el = this.view.getNode(index);
52795             if(el){
52796                 this.innerList.scrollChildIntoView(el, false);
52797             }
52798         }
52799     },
52800
52801       
52802
52803     // private
52804     validateBlur : function(){
52805         
52806         return;
52807         
52808     },
52809
52810     // private
52811     initQuery : function(){
52812         this.doQuery(this.getRawValue());
52813     },
52814
52815     // private
52816     doForce : function(){
52817         if(this.el.dom.value.length > 0){
52818             this.el.dom.value =
52819                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
52820              
52821         }
52822     },
52823
52824     /**
52825      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
52826      * query allowing the query action to be canceled if needed.
52827      * @param {String} query The SQL query to execute
52828      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
52829      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
52830      * saved in the current store (defaults to false)
52831      */
52832     doQuery : function(q, forceAll){
52833         
52834         Roo.log('doQuery?');
52835         if(q === undefined || q === null){
52836             q = '';
52837         }
52838         var qe = {
52839             query: q,
52840             forceAll: forceAll,
52841             combo: this,
52842             cancel:false
52843         };
52844         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
52845             return false;
52846         }
52847         q = qe.query;
52848         forceAll = qe.forceAll;
52849         if(forceAll === true || (q.length >= this.minChars)){
52850             if(this.lastQuery != q || this.alwaysQuery){
52851                 this.lastQuery = q;
52852                 if(this.mode == 'local'){
52853                     this.selectedIndex = -1;
52854                     if(forceAll){
52855                         this.store.clearFilter();
52856                     }else{
52857                         this.store.filter(this.displayField, q);
52858                     }
52859                     this.onLoad();
52860                 }else{
52861                     this.store.baseParams[this.queryParam] = q;
52862                     this.store.load({
52863                         params: this.getParams(q)
52864                     });
52865                     this.expand();
52866                 }
52867             }else{
52868                 this.selectedIndex = -1;
52869                 this.onLoad();   
52870             }
52871         }
52872     },
52873
52874     // private
52875     getParams : function(q){
52876         var p = {};
52877         //p[this.queryParam] = q;
52878         if(this.pageSize){
52879             p.start = 0;
52880             p.limit = this.pageSize;
52881         }
52882         return p;
52883     },
52884
52885     /**
52886      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
52887      */
52888     collapse : function(){
52889         
52890     },
52891
52892     // private
52893     collapseIf : function(e){
52894         
52895     },
52896
52897     /**
52898      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
52899      */
52900     expand : function(){
52901         
52902     } ,
52903
52904     // private
52905      
52906
52907     /** 
52908     * @cfg {Boolean} grow 
52909     * @hide 
52910     */
52911     /** 
52912     * @cfg {Number} growMin 
52913     * @hide 
52914     */
52915     /** 
52916     * @cfg {Number} growMax 
52917     * @hide 
52918     */
52919     /**
52920      * @hide
52921      * @method autoSize
52922      */
52923     
52924     setWidth : function()
52925     {
52926         
52927     },
52928     getResizeEl : function(){
52929         return this.el;
52930     }
52931 });//<script type="text/javasscript">
52932  
52933
52934 /**
52935  * @class Roo.DDView
52936  * A DnD enabled version of Roo.View.
52937  * @param {Element/String} container The Element in which to create the View.
52938  * @param {String} tpl The template string used to create the markup for each element of the View
52939  * @param {Object} config The configuration properties. These include all the config options of
52940  * {@link Roo.View} plus some specific to this class.<br>
52941  * <p>
52942  * Drag/drop is implemented by adding {@link Roo.data.Record}s to the target DDView. If copying is
52943  * not being performed, the original {@link Roo.data.Record} is removed from the source DDView.<br>
52944  * <p>
52945  * The following extra CSS rules are needed to provide insertion point highlighting:<pre><code>
52946 .x-view-drag-insert-above {
52947         border-top:1px dotted #3366cc;
52948 }
52949 .x-view-drag-insert-below {
52950         border-bottom:1px dotted #3366cc;
52951 }
52952 </code></pre>
52953  * 
52954  */
52955  
52956 Roo.DDView = function(container, tpl, config) {
52957     Roo.DDView.superclass.constructor.apply(this, arguments);
52958     this.getEl().setStyle("outline", "0px none");
52959     this.getEl().unselectable();
52960     if (this.dragGroup) {
52961         this.setDraggable(this.dragGroup.split(","));
52962     }
52963     if (this.dropGroup) {
52964         this.setDroppable(this.dropGroup.split(","));
52965     }
52966     if (this.deletable) {
52967         this.setDeletable();
52968     }
52969     this.isDirtyFlag = false;
52970         this.addEvents({
52971                 "drop" : true
52972         });
52973 };
52974
52975 Roo.extend(Roo.DDView, Roo.View, {
52976 /**     @cfg {String/Array} dragGroup The ddgroup name(s) for the View's DragZone. */
52977 /**     @cfg {String/Array} dropGroup The ddgroup name(s) for the View's DropZone. */
52978 /**     @cfg {Boolean} copy Causes drag operations to copy nodes rather than move. */
52979 /**     @cfg {Boolean} allowCopy Causes ctrl/drag operations to copy nodes rather than move. */
52980
52981         isFormField: true,
52982
52983         reset: Roo.emptyFn,
52984         
52985         clearInvalid: Roo.form.Field.prototype.clearInvalid,
52986
52987         validate: function() {
52988                 return true;
52989         },
52990         
52991         destroy: function() {
52992                 this.purgeListeners();
52993                 this.getEl.removeAllListeners();
52994                 this.getEl().remove();
52995                 if (this.dragZone) {
52996                         if (this.dragZone.destroy) {
52997                                 this.dragZone.destroy();
52998                         }
52999                 }
53000                 if (this.dropZone) {
53001                         if (this.dropZone.destroy) {
53002                                 this.dropZone.destroy();
53003                         }
53004                 }
53005         },
53006
53007 /**     Allows this class to be an Roo.form.Field so it can be found using {@link Roo.form.BasicForm#findField}. */
53008         getName: function() {
53009                 return this.name;
53010         },
53011
53012 /**     Loads the View from a JSON string representing the Records to put into the Store. */
53013         setValue: function(v) {
53014                 if (!this.store) {
53015                         throw "DDView.setValue(). DDView must be constructed with a valid Store";
53016                 }
53017                 var data = {};
53018                 data[this.store.reader.meta.root] = v ? [].concat(v) : [];
53019                 this.store.proxy = new Roo.data.MemoryProxy(data);
53020                 this.store.load();
53021         },
53022
53023 /**     @return {String} a parenthesised list of the ids of the Records in the View. */
53024         getValue: function() {
53025                 var result = '(';
53026                 this.store.each(function(rec) {
53027                         result += rec.id + ',';
53028                 });
53029                 return result.substr(0, result.length - 1) + ')';
53030         },
53031         
53032         getIds: function() {
53033                 var i = 0, result = new Array(this.store.getCount());
53034                 this.store.each(function(rec) {
53035                         result[i++] = rec.id;
53036                 });
53037                 return result;
53038         },
53039         
53040         isDirty: function() {
53041                 return this.isDirtyFlag;
53042         },
53043
53044 /**
53045  *      Part of the Roo.dd.DropZone interface. If no target node is found, the
53046  *      whole Element becomes the target, and this causes the drop gesture to append.
53047  */
53048     getTargetFromEvent : function(e) {
53049                 var target = e.getTarget();
53050                 while ((target !== null) && (target.parentNode != this.el.dom)) {
53051                 target = target.parentNode;
53052                 }
53053                 if (!target) {
53054                         target = this.el.dom.lastChild || this.el.dom;
53055                 }
53056                 return target;
53057     },
53058
53059 /**
53060  *      Create the drag data which consists of an object which has the property "ddel" as
53061  *      the drag proxy element. 
53062  */
53063     getDragData : function(e) {
53064         var target = this.findItemFromChild(e.getTarget());
53065                 if(target) {
53066                         this.handleSelection(e);
53067                         var selNodes = this.getSelectedNodes();
53068             var dragData = {
53069                 source: this,
53070                 copy: this.copy || (this.allowCopy && e.ctrlKey),
53071                 nodes: selNodes,
53072                 records: []
53073                         };
53074                         var selectedIndices = this.getSelectedIndexes();
53075                         for (var i = 0; i < selectedIndices.length; i++) {
53076                                 dragData.records.push(this.store.getAt(selectedIndices[i]));
53077                         }
53078                         if (selNodes.length == 1) {
53079                                 dragData.ddel = target.cloneNode(true); // the div element
53080                         } else {
53081                                 var div = document.createElement('div'); // create the multi element drag "ghost"
53082                                 div.className = 'multi-proxy';
53083                                 for (var i = 0, len = selNodes.length; i < len; i++) {
53084                                         div.appendChild(selNodes[i].cloneNode(true));
53085                                 }
53086                                 dragData.ddel = div;
53087                         }
53088             //console.log(dragData)
53089             //console.log(dragData.ddel.innerHTML)
53090                         return dragData;
53091                 }
53092         //console.log('nodragData')
53093                 return false;
53094     },
53095     
53096 /**     Specify to which ddGroup items in this DDView may be dragged. */
53097     setDraggable: function(ddGroup) {
53098         if (ddGroup instanceof Array) {
53099                 Roo.each(ddGroup, this.setDraggable, this);
53100                 return;
53101         }
53102         if (this.dragZone) {
53103                 this.dragZone.addToGroup(ddGroup);
53104         } else {
53105                         this.dragZone = new Roo.dd.DragZone(this.getEl(), {
53106                                 containerScroll: true,
53107                                 ddGroup: ddGroup 
53108
53109                         });
53110 //                      Draggability implies selection. DragZone's mousedown selects the element.
53111                         if (!this.multiSelect) { this.singleSelect = true; }
53112
53113 //                      Wire the DragZone's handlers up to methods in *this*
53114                         this.dragZone.getDragData = this.getDragData.createDelegate(this);
53115                 }
53116     },
53117
53118 /**     Specify from which ddGroup this DDView accepts drops. */
53119     setDroppable: function(ddGroup) {
53120         if (ddGroup instanceof Array) {
53121                 Roo.each(ddGroup, this.setDroppable, this);
53122                 return;
53123         }
53124         if (this.dropZone) {
53125                 this.dropZone.addToGroup(ddGroup);
53126         } else {
53127                         this.dropZone = new Roo.dd.DropZone(this.getEl(), {
53128                                 containerScroll: true,
53129                                 ddGroup: ddGroup
53130                         });
53131
53132 //                      Wire the DropZone's handlers up to methods in *this*
53133                         this.dropZone.getTargetFromEvent = this.getTargetFromEvent.createDelegate(this);
53134                         this.dropZone.onNodeEnter = this.onNodeEnter.createDelegate(this);
53135                         this.dropZone.onNodeOver = this.onNodeOver.createDelegate(this);
53136                         this.dropZone.onNodeOut = this.onNodeOut.createDelegate(this);
53137                         this.dropZone.onNodeDrop = this.onNodeDrop.createDelegate(this);
53138                 }
53139     },
53140
53141 /**     Decide whether to drop above or below a View node. */
53142     getDropPoint : function(e, n, dd){
53143         if (n == this.el.dom) { return "above"; }
53144                 var t = Roo.lib.Dom.getY(n), b = t + n.offsetHeight;
53145                 var c = t + (b - t) / 2;
53146                 var y = Roo.lib.Event.getPageY(e);
53147                 if(y <= c) {
53148                         return "above";
53149                 }else{
53150                         return "below";
53151                 }
53152     },
53153
53154     onNodeEnter : function(n, dd, e, data){
53155                 return false;
53156     },
53157     
53158     onNodeOver : function(n, dd, e, data){
53159                 var pt = this.getDropPoint(e, n, dd);
53160                 // set the insert point style on the target node
53161                 var dragElClass = this.dropNotAllowed;
53162                 if (pt) {
53163                         var targetElClass;
53164                         if (pt == "above"){
53165                                 dragElClass = n.previousSibling ? "x-tree-drop-ok-between" : "x-tree-drop-ok-above";
53166                                 targetElClass = "x-view-drag-insert-above";
53167                         } else {
53168                                 dragElClass = n.nextSibling ? "x-tree-drop-ok-between" : "x-tree-drop-ok-below";
53169                                 targetElClass = "x-view-drag-insert-below";
53170                         }
53171                         if (this.lastInsertClass != targetElClass){
53172                                 Roo.fly(n).replaceClass(this.lastInsertClass, targetElClass);
53173                                 this.lastInsertClass = targetElClass;
53174                         }
53175                 }
53176                 return dragElClass;
53177         },
53178
53179     onNodeOut : function(n, dd, e, data){
53180                 this.removeDropIndicators(n);
53181     },
53182
53183     onNodeDrop : function(n, dd, e, data){
53184         if (this.fireEvent("drop", this, n, dd, e, data) === false) {
53185                 return false;
53186         }
53187         var pt = this.getDropPoint(e, n, dd);
53188                 var insertAt = (n == this.el.dom) ? this.nodes.length : n.nodeIndex;
53189                 if (pt == "below") { insertAt++; }
53190                 for (var i = 0; i < data.records.length; i++) {
53191                         var r = data.records[i];
53192                         var dup = this.store.getById(r.id);
53193                         if (dup && (dd != this.dragZone)) {
53194                                 Roo.fly(this.getNode(this.store.indexOf(dup))).frame("red", 1);
53195                         } else {
53196                                 if (data.copy) {
53197                                         this.store.insert(insertAt++, r.copy());
53198                                 } else {
53199                                         data.source.isDirtyFlag = true;
53200                                         r.store.remove(r);
53201                                         this.store.insert(insertAt++, r);
53202                                 }
53203                                 this.isDirtyFlag = true;
53204                         }
53205                 }
53206                 this.dragZone.cachedTarget = null;
53207                 return true;
53208     },
53209
53210     removeDropIndicators : function(n){
53211                 if(n){
53212                         Roo.fly(n).removeClass([
53213                                 "x-view-drag-insert-above",
53214                                 "x-view-drag-insert-below"]);
53215                         this.lastInsertClass = "_noclass";
53216                 }
53217     },
53218
53219 /**
53220  *      Utility method. Add a delete option to the DDView's context menu.
53221  *      @param {String} imageUrl The URL of the "delete" icon image.
53222  */
53223         setDeletable: function(imageUrl) {
53224                 if (!this.singleSelect && !this.multiSelect) {
53225                         this.singleSelect = true;
53226                 }
53227                 var c = this.getContextMenu();
53228                 this.contextMenu.on("itemclick", function(item) {
53229                         switch (item.id) {
53230                                 case "delete":
53231                                         this.remove(this.getSelectedIndexes());
53232                                         break;
53233                         }
53234                 }, this);
53235                 this.contextMenu.add({
53236                         icon: imageUrl,
53237                         id: "delete",
53238                         text: 'Delete'
53239                 });
53240         },
53241         
53242 /**     Return the context menu for this DDView. */
53243         getContextMenu: function() {
53244                 if (!this.contextMenu) {
53245 //                      Create the View's context menu
53246                         this.contextMenu = new Roo.menu.Menu({
53247                                 id: this.id + "-contextmenu"
53248                         });
53249                         this.el.on("contextmenu", this.showContextMenu, this);
53250                 }
53251                 return this.contextMenu;
53252         },
53253         
53254         disableContextMenu: function() {
53255                 if (this.contextMenu) {
53256                         this.el.un("contextmenu", this.showContextMenu, this);
53257                 }
53258         },
53259
53260         showContextMenu: function(e, item) {
53261         item = this.findItemFromChild(e.getTarget());
53262                 if (item) {
53263                         e.stopEvent();
53264                         this.select(this.getNode(item), this.multiSelect && e.ctrlKey, true);
53265                         this.contextMenu.showAt(e.getXY());
53266             }
53267     },
53268
53269 /**
53270  *      Remove {@link Roo.data.Record}s at the specified indices.
53271  *      @param {Array/Number} selectedIndices The index (or Array of indices) of Records to remove.
53272  */
53273     remove: function(selectedIndices) {
53274                 selectedIndices = [].concat(selectedIndices);
53275                 for (var i = 0; i < selectedIndices.length; i++) {
53276                         var rec = this.store.getAt(selectedIndices[i]);
53277                         this.store.remove(rec);
53278                 }
53279     },
53280
53281 /**
53282  *      Double click fires the event, but also, if this is draggable, and there is only one other
53283  *      related DropZone, it transfers the selected node.
53284  */
53285     onDblClick : function(e){
53286         var item = this.findItemFromChild(e.getTarget());
53287         if(item){
53288             if (this.fireEvent("dblclick", this, this.indexOf(item), item, e) === false) {
53289                 return false;
53290             }
53291             if (this.dragGroup) {
53292                     var targets = Roo.dd.DragDropMgr.getRelated(this.dragZone, true);
53293                     while (targets.indexOf(this.dropZone) > -1) {
53294                             targets.remove(this.dropZone);
53295                                 }
53296                     if (targets.length == 1) {
53297                                         this.dragZone.cachedTarget = null;
53298                         var el = Roo.get(targets[0].getEl());
53299                         var box = el.getBox(true);
53300                         targets[0].onNodeDrop(el.dom, {
53301                                 target: el.dom,
53302                                 xy: [box.x, box.y + box.height - 1]
53303                         }, null, this.getDragData(e));
53304                     }
53305                 }
53306         }
53307     },
53308     
53309     handleSelection: function(e) {
53310                 this.dragZone.cachedTarget = null;
53311         var item = this.findItemFromChild(e.getTarget());
53312         if (!item) {
53313                 this.clearSelections(true);
53314                 return;
53315         }
53316                 if (item && (this.multiSelect || this.singleSelect)){
53317                         if(this.multiSelect && e.shiftKey && (!e.ctrlKey) && this.lastSelection){
53318                                 this.select(this.getNodes(this.indexOf(this.lastSelection), item.nodeIndex), false);
53319                         }else if (this.isSelected(this.getNode(item)) && e.ctrlKey){
53320                                 this.unselect(item);
53321                         } else {
53322                                 this.select(item, this.multiSelect && e.ctrlKey);
53323                                 this.lastSelection = item;
53324                         }
53325                 }
53326     },
53327
53328     onItemClick : function(item, index, e){
53329                 if(this.fireEvent("beforeclick", this, index, item, e) === false){
53330                         return false;
53331                 }
53332                 return true;
53333     },
53334
53335     unselect : function(nodeInfo, suppressEvent){
53336                 var node = this.getNode(nodeInfo);
53337                 if(node && this.isSelected(node)){
53338                         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
53339                                 Roo.fly(node).removeClass(this.selectedClass);
53340                                 this.selections.remove(node);
53341                                 if(!suppressEvent){
53342                                         this.fireEvent("selectionchange", this, this.selections);
53343                                 }
53344                         }
53345                 }
53346     }
53347 });
53348 /*
53349  * Based on:
53350  * Ext JS Library 1.1.1
53351  * Copyright(c) 2006-2007, Ext JS, LLC.
53352  *
53353  * Originally Released Under LGPL - original licence link has changed is not relivant.
53354  *
53355  * Fork - LGPL
53356  * <script type="text/javascript">
53357  */
53358  
53359 /**
53360  * @class Roo.LayoutManager
53361  * @extends Roo.util.Observable
53362  * Base class for layout managers.
53363  */
53364 Roo.LayoutManager = function(container, config){
53365     Roo.LayoutManager.superclass.constructor.call(this);
53366     this.el = Roo.get(container);
53367     // ie scrollbar fix
53368     if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
53369         document.body.scroll = "no";
53370     }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
53371         this.el.position('relative');
53372     }
53373     this.id = this.el.id;
53374     this.el.addClass("x-layout-container");
53375     /** false to disable window resize monitoring @type Boolean */
53376     this.monitorWindowResize = true;
53377     this.regions = {};
53378     this.addEvents({
53379         /**
53380          * @event layout
53381          * Fires when a layout is performed. 
53382          * @param {Roo.LayoutManager} this
53383          */
53384         "layout" : true,
53385         /**
53386          * @event regionresized
53387          * Fires when the user resizes a region. 
53388          * @param {Roo.LayoutRegion} region The resized region
53389          * @param {Number} newSize The new size (width for east/west, height for north/south)
53390          */
53391         "regionresized" : true,
53392         /**
53393          * @event regioncollapsed
53394          * Fires when a region is collapsed. 
53395          * @param {Roo.LayoutRegion} region The collapsed region
53396          */
53397         "regioncollapsed" : true,
53398         /**
53399          * @event regionexpanded
53400          * Fires when a region is expanded.  
53401          * @param {Roo.LayoutRegion} region The expanded region
53402          */
53403         "regionexpanded" : true
53404     });
53405     this.updating = false;
53406     Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
53407 };
53408
53409 Roo.extend(Roo.LayoutManager, Roo.util.Observable, {
53410     /**
53411      * Returns true if this layout is currently being updated
53412      * @return {Boolean}
53413      */
53414     isUpdating : function(){
53415         return this.updating; 
53416     },
53417     
53418     /**
53419      * Suspend the LayoutManager from doing auto-layouts while
53420      * making multiple add or remove calls
53421      */
53422     beginUpdate : function(){
53423         this.updating = true;    
53424     },
53425     
53426     /**
53427      * Restore auto-layouts and optionally disable the manager from performing a layout
53428      * @param {Boolean} noLayout true to disable a layout update 
53429      */
53430     endUpdate : function(noLayout){
53431         this.updating = false;
53432         if(!noLayout){
53433             this.layout();
53434         }    
53435     },
53436     
53437     layout: function(){
53438         
53439     },
53440     
53441     onRegionResized : function(region, newSize){
53442         this.fireEvent("regionresized", region, newSize);
53443         this.layout();
53444     },
53445     
53446     onRegionCollapsed : function(region){
53447         this.fireEvent("regioncollapsed", region);
53448     },
53449     
53450     onRegionExpanded : function(region){
53451         this.fireEvent("regionexpanded", region);
53452     },
53453         
53454     /**
53455      * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
53456      * performs box-model adjustments.
53457      * @return {Object} The size as an object {width: (the width), height: (the height)}
53458      */
53459     getViewSize : function(){
53460         var size;
53461         if(this.el.dom != document.body){
53462             size = this.el.getSize();
53463         }else{
53464             size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
53465         }
53466         size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
53467         size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
53468         return size;
53469     },
53470     
53471     /**
53472      * Returns the Element this layout is bound to.
53473      * @return {Roo.Element}
53474      */
53475     getEl : function(){
53476         return this.el;
53477     },
53478     
53479     /**
53480      * Returns the specified region.
53481      * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
53482      * @return {Roo.LayoutRegion}
53483      */
53484     getRegion : function(target){
53485         return this.regions[target.toLowerCase()];
53486     },
53487     
53488     onWindowResize : function(){
53489         if(this.monitorWindowResize){
53490             this.layout();
53491         }
53492     }
53493 });/*
53494  * Based on:
53495  * Ext JS Library 1.1.1
53496  * Copyright(c) 2006-2007, Ext JS, LLC.
53497  *
53498  * Originally Released Under LGPL - original licence link has changed is not relivant.
53499  *
53500  * Fork - LGPL
53501  * <script type="text/javascript">
53502  */
53503 /**
53504  * @class Roo.BorderLayout
53505  * @extends Roo.LayoutManager
53506  * @children Roo.ContentPanel
53507  * This class represents a common layout manager used in desktop applications. For screenshots and more details,
53508  * please see: <br><br>
53509  * <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>
53510  * <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>
53511  * Example:
53512  <pre><code>
53513  var layout = new Roo.BorderLayout(document.body, {
53514     north: {
53515         initialSize: 25,
53516         titlebar: false
53517     },
53518     west: {
53519         split:true,
53520         initialSize: 200,
53521         minSize: 175,
53522         maxSize: 400,
53523         titlebar: true,
53524         collapsible: true
53525     },
53526     east: {
53527         split:true,
53528         initialSize: 202,
53529         minSize: 175,
53530         maxSize: 400,
53531         titlebar: true,
53532         collapsible: true
53533     },
53534     south: {
53535         split:true,
53536         initialSize: 100,
53537         minSize: 100,
53538         maxSize: 200,
53539         titlebar: true,
53540         collapsible: true
53541     },
53542     center: {
53543         titlebar: true,
53544         autoScroll:true,
53545         resizeTabs: true,
53546         minTabWidth: 50,
53547         preferredTabWidth: 150
53548     }
53549 });
53550
53551 // shorthand
53552 var CP = Roo.ContentPanel;
53553
53554 layout.beginUpdate();
53555 layout.add("north", new CP("north", "North"));
53556 layout.add("south", new CP("south", {title: "South", closable: true}));
53557 layout.add("west", new CP("west", {title: "West"}));
53558 layout.add("east", new CP("autoTabs", {title: "Auto Tabs", closable: true}));
53559 layout.add("center", new CP("center1", {title: "Close Me", closable: true}));
53560 layout.add("center", new CP("center2", {title: "Center Panel", closable: false}));
53561 layout.getRegion("center").showPanel("center1");
53562 layout.endUpdate();
53563 </code></pre>
53564
53565 <b>The container the layout is rendered into can be either the body element or any other element.
53566 If it is not the body element, the container needs to either be an absolute positioned element,
53567 or you will need to add "position:relative" to the css of the container.  You will also need to specify
53568 the container size if it is not the body element.</b>
53569
53570 * @constructor
53571 * Create a new BorderLayout
53572 * @param {String/HTMLElement/Element} container The container this layout is bound to
53573 * @param {Object} config Configuration options
53574  */
53575 Roo.BorderLayout = function(container, config){
53576     config = config || {};
53577     Roo.BorderLayout.superclass.constructor.call(this, container, config);
53578     this.factory = config.factory || Roo.BorderLayout.RegionFactory;
53579     for(var i = 0, len = this.factory.validRegions.length; i < len; i++) {
53580         var target = this.factory.validRegions[i];
53581         if(config[target]){
53582             this.addRegion(target, config[target]);
53583         }
53584     }
53585 };
53586
53587 Roo.extend(Roo.BorderLayout, Roo.LayoutManager, {
53588         
53589         /**
53590          * @cfg {Roo.LayoutRegion} east
53591          */
53592         /**
53593          * @cfg {Roo.LayoutRegion} west
53594          */
53595         /**
53596          * @cfg {Roo.LayoutRegion} north
53597          */
53598         /**
53599          * @cfg {Roo.LayoutRegion} south
53600          */
53601         /**
53602          * @cfg {Roo.LayoutRegion} center
53603          */
53604     /**
53605      * Creates and adds a new region if it doesn't already exist.
53606      * @param {String} target The target region key (north, south, east, west or center).
53607      * @param {Object} config The regions config object
53608      * @return {BorderLayoutRegion} The new region
53609      */
53610     addRegion : function(target, config){
53611         if(!this.regions[target]){
53612             var r = this.factory.create(target, this, config);
53613             this.bindRegion(target, r);
53614         }
53615         return this.regions[target];
53616     },
53617
53618     // private (kinda)
53619     bindRegion : function(name, r){
53620         this.regions[name] = r;
53621         r.on("visibilitychange", this.layout, this);
53622         r.on("paneladded", this.layout, this);
53623         r.on("panelremoved", this.layout, this);
53624         r.on("invalidated", this.layout, this);
53625         r.on("resized", this.onRegionResized, this);
53626         r.on("collapsed", this.onRegionCollapsed, this);
53627         r.on("expanded", this.onRegionExpanded, this);
53628     },
53629
53630     /**
53631      * Performs a layout update.
53632      */
53633     layout : function(){
53634         if(this.updating) {
53635             return;
53636         }
53637         var size = this.getViewSize();
53638         var w = size.width;
53639         var h = size.height;
53640         var centerW = w;
53641         var centerH = h;
53642         var centerY = 0;
53643         var centerX = 0;
53644         //var x = 0, y = 0;
53645
53646         var rs = this.regions;
53647         var north = rs["north"];
53648         var south = rs["south"]; 
53649         var west = rs["west"];
53650         var east = rs["east"];
53651         var center = rs["center"];
53652         //if(this.hideOnLayout){ // not supported anymore
53653             //c.el.setStyle("display", "none");
53654         //}
53655         if(north && north.isVisible()){
53656             var b = north.getBox();
53657             var m = north.getMargins();
53658             b.width = w - (m.left+m.right);
53659             b.x = m.left;
53660             b.y = m.top;
53661             centerY = b.height + b.y + m.bottom;
53662             centerH -= centerY;
53663             north.updateBox(this.safeBox(b));
53664         }
53665         if(south && south.isVisible()){
53666             var b = south.getBox();
53667             var m = south.getMargins();
53668             b.width = w - (m.left+m.right);
53669             b.x = m.left;
53670             var totalHeight = (b.height + m.top + m.bottom);
53671             b.y = h - totalHeight + m.top;
53672             centerH -= totalHeight;
53673             south.updateBox(this.safeBox(b));
53674         }
53675         if(west && west.isVisible()){
53676             var b = west.getBox();
53677             var m = west.getMargins();
53678             b.height = centerH - (m.top+m.bottom);
53679             b.x = m.left;
53680             b.y = centerY + m.top;
53681             var totalWidth = (b.width + m.left + m.right);
53682             centerX += totalWidth;
53683             centerW -= totalWidth;
53684             west.updateBox(this.safeBox(b));
53685         }
53686         if(east && east.isVisible()){
53687             var b = east.getBox();
53688             var m = east.getMargins();
53689             b.height = centerH - (m.top+m.bottom);
53690             var totalWidth = (b.width + m.left + m.right);
53691             b.x = w - totalWidth + m.left;
53692             b.y = centerY + m.top;
53693             centerW -= totalWidth;
53694             east.updateBox(this.safeBox(b));
53695         }
53696         if(center){
53697             var m = center.getMargins();
53698             var centerBox = {
53699                 x: centerX + m.left,
53700                 y: centerY + m.top,
53701                 width: centerW - (m.left+m.right),
53702                 height: centerH - (m.top+m.bottom)
53703             };
53704             //if(this.hideOnLayout){
53705                 //center.el.setStyle("display", "block");
53706             //}
53707             center.updateBox(this.safeBox(centerBox));
53708         }
53709         this.el.repaint();
53710         this.fireEvent("layout", this);
53711     },
53712
53713     // private
53714     safeBox : function(box){
53715         box.width = Math.max(0, box.width);
53716         box.height = Math.max(0, box.height);
53717         return box;
53718     },
53719
53720     /**
53721      * Adds a ContentPanel (or subclass) to this layout.
53722      * @param {String} target The target region key (north, south, east, west or center).
53723      * @param {Roo.ContentPanel} panel The panel to add
53724      * @return {Roo.ContentPanel} The added panel
53725      */
53726     add : function(target, panel){
53727          
53728         target = target.toLowerCase();
53729         return this.regions[target].add(panel);
53730     },
53731
53732     /**
53733      * Remove a ContentPanel (or subclass) to this layout.
53734      * @param {String} target The target region key (north, south, east, west or center).
53735      * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
53736      * @return {Roo.ContentPanel} The removed panel
53737      */
53738     remove : function(target, panel){
53739         target = target.toLowerCase();
53740         return this.regions[target].remove(panel);
53741     },
53742
53743     /**
53744      * Searches all regions for a panel with the specified id
53745      * @param {String} panelId
53746      * @return {Roo.ContentPanel} The panel or null if it wasn't found
53747      */
53748     findPanel : function(panelId){
53749         var rs = this.regions;
53750         for(var target in rs){
53751             if(typeof rs[target] != "function"){
53752                 var p = rs[target].getPanel(panelId);
53753                 if(p){
53754                     return p;
53755                 }
53756             }
53757         }
53758         return null;
53759     },
53760
53761     /**
53762      * Searches all regions for a panel with the specified id and activates (shows) it.
53763      * @param {String/ContentPanel} panelId The panels id or the panel itself
53764      * @return {Roo.ContentPanel} The shown panel or null
53765      */
53766     showPanel : function(panelId) {
53767       var rs = this.regions;
53768       for(var target in rs){
53769          var r = rs[target];
53770          if(typeof r != "function"){
53771             if(r.hasPanel(panelId)){
53772                return r.showPanel(panelId);
53773             }
53774          }
53775       }
53776       return null;
53777    },
53778
53779    /**
53780      * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
53781      * @param {Roo.state.Provider} provider (optional) An alternate state provider
53782      */
53783     restoreState : function(provider){
53784         if(!provider){
53785             provider = Roo.state.Manager;
53786         }
53787         var sm = new Roo.LayoutStateManager();
53788         sm.init(this, provider);
53789     },
53790
53791     /**
53792      * Adds a batch of multiple ContentPanels dynamically by passing a special regions config object.  This config
53793      * object should contain properties for each region to add ContentPanels to, and each property's value should be
53794      * a valid ContentPanel config object.  Example:
53795      * <pre><code>
53796 // Create the main layout
53797 var layout = new Roo.BorderLayout('main-ct', {
53798     west: {
53799         split:true,
53800         minSize: 175,
53801         titlebar: true
53802     },
53803     center: {
53804         title:'Components'
53805     }
53806 }, 'main-ct');
53807
53808 // Create and add multiple ContentPanels at once via configs
53809 layout.batchAdd({
53810    west: {
53811        id: 'source-files',
53812        autoCreate:true,
53813        title:'Ext Source Files',
53814        autoScroll:true,
53815        fitToFrame:true
53816    },
53817    center : {
53818        el: cview,
53819        autoScroll:true,
53820        fitToFrame:true,
53821        toolbar: tb,
53822        resizeEl:'cbody'
53823    }
53824 });
53825 </code></pre>
53826      * @param {Object} regions An object containing ContentPanel configs by region name
53827      */
53828     batchAdd : function(regions){
53829         this.beginUpdate();
53830         for(var rname in regions){
53831             var lr = this.regions[rname];
53832             if(lr){
53833                 this.addTypedPanels(lr, regions[rname]);
53834             }
53835         }
53836         this.endUpdate();
53837     },
53838
53839     // private
53840     addTypedPanels : function(lr, ps){
53841         if(typeof ps == 'string'){
53842             lr.add(new Roo.ContentPanel(ps));
53843         }
53844         else if(ps instanceof Array){
53845             for(var i =0, len = ps.length; i < len; i++){
53846                 this.addTypedPanels(lr, ps[i]);
53847             }
53848         }
53849         else if(!ps.events){ // raw config?
53850             var el = ps.el;
53851             delete ps.el; // prevent conflict
53852             lr.add(new Roo.ContentPanel(el || Roo.id(), ps));
53853         }
53854         else {  // panel object assumed!
53855             lr.add(ps);
53856         }
53857     },
53858     /**
53859      * Adds a xtype elements to the layout.
53860      * <pre><code>
53861
53862 layout.addxtype({
53863        xtype : 'ContentPanel',
53864        region: 'west',
53865        items: [ .... ]
53866    }
53867 );
53868
53869 layout.addxtype({
53870         xtype : 'NestedLayoutPanel',
53871         region: 'west',
53872         layout: {
53873            center: { },
53874            west: { }   
53875         },
53876         items : [ ... list of content panels or nested layout panels.. ]
53877    }
53878 );
53879 </code></pre>
53880      * @param {Object} cfg Xtype definition of item to add.
53881      */
53882     addxtype : function(cfg)
53883     {
53884         // basically accepts a pannel...
53885         // can accept a layout region..!?!?
53886         //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
53887         
53888         if (!cfg.xtype.match(/Panel$/)) {
53889             return false;
53890         }
53891         var ret = false;
53892         
53893         if (typeof(cfg.region) == 'undefined') {
53894             Roo.log("Failed to add Panel, region was not set");
53895             Roo.log(cfg);
53896             return false;
53897         }
53898         var region = cfg.region;
53899         delete cfg.region;
53900         
53901           
53902         var xitems = [];
53903         if (cfg.items) {
53904             xitems = cfg.items;
53905             delete cfg.items;
53906         }
53907         var nb = false;
53908         
53909         switch(cfg.xtype) 
53910         {
53911             case 'ContentPanel':  // ContentPanel (el, cfg)
53912             case 'ScrollPanel':  // ContentPanel (el, cfg)
53913             case 'ViewPanel': 
53914                 if(cfg.autoCreate) {
53915                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
53916                 } else {
53917                     var el = this.el.createChild();
53918                     ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
53919                 }
53920                 
53921                 this.add(region, ret);
53922                 break;
53923             
53924             
53925             case 'TreePanel': // our new panel!
53926                 cfg.el = this.el.createChild();
53927                 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
53928                 this.add(region, ret);
53929                 break;
53930             
53931             case 'NestedLayoutPanel': 
53932                 // create a new Layout (which is  a Border Layout...
53933                 var el = this.el.createChild();
53934                 var clayout = cfg.layout;
53935                 delete cfg.layout;
53936                 clayout.items   = clayout.items  || [];
53937                 // replace this exitems with the clayout ones..
53938                 xitems = clayout.items;
53939                  
53940                 
53941                 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
53942                     cfg.background = false;
53943                 }
53944                 var layout = new Roo.BorderLayout(el, clayout);
53945                 
53946                 ret = new Roo[cfg.xtype](layout, cfg); // new panel!!!!!
53947                 //console.log('adding nested layout panel '  + cfg.toSource());
53948                 this.add(region, ret);
53949                 nb = {}; /// find first...
53950                 break;
53951                 
53952             case 'GridPanel': 
53953             
53954                 // needs grid and region
53955                 
53956                 //var el = this.getRegion(region).el.createChild();
53957                 var el = this.el.createChild();
53958                 // create the grid first...
53959                 
53960                 var grid = new Roo.grid[cfg.grid.xtype](el, cfg.grid);
53961                 delete cfg.grid;
53962                 if (region == 'center' && this.active ) {
53963                     cfg.background = false;
53964                 }
53965                 ret = new Roo[cfg.xtype](grid, cfg); // new panel!!!!!
53966                 
53967                 this.add(region, ret);
53968                 if (cfg.background) {
53969                     ret.on('activate', function(gp) {
53970                         if (!gp.grid.rendered) {
53971                             gp.grid.render();
53972                         }
53973                     });
53974                 } else {
53975                     grid.render();
53976                 }
53977                 break;
53978            
53979            
53980            
53981                 
53982                 
53983                 
53984             default:
53985                 if (typeof(Roo[cfg.xtype]) != 'undefined') {
53986                     
53987                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
53988                     this.add(region, ret);
53989                 } else {
53990                 
53991                     alert("Can not add '" + cfg.xtype + "' to BorderLayout");
53992                     return null;
53993                 }
53994                 
53995              // GridPanel (grid, cfg)
53996             
53997         }
53998         this.beginUpdate();
53999         // add children..
54000         var region = '';
54001         var abn = {};
54002         Roo.each(xitems, function(i)  {
54003             region = nb && i.region ? i.region : false;
54004             
54005             var add = ret.addxtype(i);
54006            
54007             if (region) {
54008                 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
54009                 if (!i.background) {
54010                     abn[region] = nb[region] ;
54011                 }
54012             }
54013             
54014         });
54015         this.endUpdate();
54016
54017         // make the last non-background panel active..
54018         //if (nb) { Roo.log(abn); }
54019         if (nb) {
54020             
54021             for(var r in abn) {
54022                 region = this.getRegion(r);
54023                 if (region) {
54024                     // tried using nb[r], but it does not work..
54025                      
54026                     region.showPanel(abn[r]);
54027                    
54028                 }
54029             }
54030         }
54031         return ret;
54032         
54033     }
54034 });
54035
54036 /**
54037  * Shortcut for creating a new BorderLayout object and adding one or more ContentPanels to it in a single step, handling
54038  * the beginUpdate and endUpdate calls internally.  The key to this method is the <b>panels</b> property that can be
54039  * provided with each region config, which allows you to add ContentPanel configs in addition to the region configs
54040  * during creation.  The following code is equivalent to the constructor-based example at the beginning of this class:
54041  * <pre><code>
54042 // shorthand
54043 var CP = Roo.ContentPanel;
54044
54045 var layout = Roo.BorderLayout.create({
54046     north: {
54047         initialSize: 25,
54048         titlebar: false,
54049         panels: [new CP("north", "North")]
54050     },
54051     west: {
54052         split:true,
54053         initialSize: 200,
54054         minSize: 175,
54055         maxSize: 400,
54056         titlebar: true,
54057         collapsible: true,
54058         panels: [new CP("west", {title: "West"})]
54059     },
54060     east: {
54061         split:true,
54062         initialSize: 202,
54063         minSize: 175,
54064         maxSize: 400,
54065         titlebar: true,
54066         collapsible: true,
54067         panels: [new CP("autoTabs", {title: "Auto Tabs", closable: true})]
54068     },
54069     south: {
54070         split:true,
54071         initialSize: 100,
54072         minSize: 100,
54073         maxSize: 200,
54074         titlebar: true,
54075         collapsible: true,
54076         panels: [new CP("south", {title: "South", closable: true})]
54077     },
54078     center: {
54079         titlebar: true,
54080         autoScroll:true,
54081         resizeTabs: true,
54082         minTabWidth: 50,
54083         preferredTabWidth: 150,
54084         panels: [
54085             new CP("center1", {title: "Close Me", closable: true}),
54086             new CP("center2", {title: "Center Panel", closable: false})
54087         ]
54088     }
54089 }, document.body);
54090
54091 layout.getRegion("center").showPanel("center1");
54092 </code></pre>
54093  * @param config
54094  * @param targetEl
54095  */
54096 Roo.BorderLayout.create = function(config, targetEl){
54097     var layout = new Roo.BorderLayout(targetEl || document.body, config);
54098     layout.beginUpdate();
54099     var regions = Roo.BorderLayout.RegionFactory.validRegions;
54100     for(var j = 0, jlen = regions.length; j < jlen; j++){
54101         var lr = regions[j];
54102         if(layout.regions[lr] && config[lr].panels){
54103             var r = layout.regions[lr];
54104             var ps = config[lr].panels;
54105             layout.addTypedPanels(r, ps);
54106         }
54107     }
54108     layout.endUpdate();
54109     return layout;
54110 };
54111
54112 // private
54113 Roo.BorderLayout.RegionFactory = {
54114     // private
54115     validRegions : ["north","south","east","west","center"],
54116
54117     // private
54118     create : function(target, mgr, config){
54119         target = target.toLowerCase();
54120         if(config.lightweight || config.basic){
54121             return new Roo.BasicLayoutRegion(mgr, config, target);
54122         }
54123         switch(target){
54124             case "north":
54125                 return new Roo.NorthLayoutRegion(mgr, config);
54126             case "south":
54127                 return new Roo.SouthLayoutRegion(mgr, config);
54128             case "east":
54129                 return new Roo.EastLayoutRegion(mgr, config);
54130             case "west":
54131                 return new Roo.WestLayoutRegion(mgr, config);
54132             case "center":
54133                 return new Roo.CenterLayoutRegion(mgr, config);
54134         }
54135         throw 'Layout region "'+target+'" not supported.';
54136     }
54137 };/*
54138  * Based on:
54139  * Ext JS Library 1.1.1
54140  * Copyright(c) 2006-2007, Ext JS, LLC.
54141  *
54142  * Originally Released Under LGPL - original licence link has changed is not relivant.
54143  *
54144  * Fork - LGPL
54145  * <script type="text/javascript">
54146  */
54147  
54148 /**
54149  * @class Roo.BasicLayoutRegion
54150  * @extends Roo.util.Observable
54151  * This class represents a lightweight region in a layout manager. This region does not move dom nodes
54152  * and does not have a titlebar, tabs or any other features. All it does is size and position 
54153  * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
54154  */
54155 Roo.BasicLayoutRegion = function(mgr, config, pos, skipConfig){
54156     this.mgr = mgr;
54157     this.position  = pos;
54158     this.events = {
54159         /**
54160          * @scope Roo.BasicLayoutRegion
54161          */
54162         
54163         /**
54164          * @event beforeremove
54165          * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
54166          * @param {Roo.LayoutRegion} this
54167          * @param {Roo.ContentPanel} panel The panel
54168          * @param {Object} e The cancel event object
54169          */
54170         "beforeremove" : true,
54171         /**
54172          * @event invalidated
54173          * Fires when the layout for this region is changed.
54174          * @param {Roo.LayoutRegion} this
54175          */
54176         "invalidated" : true,
54177         /**
54178          * @event visibilitychange
54179          * Fires when this region is shown or hidden 
54180          * @param {Roo.LayoutRegion} this
54181          * @param {Boolean} visibility true or false
54182          */
54183         "visibilitychange" : true,
54184         /**
54185          * @event paneladded
54186          * Fires when a panel is added. 
54187          * @param {Roo.LayoutRegion} this
54188          * @param {Roo.ContentPanel} panel The panel
54189          */
54190         "paneladded" : true,
54191         /**
54192          * @event panelremoved
54193          * Fires when a panel is removed. 
54194          * @param {Roo.LayoutRegion} this
54195          * @param {Roo.ContentPanel} panel The panel
54196          */
54197         "panelremoved" : true,
54198         /**
54199          * @event beforecollapse
54200          * Fires when this region before collapse.
54201          * @param {Roo.LayoutRegion} this
54202          */
54203         "beforecollapse" : true,
54204         /**
54205          * @event collapsed
54206          * Fires when this region is collapsed.
54207          * @param {Roo.LayoutRegion} this
54208          */
54209         "collapsed" : true,
54210         /**
54211          * @event expanded
54212          * Fires when this region is expanded.
54213          * @param {Roo.LayoutRegion} this
54214          */
54215         "expanded" : true,
54216         /**
54217          * @event slideshow
54218          * Fires when this region is slid into view.
54219          * @param {Roo.LayoutRegion} this
54220          */
54221         "slideshow" : true,
54222         /**
54223          * @event slidehide
54224          * Fires when this region slides out of view. 
54225          * @param {Roo.LayoutRegion} this
54226          */
54227         "slidehide" : true,
54228         /**
54229          * @event panelactivated
54230          * Fires when a panel is activated. 
54231          * @param {Roo.LayoutRegion} this
54232          * @param {Roo.ContentPanel} panel The activated panel
54233          */
54234         "panelactivated" : true,
54235         /**
54236          * @event resized
54237          * Fires when the user resizes this region. 
54238          * @param {Roo.LayoutRegion} this
54239          * @param {Number} newSize The new size (width for east/west, height for north/south)
54240          */
54241         "resized" : true
54242     };
54243     /** A collection of panels in this region. @type Roo.util.MixedCollection */
54244     this.panels = new Roo.util.MixedCollection();
54245     this.panels.getKey = this.getPanelId.createDelegate(this);
54246     this.box = null;
54247     this.activePanel = null;
54248     // ensure listeners are added...
54249     
54250     if (config.listeners || config.events) {
54251         Roo.BasicLayoutRegion.superclass.constructor.call(this, {
54252             listeners : config.listeners || {},
54253             events : config.events || {}
54254         });
54255     }
54256     
54257     if(skipConfig !== true){
54258         this.applyConfig(config);
54259     }
54260 };
54261
54262 Roo.extend(Roo.BasicLayoutRegion, Roo.util.Observable, {
54263     getPanelId : function(p){
54264         return p.getId();
54265     },
54266     
54267     applyConfig : function(config){
54268         this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
54269         this.config = config;
54270         
54271     },
54272     
54273     /**
54274      * Resizes the region to the specified size. For vertical regions (west, east) this adjusts 
54275      * the width, for horizontal (north, south) the height.
54276      * @param {Number} newSize The new width or height
54277      */
54278     resizeTo : function(newSize){
54279         var el = this.el ? this.el :
54280                  (this.activePanel ? this.activePanel.getEl() : null);
54281         if(el){
54282             switch(this.position){
54283                 case "east":
54284                 case "west":
54285                     el.setWidth(newSize);
54286                     this.fireEvent("resized", this, newSize);
54287                 break;
54288                 case "north":
54289                 case "south":
54290                     el.setHeight(newSize);
54291                     this.fireEvent("resized", this, newSize);
54292                 break;                
54293             }
54294         }
54295     },
54296     
54297     getBox : function(){
54298         return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
54299     },
54300     
54301     getMargins : function(){
54302         return this.margins;
54303     },
54304     
54305     updateBox : function(box){
54306         this.box = box;
54307         var el = this.activePanel.getEl();
54308         el.dom.style.left = box.x + "px";
54309         el.dom.style.top = box.y + "px";
54310         this.activePanel.setSize(box.width, box.height);
54311     },
54312     
54313     /**
54314      * Returns the container element for this region.
54315      * @return {Roo.Element}
54316      */
54317     getEl : function(){
54318         return this.activePanel;
54319     },
54320     
54321     /**
54322      * Returns true if this region is currently visible.
54323      * @return {Boolean}
54324      */
54325     isVisible : function(){
54326         return this.activePanel ? true : false;
54327     },
54328     
54329     setActivePanel : function(panel){
54330         panel = this.getPanel(panel);
54331         if(this.activePanel && this.activePanel != panel){
54332             this.activePanel.setActiveState(false);
54333             this.activePanel.getEl().setLeftTop(-10000,-10000);
54334         }
54335         this.activePanel = panel;
54336         panel.setActiveState(true);
54337         if(this.box){
54338             panel.setSize(this.box.width, this.box.height);
54339         }
54340         this.fireEvent("panelactivated", this, panel);
54341         this.fireEvent("invalidated");
54342     },
54343     
54344     /**
54345      * Show the specified panel.
54346      * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
54347      * @return {Roo.ContentPanel} The shown panel or null
54348      */
54349     showPanel : function(panel){
54350         if(panel = this.getPanel(panel)){
54351             this.setActivePanel(panel);
54352         }
54353         return panel;
54354     },
54355     
54356     /**
54357      * Get the active panel for this region.
54358      * @return {Roo.ContentPanel} The active panel or null
54359      */
54360     getActivePanel : function(){
54361         return this.activePanel;
54362     },
54363     
54364     /**
54365      * Add the passed ContentPanel(s)
54366      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
54367      * @return {Roo.ContentPanel} The panel added (if only one was added)
54368      */
54369     add : function(panel){
54370         if(arguments.length > 1){
54371             for(var i = 0, len = arguments.length; i < len; i++) {
54372                 this.add(arguments[i]);
54373             }
54374             return null;
54375         }
54376         if(this.hasPanel(panel)){
54377             this.showPanel(panel);
54378             return panel;
54379         }
54380         var el = panel.getEl();
54381         if(el.dom.parentNode != this.mgr.el.dom){
54382             this.mgr.el.dom.appendChild(el.dom);
54383         }
54384         if(panel.setRegion){
54385             panel.setRegion(this);
54386         }
54387         this.panels.add(panel);
54388         el.setStyle("position", "absolute");
54389         if(!panel.background){
54390             this.setActivePanel(panel);
54391             if(this.config.initialSize && this.panels.getCount()==1){
54392                 this.resizeTo(this.config.initialSize);
54393             }
54394         }
54395         this.fireEvent("paneladded", this, panel);
54396         return panel;
54397     },
54398     
54399     /**
54400      * Returns true if the panel is in this region.
54401      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
54402      * @return {Boolean}
54403      */
54404     hasPanel : function(panel){
54405         if(typeof panel == "object"){ // must be panel obj
54406             panel = panel.getId();
54407         }
54408         return this.getPanel(panel) ? true : false;
54409     },
54410     
54411     /**
54412      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
54413      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
54414      * @param {Boolean} preservePanel Overrides the config preservePanel option
54415      * @return {Roo.ContentPanel} The panel that was removed
54416      */
54417     remove : function(panel, preservePanel){
54418         panel = this.getPanel(panel);
54419         if(!panel){
54420             return null;
54421         }
54422         var e = {};
54423         this.fireEvent("beforeremove", this, panel, e);
54424         if(e.cancel === true){
54425             return null;
54426         }
54427         var panelId = panel.getId();
54428         this.panels.removeKey(panelId);
54429         return panel;
54430     },
54431     
54432     /**
54433      * Returns the panel specified or null if it's not in this region.
54434      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
54435      * @return {Roo.ContentPanel}
54436      */
54437     getPanel : function(id){
54438         if(typeof id == "object"){ // must be panel obj
54439             return id;
54440         }
54441         return this.panels.get(id);
54442     },
54443     
54444     /**
54445      * Returns this regions position (north/south/east/west/center).
54446      * @return {String} 
54447      */
54448     getPosition: function(){
54449         return this.position;    
54450     }
54451 });/*
54452  * Based on:
54453  * Ext JS Library 1.1.1
54454  * Copyright(c) 2006-2007, Ext JS, LLC.
54455  *
54456  * Originally Released Under LGPL - original licence link has changed is not relivant.
54457  *
54458  * Fork - LGPL
54459  * <script type="text/javascript">
54460  */
54461  
54462 /**
54463  * @class Roo.LayoutRegion
54464  * @extends Roo.BasicLayoutRegion
54465  * This class represents a region in a layout manager.
54466  * @cfg {Boolean}   collapsible     False to disable collapsing (defaults to true)
54467  * @cfg {Boolean}   collapsed       True to set the initial display to collapsed (defaults to false)
54468  * @cfg {Boolean}   floatable       False to disable floating (defaults to true)
54469  * @cfg {Object}    margins         Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
54470  * @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})
54471  * @cfg {String}    tabPosition     (top|bottom) "top" or "bottom" (defaults to "bottom")
54472  * @cfg {String}    collapsedTitle  Optional string message to display in the collapsed block of a north or south region
54473  * @cfg {Boolean}   alwaysShowTabs  True to always display tabs even when there is only 1 panel (defaults to false)
54474  * @cfg {Boolean}   autoScroll      True to enable overflow scrolling (defaults to false)
54475  * @cfg {Boolean}   titlebar        True to display a title bar (defaults to true)
54476  * @cfg {String}    title           The title for the region (overrides panel titles)
54477  * @cfg {Boolean}   animate         True to animate expand/collapse (defaults to false)
54478  * @cfg {Boolean}   autoHide        False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
54479  * @cfg {Boolean}   preservePanels  True to preserve removed panels so they can be readded later (defaults to false)
54480  * @cfg {Boolean}   closeOnTab      True to place the close icon on the tabs instead of the region titlebar (defaults to false)
54481  * @cfg {Boolean}   hideTabs        True to hide the tab strip (defaults to false)
54482  * @cfg {Boolean}   resizeTabs      True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
54483  *                      the space available, similar to FireFox 1.5 tabs (defaults to false)
54484  * @cfg {Number}    minTabWidth     The minimum tab width (defaults to 40)
54485  * @cfg {Number}    preferredTabWidth The preferred tab width (defaults to 150)
54486  * @cfg {Boolean}   showPin         True to show a pin button
54487  * @cfg {Boolean}   hidden          True to start the region hidden (defaults to false)
54488  * @cfg {Boolean}   hideWhenEmpty   True to hide the region when it has no panels
54489  * @cfg {Boolean}   disableTabTips  True to disable tab tooltips
54490  * @cfg {Number}    width           For East/West panels
54491  * @cfg {Number}    height          For North/South panels
54492  * @cfg {Boolean}   split           To show the splitter
54493  * @cfg {Boolean}   toolbar         xtype configuration for a toolbar - shows on right of tabbar
54494  */
54495 Roo.LayoutRegion = function(mgr, config, pos){
54496     Roo.LayoutRegion.superclass.constructor.call(this, mgr, config, pos, true);
54497     var dh = Roo.DomHelper;
54498     /** This region's container element 
54499     * @type Roo.Element */
54500     this.el = dh.append(mgr.el.dom, {tag: "div", cls: "x-layout-panel x-layout-panel-" + this.position}, true);
54501     /** This region's title element 
54502     * @type Roo.Element */
54503
54504     this.titleEl = dh.append(this.el.dom, {tag: "div", unselectable: "on", cls: "x-unselectable x-layout-panel-hd x-layout-title-"+this.position, children:[
54505         {tag: "span", cls: "x-unselectable x-layout-panel-hd-text", unselectable: "on", html: "&#160;"},
54506         {tag: "div", cls: "x-unselectable x-layout-panel-hd-tools", unselectable: "on"}
54507     ]}, true);
54508     this.titleEl.enableDisplayMode();
54509     /** This region's title text element 
54510     * @type HTMLElement */
54511     this.titleTextEl = this.titleEl.dom.firstChild;
54512     this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
54513     this.closeBtn = this.createTool(this.tools.dom, "x-layout-close");
54514     this.closeBtn.enableDisplayMode();
54515     this.closeBtn.on("click", this.closeClicked, this);
54516     this.closeBtn.hide();
54517
54518     this.createBody(config);
54519     this.visible = true;
54520     this.collapsed = false;
54521
54522     if(config.hideWhenEmpty){
54523         this.hide();
54524         this.on("paneladded", this.validateVisibility, this);
54525         this.on("panelremoved", this.validateVisibility, this);
54526     }
54527     this.applyConfig(config);
54528 };
54529
54530 Roo.extend(Roo.LayoutRegion, Roo.BasicLayoutRegion, {
54531
54532     createBody : function(){
54533         /** This region's body element 
54534         * @type Roo.Element */
54535         this.bodyEl = this.el.createChild({tag: "div", cls: "x-layout-panel-body"});
54536     },
54537
54538     applyConfig : function(c){
54539         if(c.collapsible && this.position != "center" && !this.collapsedEl){
54540             var dh = Roo.DomHelper;
54541             if(c.titlebar !== false){
54542                 this.collapseBtn = this.createTool(this.tools.dom, "x-layout-collapse-"+this.position);
54543                 this.collapseBtn.on("click", this.collapse, this);
54544                 this.collapseBtn.enableDisplayMode();
54545
54546                 if(c.showPin === true || this.showPin){
54547                     this.stickBtn = this.createTool(this.tools.dom, "x-layout-stick");
54548                     this.stickBtn.enableDisplayMode();
54549                     this.stickBtn.on("click", this.expand, this);
54550                     this.stickBtn.hide();
54551                 }
54552             }
54553             /** This region's collapsed element
54554             * @type Roo.Element */
54555             this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
54556                 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
54557             ]}, true);
54558             if(c.floatable !== false){
54559                this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
54560                this.collapsedEl.on("click", this.collapseClick, this);
54561             }
54562
54563             if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
54564                 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
54565                    id: "message", unselectable: "on", style:{"float":"left"}});
54566                this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
54567              }
54568             this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
54569             this.expandBtn.on("click", this.expand, this);
54570         }
54571         if(this.collapseBtn){
54572             this.collapseBtn.setVisible(c.collapsible == true);
54573         }
54574         this.cmargins = c.cmargins || this.cmargins ||
54575                          (this.position == "west" || this.position == "east" ?
54576                              {top: 0, left: 2, right:2, bottom: 0} :
54577                              {top: 2, left: 0, right:0, bottom: 2});
54578         this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
54579         this.bottomTabs = c.tabPosition != "top";
54580         this.autoScroll = c.autoScroll || false;
54581         if(this.autoScroll){
54582             this.bodyEl.setStyle("overflow", "auto");
54583         }else{
54584             this.bodyEl.setStyle("overflow", "hidden");
54585         }
54586         //if(c.titlebar !== false){
54587             if((!c.titlebar && !c.title) || c.titlebar === false){
54588                 this.titleEl.hide();
54589             }else{
54590                 this.titleEl.show();
54591                 if(c.title){
54592                     this.titleTextEl.innerHTML = c.title;
54593                 }
54594             }
54595         //}
54596         this.duration = c.duration || .30;
54597         this.slideDuration = c.slideDuration || .45;
54598         this.config = c;
54599         if(c.collapsed){
54600             this.collapse(true);
54601         }
54602         if(c.hidden){
54603             this.hide();
54604         }
54605     },
54606     /**
54607      * Returns true if this region is currently visible.
54608      * @return {Boolean}
54609      */
54610     isVisible : function(){
54611         return this.visible;
54612     },
54613
54614     /**
54615      * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
54616      * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&amp;#160;")
54617      */
54618     setCollapsedTitle : function(title){
54619         title = title || "&#160;";
54620         if(this.collapsedTitleTextEl){
54621             this.collapsedTitleTextEl.innerHTML = title;
54622         }
54623     },
54624
54625     getBox : function(){
54626         var b;
54627         if(!this.collapsed){
54628             b = this.el.getBox(false, true);
54629         }else{
54630             b = this.collapsedEl.getBox(false, true);
54631         }
54632         return b;
54633     },
54634
54635     getMargins : function(){
54636         return this.collapsed ? this.cmargins : this.margins;
54637     },
54638
54639     highlight : function(){
54640         this.el.addClass("x-layout-panel-dragover");
54641     },
54642
54643     unhighlight : function(){
54644         this.el.removeClass("x-layout-panel-dragover");
54645     },
54646
54647     updateBox : function(box){
54648         this.box = box;
54649         if(!this.collapsed){
54650             this.el.dom.style.left = box.x + "px";
54651             this.el.dom.style.top = box.y + "px";
54652             this.updateBody(box.width, box.height);
54653         }else{
54654             this.collapsedEl.dom.style.left = box.x + "px";
54655             this.collapsedEl.dom.style.top = box.y + "px";
54656             this.collapsedEl.setSize(box.width, box.height);
54657         }
54658         if(this.tabs){
54659             this.tabs.autoSizeTabs();
54660         }
54661     },
54662
54663     updateBody : function(w, h){
54664         if(w !== null){
54665             this.el.setWidth(w);
54666             w -= this.el.getBorderWidth("rl");
54667             if(this.config.adjustments){
54668                 w += this.config.adjustments[0];
54669             }
54670         }
54671         if(h !== null){
54672             this.el.setHeight(h);
54673             h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
54674             h -= this.el.getBorderWidth("tb");
54675             if(this.config.adjustments){
54676                 h += this.config.adjustments[1];
54677             }
54678             this.bodyEl.setHeight(h);
54679             if(this.tabs){
54680                 h = this.tabs.syncHeight(h);
54681             }
54682         }
54683         if(this.panelSize){
54684             w = w !== null ? w : this.panelSize.width;
54685             h = h !== null ? h : this.panelSize.height;
54686         }
54687         if(this.activePanel){
54688             var el = this.activePanel.getEl();
54689             w = w !== null ? w : el.getWidth();
54690             h = h !== null ? h : el.getHeight();
54691             this.panelSize = {width: w, height: h};
54692             this.activePanel.setSize(w, h);
54693         }
54694         if(Roo.isIE && this.tabs){
54695             this.tabs.el.repaint();
54696         }
54697     },
54698
54699     /**
54700      * Returns the container element for this region.
54701      * @return {Roo.Element}
54702      */
54703     getEl : function(){
54704         return this.el;
54705     },
54706
54707     /**
54708      * Hides this region.
54709      */
54710     hide : function(){
54711         if(!this.collapsed){
54712             this.el.dom.style.left = "-2000px";
54713             this.el.hide();
54714         }else{
54715             this.collapsedEl.dom.style.left = "-2000px";
54716             this.collapsedEl.hide();
54717         }
54718         this.visible = false;
54719         this.fireEvent("visibilitychange", this, false);
54720     },
54721
54722     /**
54723      * Shows this region if it was previously hidden.
54724      */
54725     show : function(){
54726         if(!this.collapsed){
54727             this.el.show();
54728         }else{
54729             this.collapsedEl.show();
54730         }
54731         this.visible = true;
54732         this.fireEvent("visibilitychange", this, true);
54733     },
54734
54735     closeClicked : function(){
54736         if(this.activePanel){
54737             this.remove(this.activePanel);
54738         }
54739     },
54740
54741     collapseClick : function(e){
54742         if(this.isSlid){
54743            e.stopPropagation();
54744            this.slideIn();
54745         }else{
54746            e.stopPropagation();
54747            this.slideOut();
54748         }
54749     },
54750
54751     /**
54752      * Collapses this region.
54753      * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
54754      */
54755     collapse : function(skipAnim, skipCheck){
54756         if(this.collapsed) {
54757             return;
54758         }
54759         
54760         if(skipCheck || this.fireEvent("beforecollapse", this) != false){
54761             
54762             this.collapsed = true;
54763             if(this.split){
54764                 this.split.el.hide();
54765             }
54766             if(this.config.animate && skipAnim !== true){
54767                 this.fireEvent("invalidated", this);
54768                 this.animateCollapse();
54769             }else{
54770                 this.el.setLocation(-20000,-20000);
54771                 this.el.hide();
54772                 this.collapsedEl.show();
54773                 this.fireEvent("collapsed", this);
54774                 this.fireEvent("invalidated", this);
54775             }
54776         }
54777         
54778     },
54779
54780     animateCollapse : function(){
54781         // overridden
54782     },
54783
54784     /**
54785      * Expands this region if it was previously collapsed.
54786      * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
54787      * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
54788      */
54789     expand : function(e, skipAnim){
54790         if(e) {
54791             e.stopPropagation();
54792         }
54793         if(!this.collapsed || this.el.hasActiveFx()) {
54794             return;
54795         }
54796         if(this.isSlid){
54797             this.afterSlideIn();
54798             skipAnim = true;
54799         }
54800         this.collapsed = false;
54801         if(this.config.animate && skipAnim !== true){
54802             this.animateExpand();
54803         }else{
54804             this.el.show();
54805             if(this.split){
54806                 this.split.el.show();
54807             }
54808             this.collapsedEl.setLocation(-2000,-2000);
54809             this.collapsedEl.hide();
54810             this.fireEvent("invalidated", this);
54811             this.fireEvent("expanded", this);
54812         }
54813     },
54814
54815     animateExpand : function(){
54816         // overridden
54817     },
54818
54819     initTabs : function()
54820     {
54821         this.bodyEl.setStyle("overflow", "hidden");
54822         var ts = new Roo.TabPanel(
54823                 this.bodyEl.dom,
54824                 {
54825                     tabPosition: this.bottomTabs ? 'bottom' : 'top',
54826                     disableTooltips: this.config.disableTabTips,
54827                     toolbar : this.config.toolbar
54828                 }
54829         );
54830         if(this.config.hideTabs){
54831             ts.stripWrap.setDisplayed(false);
54832         }
54833         this.tabs = ts;
54834         ts.resizeTabs = this.config.resizeTabs === true;
54835         ts.minTabWidth = this.config.minTabWidth || 40;
54836         ts.maxTabWidth = this.config.maxTabWidth || 250;
54837         ts.preferredTabWidth = this.config.preferredTabWidth || 150;
54838         ts.monitorResize = false;
54839         ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
54840         ts.bodyEl.addClass('x-layout-tabs-body');
54841         this.panels.each(this.initPanelAsTab, this);
54842     },
54843
54844     initPanelAsTab : function(panel){
54845         var ti = this.tabs.addTab(panel.getEl().id, panel.getTitle(), null,
54846                     this.config.closeOnTab && panel.isClosable());
54847         if(panel.tabTip !== undefined){
54848             ti.setTooltip(panel.tabTip);
54849         }
54850         ti.on("activate", function(){
54851               this.setActivePanel(panel);
54852         }, this);
54853         if(this.config.closeOnTab){
54854             ti.on("beforeclose", function(t, e){
54855                 e.cancel = true;
54856                 this.remove(panel);
54857             }, this);
54858         }
54859         return ti;
54860     },
54861
54862     updatePanelTitle : function(panel, title){
54863         if(this.activePanel == panel){
54864             this.updateTitle(title);
54865         }
54866         if(this.tabs){
54867             var ti = this.tabs.getTab(panel.getEl().id);
54868             ti.setText(title);
54869             if(panel.tabTip !== undefined){
54870                 ti.setTooltip(panel.tabTip);
54871             }
54872         }
54873     },
54874
54875     updateTitle : function(title){
54876         if(this.titleTextEl && !this.config.title){
54877             this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : "&#160;");
54878         }
54879     },
54880
54881     setActivePanel : function(panel){
54882         panel = this.getPanel(panel);
54883         if(this.activePanel && this.activePanel != panel){
54884             this.activePanel.setActiveState(false);
54885         }
54886         this.activePanel = panel;
54887         panel.setActiveState(true);
54888         if(this.panelSize){
54889             panel.setSize(this.panelSize.width, this.panelSize.height);
54890         }
54891         if(this.closeBtn){
54892             this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
54893         }
54894         this.updateTitle(panel.getTitle());
54895         if(this.tabs){
54896             this.fireEvent("invalidated", this);
54897         }
54898         this.fireEvent("panelactivated", this, panel);
54899     },
54900
54901     /**
54902      * Shows the specified panel.
54903      * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
54904      * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
54905      */
54906     showPanel : function(panel)
54907     {
54908         panel = this.getPanel(panel);
54909         if(panel){
54910             if(this.tabs){
54911                 var tab = this.tabs.getTab(panel.getEl().id);
54912                 if(tab.isHidden()){
54913                     this.tabs.unhideTab(tab.id);
54914                 }
54915                 tab.activate();
54916             }else{
54917                 this.setActivePanel(panel);
54918             }
54919         }
54920         return panel;
54921     },
54922
54923     /**
54924      * Get the active panel for this region.
54925      * @return {Roo.ContentPanel} The active panel or null
54926      */
54927     getActivePanel : function(){
54928         return this.activePanel;
54929     },
54930
54931     validateVisibility : function(){
54932         if(this.panels.getCount() < 1){
54933             this.updateTitle("&#160;");
54934             this.closeBtn.hide();
54935             this.hide();
54936         }else{
54937             if(!this.isVisible()){
54938                 this.show();
54939             }
54940         }
54941     },
54942
54943     /**
54944      * Adds the passed ContentPanel(s) to this region.
54945      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
54946      * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
54947      */
54948     add : function(panel){
54949         if(arguments.length > 1){
54950             for(var i = 0, len = arguments.length; i < len; i++) {
54951                 this.add(arguments[i]);
54952             }
54953             return null;
54954         }
54955         if(this.hasPanel(panel)){
54956             this.showPanel(panel);
54957             return panel;
54958         }
54959         panel.setRegion(this);
54960         this.panels.add(panel);
54961         if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
54962             this.bodyEl.dom.appendChild(panel.getEl().dom);
54963             if(panel.background !== true){
54964                 this.setActivePanel(panel);
54965             }
54966             this.fireEvent("paneladded", this, panel);
54967             return panel;
54968         }
54969         if(!this.tabs){
54970             this.initTabs();
54971         }else{
54972             this.initPanelAsTab(panel);
54973         }
54974         if(panel.background !== true){
54975             this.tabs.activate(panel.getEl().id);
54976         }
54977         this.fireEvent("paneladded", this, panel);
54978         return panel;
54979     },
54980
54981     /**
54982      * Hides the tab for the specified panel.
54983      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
54984      */
54985     hidePanel : function(panel){
54986         if(this.tabs && (panel = this.getPanel(panel))){
54987             this.tabs.hideTab(panel.getEl().id);
54988         }
54989     },
54990
54991     /**
54992      * Unhides the tab for a previously hidden panel.
54993      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
54994      */
54995     unhidePanel : function(panel){
54996         if(this.tabs && (panel = this.getPanel(panel))){
54997             this.tabs.unhideTab(panel.getEl().id);
54998         }
54999     },
55000
55001     clearPanels : function(){
55002         while(this.panels.getCount() > 0){
55003              this.remove(this.panels.first());
55004         }
55005     },
55006
55007     /**
55008      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
55009      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
55010      * @param {Boolean} preservePanel Overrides the config preservePanel option
55011      * @return {Roo.ContentPanel} The panel that was removed
55012      */
55013     remove : function(panel, preservePanel){
55014         panel = this.getPanel(panel);
55015         if(!panel){
55016             return null;
55017         }
55018         var e = {};
55019         this.fireEvent("beforeremove", this, panel, e);
55020         if(e.cancel === true){
55021             return null;
55022         }
55023         preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
55024         var panelId = panel.getId();
55025         this.panels.removeKey(panelId);
55026         if(preservePanel){
55027             document.body.appendChild(panel.getEl().dom);
55028         }
55029         if(this.tabs){
55030             this.tabs.removeTab(panel.getEl().id);
55031         }else if (!preservePanel){
55032             this.bodyEl.dom.removeChild(panel.getEl().dom);
55033         }
55034         if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
55035             var p = this.panels.first();
55036             var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
55037             tempEl.appendChild(p.getEl().dom);
55038             this.bodyEl.update("");
55039             this.bodyEl.dom.appendChild(p.getEl().dom);
55040             tempEl = null;
55041             this.updateTitle(p.getTitle());
55042             this.tabs = null;
55043             this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
55044             this.setActivePanel(p);
55045         }
55046         panel.setRegion(null);
55047         if(this.activePanel == panel){
55048             this.activePanel = null;
55049         }
55050         if(this.config.autoDestroy !== false && preservePanel !== true){
55051             try{panel.destroy();}catch(e){}
55052         }
55053         this.fireEvent("panelremoved", this, panel);
55054         return panel;
55055     },
55056
55057     /**
55058      * Returns the TabPanel component used by this region
55059      * @return {Roo.TabPanel}
55060      */
55061     getTabs : function(){
55062         return this.tabs;
55063     },
55064
55065     createTool : function(parentEl, className){
55066         var btn = Roo.DomHelper.append(parentEl, {tag: "div", cls: "x-layout-tools-button",
55067             children: [{tag: "div", cls: "x-layout-tools-button-inner " + className, html: "&#160;"}]}, true);
55068         btn.addClassOnOver("x-layout-tools-button-over");
55069         return btn;
55070     }
55071 });/*
55072  * Based on:
55073  * Ext JS Library 1.1.1
55074  * Copyright(c) 2006-2007, Ext JS, LLC.
55075  *
55076  * Originally Released Under LGPL - original licence link has changed is not relivant.
55077  *
55078  * Fork - LGPL
55079  * <script type="text/javascript">
55080  */
55081  
55082
55083
55084 /**
55085  * @class Roo.SplitLayoutRegion
55086  * @extends Roo.LayoutRegion
55087  * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
55088  */
55089 Roo.SplitLayoutRegion = function(mgr, config, pos, cursor){
55090     this.cursor = cursor;
55091     Roo.SplitLayoutRegion.superclass.constructor.call(this, mgr, config, pos);
55092 };
55093
55094 Roo.extend(Roo.SplitLayoutRegion, Roo.LayoutRegion, {
55095     splitTip : "Drag to resize.",
55096     collapsibleSplitTip : "Drag to resize. Double click to hide.",
55097     useSplitTips : false,
55098
55099     applyConfig : function(config){
55100         Roo.SplitLayoutRegion.superclass.applyConfig.call(this, config);
55101         if(config.split){
55102             if(!this.split){
55103                 var splitEl = Roo.DomHelper.append(this.mgr.el.dom, 
55104                         {tag: "div", id: this.el.id + "-split", cls: "x-layout-split x-layout-split-"+this.position, html: "&#160;"});
55105                 /** The SplitBar for this region 
55106                 * @type Roo.SplitBar */
55107                 this.split = new Roo.SplitBar(splitEl, this.el, this.orientation);
55108                 this.split.on("moved", this.onSplitMove, this);
55109                 this.split.useShim = config.useShim === true;
55110                 this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
55111                 if(this.useSplitTips){
55112                     this.split.el.dom.title = config.collapsible ? this.collapsibleSplitTip : this.splitTip;
55113                 }
55114                 if(config.collapsible){
55115                     this.split.el.on("dblclick", this.collapse,  this);
55116                 }
55117             }
55118             if(typeof config.minSize != "undefined"){
55119                 this.split.minSize = config.minSize;
55120             }
55121             if(typeof config.maxSize != "undefined"){
55122                 this.split.maxSize = config.maxSize;
55123             }
55124             if(config.hideWhenEmpty || config.hidden || config.collapsed){
55125                 this.hideSplitter();
55126             }
55127         }
55128     },
55129
55130     getHMaxSize : function(){
55131          var cmax = this.config.maxSize || 10000;
55132          var center = this.mgr.getRegion("center");
55133          return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
55134     },
55135
55136     getVMaxSize : function(){
55137          var cmax = this.config.maxSize || 10000;
55138          var center = this.mgr.getRegion("center");
55139          return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
55140     },
55141
55142     onSplitMove : function(split, newSize){
55143         this.fireEvent("resized", this, newSize);
55144     },
55145     
55146     /** 
55147      * Returns the {@link Roo.SplitBar} for this region.
55148      * @return {Roo.SplitBar}
55149      */
55150     getSplitBar : function(){
55151         return this.split;
55152     },
55153     
55154     hide : function(){
55155         this.hideSplitter();
55156         Roo.SplitLayoutRegion.superclass.hide.call(this);
55157     },
55158
55159     hideSplitter : function(){
55160         if(this.split){
55161             this.split.el.setLocation(-2000,-2000);
55162             this.split.el.hide();
55163         }
55164     },
55165
55166     show : function(){
55167         if(this.split){
55168             this.split.el.show();
55169         }
55170         Roo.SplitLayoutRegion.superclass.show.call(this);
55171     },
55172     
55173     beforeSlide: function(){
55174         if(Roo.isGecko){// firefox overflow auto bug workaround
55175             this.bodyEl.clip();
55176             if(this.tabs) {
55177                 this.tabs.bodyEl.clip();
55178             }
55179             if(this.activePanel){
55180                 this.activePanel.getEl().clip();
55181                 
55182                 if(this.activePanel.beforeSlide){
55183                     this.activePanel.beforeSlide();
55184                 }
55185             }
55186         }
55187     },
55188     
55189     afterSlide : function(){
55190         if(Roo.isGecko){// firefox overflow auto bug workaround
55191             this.bodyEl.unclip();
55192             if(this.tabs) {
55193                 this.tabs.bodyEl.unclip();
55194             }
55195             if(this.activePanel){
55196                 this.activePanel.getEl().unclip();
55197                 if(this.activePanel.afterSlide){
55198                     this.activePanel.afterSlide();
55199                 }
55200             }
55201         }
55202     },
55203
55204     initAutoHide : function(){
55205         if(this.autoHide !== false){
55206             if(!this.autoHideHd){
55207                 var st = new Roo.util.DelayedTask(this.slideIn, this);
55208                 this.autoHideHd = {
55209                     "mouseout": function(e){
55210                         if(!e.within(this.el, true)){
55211                             st.delay(500);
55212                         }
55213                     },
55214                     "mouseover" : function(e){
55215                         st.cancel();
55216                     },
55217                     scope : this
55218                 };
55219             }
55220             this.el.on(this.autoHideHd);
55221         }
55222     },
55223
55224     clearAutoHide : function(){
55225         if(this.autoHide !== false){
55226             this.el.un("mouseout", this.autoHideHd.mouseout);
55227             this.el.un("mouseover", this.autoHideHd.mouseover);
55228         }
55229     },
55230
55231     clearMonitor : function(){
55232         Roo.get(document).un("click", this.slideInIf, this);
55233     },
55234
55235     // these names are backwards but not changed for compat
55236     slideOut : function(){
55237         if(this.isSlid || this.el.hasActiveFx()){
55238             return;
55239         }
55240         this.isSlid = true;
55241         if(this.collapseBtn){
55242             this.collapseBtn.hide();
55243         }
55244         this.closeBtnState = this.closeBtn.getStyle('display');
55245         this.closeBtn.hide();
55246         if(this.stickBtn){
55247             this.stickBtn.show();
55248         }
55249         this.el.show();
55250         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
55251         this.beforeSlide();
55252         this.el.setStyle("z-index", 10001);
55253         this.el.slideIn(this.getSlideAnchor(), {
55254             callback: function(){
55255                 this.afterSlide();
55256                 this.initAutoHide();
55257                 Roo.get(document).on("click", this.slideInIf, this);
55258                 this.fireEvent("slideshow", this);
55259             },
55260             scope: this,
55261             block: true
55262         });
55263     },
55264
55265     afterSlideIn : function(){
55266         this.clearAutoHide();
55267         this.isSlid = false;
55268         this.clearMonitor();
55269         this.el.setStyle("z-index", "");
55270         if(this.collapseBtn){
55271             this.collapseBtn.show();
55272         }
55273         this.closeBtn.setStyle('display', this.closeBtnState);
55274         if(this.stickBtn){
55275             this.stickBtn.hide();
55276         }
55277         this.fireEvent("slidehide", this);
55278     },
55279
55280     slideIn : function(cb){
55281         if(!this.isSlid || this.el.hasActiveFx()){
55282             Roo.callback(cb);
55283             return;
55284         }
55285         this.isSlid = false;
55286         this.beforeSlide();
55287         this.el.slideOut(this.getSlideAnchor(), {
55288             callback: function(){
55289                 this.el.setLeftTop(-10000, -10000);
55290                 this.afterSlide();
55291                 this.afterSlideIn();
55292                 Roo.callback(cb);
55293             },
55294             scope: this,
55295             block: true
55296         });
55297     },
55298     
55299     slideInIf : function(e){
55300         if(!e.within(this.el)){
55301             this.slideIn();
55302         }
55303     },
55304
55305     animateCollapse : function(){
55306         this.beforeSlide();
55307         this.el.setStyle("z-index", 20000);
55308         var anchor = this.getSlideAnchor();
55309         this.el.slideOut(anchor, {
55310             callback : function(){
55311                 this.el.setStyle("z-index", "");
55312                 this.collapsedEl.slideIn(anchor, {duration:.3});
55313                 this.afterSlide();
55314                 this.el.setLocation(-10000,-10000);
55315                 this.el.hide();
55316                 this.fireEvent("collapsed", this);
55317             },
55318             scope: this,
55319             block: true
55320         });
55321     },
55322
55323     animateExpand : function(){
55324         this.beforeSlide();
55325         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
55326         this.el.setStyle("z-index", 20000);
55327         this.collapsedEl.hide({
55328             duration:.1
55329         });
55330         this.el.slideIn(this.getSlideAnchor(), {
55331             callback : function(){
55332                 this.el.setStyle("z-index", "");
55333                 this.afterSlide();
55334                 if(this.split){
55335                     this.split.el.show();
55336                 }
55337                 this.fireEvent("invalidated", this);
55338                 this.fireEvent("expanded", this);
55339             },
55340             scope: this,
55341             block: true
55342         });
55343     },
55344
55345     anchors : {
55346         "west" : "left",
55347         "east" : "right",
55348         "north" : "top",
55349         "south" : "bottom"
55350     },
55351
55352     sanchors : {
55353         "west" : "l",
55354         "east" : "r",
55355         "north" : "t",
55356         "south" : "b"
55357     },
55358
55359     canchors : {
55360         "west" : "tl-tr",
55361         "east" : "tr-tl",
55362         "north" : "tl-bl",
55363         "south" : "bl-tl"
55364     },
55365
55366     getAnchor : function(){
55367         return this.anchors[this.position];
55368     },
55369
55370     getCollapseAnchor : function(){
55371         return this.canchors[this.position];
55372     },
55373
55374     getSlideAnchor : function(){
55375         return this.sanchors[this.position];
55376     },
55377
55378     getAlignAdj : function(){
55379         var cm = this.cmargins;
55380         switch(this.position){
55381             case "west":
55382                 return [0, 0];
55383             break;
55384             case "east":
55385                 return [0, 0];
55386             break;
55387             case "north":
55388                 return [0, 0];
55389             break;
55390             case "south":
55391                 return [0, 0];
55392             break;
55393         }
55394     },
55395
55396     getExpandAdj : function(){
55397         var c = this.collapsedEl, cm = this.cmargins;
55398         switch(this.position){
55399             case "west":
55400                 return [-(cm.right+c.getWidth()+cm.left), 0];
55401             break;
55402             case "east":
55403                 return [cm.right+c.getWidth()+cm.left, 0];
55404             break;
55405             case "north":
55406                 return [0, -(cm.top+cm.bottom+c.getHeight())];
55407             break;
55408             case "south":
55409                 return [0, cm.top+cm.bottom+c.getHeight()];
55410             break;
55411         }
55412     }
55413 });/*
55414  * Based on:
55415  * Ext JS Library 1.1.1
55416  * Copyright(c) 2006-2007, Ext JS, LLC.
55417  *
55418  * Originally Released Under LGPL - original licence link has changed is not relivant.
55419  *
55420  * Fork - LGPL
55421  * <script type="text/javascript">
55422  */
55423 /*
55424  * These classes are private internal classes
55425  */
55426 Roo.CenterLayoutRegion = function(mgr, config){
55427     Roo.LayoutRegion.call(this, mgr, config, "center");
55428     this.visible = true;
55429     this.minWidth = config.minWidth || 20;
55430     this.minHeight = config.minHeight || 20;
55431 };
55432
55433 Roo.extend(Roo.CenterLayoutRegion, Roo.LayoutRegion, {
55434     hide : function(){
55435         // center panel can't be hidden
55436     },
55437     
55438     show : function(){
55439         // center panel can't be hidden
55440     },
55441     
55442     getMinWidth: function(){
55443         return this.minWidth;
55444     },
55445     
55446     getMinHeight: function(){
55447         return this.minHeight;
55448     }
55449 });
55450
55451
55452 Roo.NorthLayoutRegion = function(mgr, config){
55453     Roo.LayoutRegion.call(this, mgr, config, "north", "n-resize");
55454     if(this.split){
55455         this.split.placement = Roo.SplitBar.TOP;
55456         this.split.orientation = Roo.SplitBar.VERTICAL;
55457         this.split.el.addClass("x-layout-split-v");
55458     }
55459     var size = config.initialSize || config.height;
55460     if(typeof size != "undefined"){
55461         this.el.setHeight(size);
55462     }
55463 };
55464 Roo.extend(Roo.NorthLayoutRegion, Roo.SplitLayoutRegion, {
55465     orientation: Roo.SplitBar.VERTICAL,
55466     getBox : function(){
55467         if(this.collapsed){
55468             return this.collapsedEl.getBox();
55469         }
55470         var box = this.el.getBox();
55471         if(this.split){
55472             box.height += this.split.el.getHeight();
55473         }
55474         return box;
55475     },
55476     
55477     updateBox : function(box){
55478         if(this.split && !this.collapsed){
55479             box.height -= this.split.el.getHeight();
55480             this.split.el.setLeft(box.x);
55481             this.split.el.setTop(box.y+box.height);
55482             this.split.el.setWidth(box.width);
55483         }
55484         if(this.collapsed){
55485             this.updateBody(box.width, null);
55486         }
55487         Roo.LayoutRegion.prototype.updateBox.call(this, box);
55488     }
55489 });
55490
55491 Roo.SouthLayoutRegion = function(mgr, config){
55492     Roo.SplitLayoutRegion.call(this, mgr, config, "south", "s-resize");
55493     if(this.split){
55494         this.split.placement = Roo.SplitBar.BOTTOM;
55495         this.split.orientation = Roo.SplitBar.VERTICAL;
55496         this.split.el.addClass("x-layout-split-v");
55497     }
55498     var size = config.initialSize || config.height;
55499     if(typeof size != "undefined"){
55500         this.el.setHeight(size);
55501     }
55502 };
55503 Roo.extend(Roo.SouthLayoutRegion, Roo.SplitLayoutRegion, {
55504     orientation: Roo.SplitBar.VERTICAL,
55505     getBox : function(){
55506         if(this.collapsed){
55507             return this.collapsedEl.getBox();
55508         }
55509         var box = this.el.getBox();
55510         if(this.split){
55511             var sh = this.split.el.getHeight();
55512             box.height += sh;
55513             box.y -= sh;
55514         }
55515         return box;
55516     },
55517     
55518     updateBox : function(box){
55519         if(this.split && !this.collapsed){
55520             var sh = this.split.el.getHeight();
55521             box.height -= sh;
55522             box.y += sh;
55523             this.split.el.setLeft(box.x);
55524             this.split.el.setTop(box.y-sh);
55525             this.split.el.setWidth(box.width);
55526         }
55527         if(this.collapsed){
55528             this.updateBody(box.width, null);
55529         }
55530         Roo.LayoutRegion.prototype.updateBox.call(this, box);
55531     }
55532 });
55533
55534 Roo.EastLayoutRegion = function(mgr, config){
55535     Roo.SplitLayoutRegion.call(this, mgr, config, "east", "e-resize");
55536     if(this.split){
55537         this.split.placement = Roo.SplitBar.RIGHT;
55538         this.split.orientation = Roo.SplitBar.HORIZONTAL;
55539         this.split.el.addClass("x-layout-split-h");
55540     }
55541     var size = config.initialSize || config.width;
55542     if(typeof size != "undefined"){
55543         this.el.setWidth(size);
55544     }
55545 };
55546 Roo.extend(Roo.EastLayoutRegion, Roo.SplitLayoutRegion, {
55547     orientation: Roo.SplitBar.HORIZONTAL,
55548     getBox : function(){
55549         if(this.collapsed){
55550             return this.collapsedEl.getBox();
55551         }
55552         var box = this.el.getBox();
55553         if(this.split){
55554             var sw = this.split.el.getWidth();
55555             box.width += sw;
55556             box.x -= sw;
55557         }
55558         return box;
55559     },
55560
55561     updateBox : function(box){
55562         if(this.split && !this.collapsed){
55563             var sw = this.split.el.getWidth();
55564             box.width -= sw;
55565             this.split.el.setLeft(box.x);
55566             this.split.el.setTop(box.y);
55567             this.split.el.setHeight(box.height);
55568             box.x += sw;
55569         }
55570         if(this.collapsed){
55571             this.updateBody(null, box.height);
55572         }
55573         Roo.LayoutRegion.prototype.updateBox.call(this, box);
55574     }
55575 });
55576
55577 Roo.WestLayoutRegion = function(mgr, config){
55578     Roo.SplitLayoutRegion.call(this, mgr, config, "west", "w-resize");
55579     if(this.split){
55580         this.split.placement = Roo.SplitBar.LEFT;
55581         this.split.orientation = Roo.SplitBar.HORIZONTAL;
55582         this.split.el.addClass("x-layout-split-h");
55583     }
55584     var size = config.initialSize || config.width;
55585     if(typeof size != "undefined"){
55586         this.el.setWidth(size);
55587     }
55588 };
55589 Roo.extend(Roo.WestLayoutRegion, Roo.SplitLayoutRegion, {
55590     orientation: Roo.SplitBar.HORIZONTAL,
55591     getBox : function(){
55592         if(this.collapsed){
55593             return this.collapsedEl.getBox();
55594         }
55595         var box = this.el.getBox();
55596         if(this.split){
55597             box.width += this.split.el.getWidth();
55598         }
55599         return box;
55600     },
55601     
55602     updateBox : function(box){
55603         if(this.split && !this.collapsed){
55604             var sw = this.split.el.getWidth();
55605             box.width -= sw;
55606             this.split.el.setLeft(box.x+box.width);
55607             this.split.el.setTop(box.y);
55608             this.split.el.setHeight(box.height);
55609         }
55610         if(this.collapsed){
55611             this.updateBody(null, box.height);
55612         }
55613         Roo.LayoutRegion.prototype.updateBox.call(this, box);
55614     }
55615 });
55616 /*
55617  * Based on:
55618  * Ext JS Library 1.1.1
55619  * Copyright(c) 2006-2007, Ext JS, LLC.
55620  *
55621  * Originally Released Under LGPL - original licence link has changed is not relivant.
55622  *
55623  * Fork - LGPL
55624  * <script type="text/javascript">
55625  */
55626  
55627  
55628 /*
55629  * Private internal class for reading and applying state
55630  */
55631 Roo.LayoutStateManager = function(layout){
55632      // default empty state
55633      this.state = {
55634         north: {},
55635         south: {},
55636         east: {},
55637         west: {}       
55638     };
55639 };
55640
55641 Roo.LayoutStateManager.prototype = {
55642     init : function(layout, provider){
55643         this.provider = provider;
55644         var state = provider.get(layout.id+"-layout-state");
55645         if(state){
55646             var wasUpdating = layout.isUpdating();
55647             if(!wasUpdating){
55648                 layout.beginUpdate();
55649             }
55650             for(var key in state){
55651                 if(typeof state[key] != "function"){
55652                     var rstate = state[key];
55653                     var r = layout.getRegion(key);
55654                     if(r && rstate){
55655                         if(rstate.size){
55656                             r.resizeTo(rstate.size);
55657                         }
55658                         if(rstate.collapsed == true){
55659                             r.collapse(true);
55660                         }else{
55661                             r.expand(null, true);
55662                         }
55663                     }
55664                 }
55665             }
55666             if(!wasUpdating){
55667                 layout.endUpdate();
55668             }
55669             this.state = state; 
55670         }
55671         this.layout = layout;
55672         layout.on("regionresized", this.onRegionResized, this);
55673         layout.on("regioncollapsed", this.onRegionCollapsed, this);
55674         layout.on("regionexpanded", this.onRegionExpanded, this);
55675     },
55676     
55677     storeState : function(){
55678         this.provider.set(this.layout.id+"-layout-state", this.state);
55679     },
55680     
55681     onRegionResized : function(region, newSize){
55682         this.state[region.getPosition()].size = newSize;
55683         this.storeState();
55684     },
55685     
55686     onRegionCollapsed : function(region){
55687         this.state[region.getPosition()].collapsed = true;
55688         this.storeState();
55689     },
55690     
55691     onRegionExpanded : function(region){
55692         this.state[region.getPosition()].collapsed = false;
55693         this.storeState();
55694     }
55695 };/*
55696  * Based on:
55697  * Ext JS Library 1.1.1
55698  * Copyright(c) 2006-2007, Ext JS, LLC.
55699  *
55700  * Originally Released Under LGPL - original licence link has changed is not relivant.
55701  *
55702  * Fork - LGPL
55703  * <script type="text/javascript">
55704  */
55705 /**
55706  * @class Roo.ContentPanel
55707  * @extends Roo.util.Observable
55708  * @children Roo.form.Form Roo.JsonView Roo.View
55709  * @parent Roo.BorderLayout Roo.LayoutDialog builder-top
55710  * A basic ContentPanel element.
55711  * @cfg {Boolean}   fitToFrame    True for this panel to adjust its size to fit when the region resizes  (defaults to false)
55712  * @cfg {Boolean}   fitContainer   When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container  (defaults to false)
55713  * @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
55714  * @cfg {Boolean}   closable      True if the panel can be closed/removed
55715  * @cfg {Boolean}   background    True if the panel should not be activated when it is added (defaults to false)
55716  * @cfg {String|HTMLElement|Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
55717  * @cfg {Roo.Toolbar}   toolbar       A toolbar for this panel
55718  * @cfg {Boolean} autoScroll    True to scroll overflow in this panel (use with {@link #fitToFrame})
55719  * @cfg {String} title          The title for this panel
55720  * @cfg {Array} adjustments     Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
55721  * @cfg {String} url            Calls {@link #setUrl} with this value
55722  * @cfg {String} region [required]   (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
55723  * @cfg {String|Object} params  When used with {@link #url}, calls {@link #setUrl} with this value
55724  * @cfg {Boolean} loadOnce      When used with {@link #url}, calls {@link #setUrl} with this value
55725  * @cfg {String}    content        Raw content to fill content panel with (uses setContent on construction.)
55726  * @cfg {String}    style  Extra style to add to the content panel
55727  * @cfg {Roo.menu.Menu} menu  popup menu
55728
55729  * @constructor
55730  * Create a new ContentPanel.
55731  * @param {String/HTMLElement/Roo.Element} el The container element for this panel
55732  * @param {String/Object} config A string to set only the title or a config object
55733  * @param {String} content (optional) Set the HTML content for this panel
55734  * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
55735  */
55736 Roo.ContentPanel = function(el, config, content){
55737     
55738      
55739     /*
55740     if(el.autoCreate || el.xtype){ // xtype is available if this is called from factory
55741         config = el;
55742         el = Roo.id();
55743     }
55744     if (config && config.parentLayout) { 
55745         el = config.parentLayout.el.createChild(); 
55746     }
55747     */
55748     if(el.autoCreate){ // xtype is available if this is called from factory
55749         config = el;
55750         el = Roo.id();
55751     }
55752     this.el = Roo.get(el);
55753     if(!this.el && config && config.autoCreate){
55754         if(typeof config.autoCreate == "object"){
55755             if(!config.autoCreate.id){
55756                 config.autoCreate.id = config.id||el;
55757             }
55758             this.el = Roo.DomHelper.append(document.body,
55759                         config.autoCreate, true);
55760         }else{
55761             this.el = Roo.DomHelper.append(document.body,
55762                         {tag: "div", cls: "x-layout-inactive-content", id: config.id||el}, true);
55763         }
55764     }
55765     
55766     
55767     this.closable = false;
55768     this.loaded = false;
55769     this.active = false;
55770     if(typeof config == "string"){
55771         this.title = config;
55772     }else{
55773         Roo.apply(this, config);
55774     }
55775     
55776     if (this.toolbar && !this.toolbar.el && this.toolbar.xtype) {
55777         this.wrapEl = this.el.wrap();
55778         this.toolbar.container = this.el.insertSibling(false, 'before');
55779         this.toolbar = new Roo.Toolbar(this.toolbar);
55780     }
55781     
55782     // xtype created footer. - not sure if will work as we normally have to render first..
55783     if (this.footer && !this.footer.el && this.footer.xtype) {
55784         if (!this.wrapEl) {
55785             this.wrapEl = this.el.wrap();
55786         }
55787     
55788         this.footer.container = this.wrapEl.createChild();
55789          
55790         this.footer = Roo.factory(this.footer, Roo);
55791         
55792     }
55793     
55794     if(this.resizeEl){
55795         this.resizeEl = Roo.get(this.resizeEl, true);
55796     }else{
55797         this.resizeEl = this.el;
55798     }
55799     // handle view.xtype
55800     
55801  
55802     
55803     
55804     this.addEvents({
55805         /**
55806          * @event activate
55807          * Fires when this panel is activated. 
55808          * @param {Roo.ContentPanel} this
55809          */
55810         "activate" : true,
55811         /**
55812          * @event deactivate
55813          * Fires when this panel is activated. 
55814          * @param {Roo.ContentPanel} this
55815          */
55816         "deactivate" : true,
55817
55818         /**
55819          * @event resize
55820          * Fires when this panel is resized if fitToFrame is true.
55821          * @param {Roo.ContentPanel} this
55822          * @param {Number} width The width after any component adjustments
55823          * @param {Number} height The height after any component adjustments
55824          */
55825         "resize" : true,
55826         
55827          /**
55828          * @event render
55829          * Fires when this tab is created
55830          * @param {Roo.ContentPanel} this
55831          */
55832         "render" : true
55833          
55834         
55835     });
55836     
55837
55838     
55839     
55840     if(this.autoScroll){
55841         this.resizeEl.setStyle("overflow", "auto");
55842     } else {
55843         // fix randome scrolling
55844         this.el.on('scroll', function() {
55845             Roo.log('fix random scolling');
55846             this.scrollTo('top',0); 
55847         });
55848     }
55849     content = content || this.content;
55850     if(content){
55851         this.setContent(content);
55852     }
55853     if(config && config.url){
55854         this.setUrl(this.url, this.params, this.loadOnce);
55855     }
55856     
55857     
55858     
55859     Roo.ContentPanel.superclass.constructor.call(this);
55860     
55861     if (this.view && typeof(this.view.xtype) != 'undefined') {
55862         this.view.el = this.el.appendChild(document.createElement("div"));
55863         this.view = Roo.factory(this.view); 
55864         this.view.render  &&  this.view.render(false, '');  
55865     }
55866     
55867     
55868     this.fireEvent('render', this);
55869 };
55870
55871 Roo.extend(Roo.ContentPanel, Roo.util.Observable, {
55872     tabTip:'',
55873     setRegion : function(region){
55874         this.region = region;
55875         if(region){
55876            this.el.replaceClass("x-layout-inactive-content", "x-layout-active-content");
55877         }else{
55878            this.el.replaceClass("x-layout-active-content", "x-layout-inactive-content");
55879         } 
55880     },
55881     
55882     /**
55883      * Returns the toolbar for this Panel if one was configured. 
55884      * @return {Roo.Toolbar} 
55885      */
55886     getToolbar : function(){
55887         return this.toolbar;
55888     },
55889     
55890     setActiveState : function(active){
55891         this.active = active;
55892         if(!active){
55893             this.fireEvent("deactivate", this);
55894         }else{
55895             this.fireEvent("activate", this);
55896         }
55897     },
55898     /**
55899      * Updates this panel's element
55900      * @param {String} content The new content
55901      * @param {Boolean} loadScripts (optional) true to look for and process scripts
55902     */
55903     setContent : function(content, loadScripts){
55904         this.el.update(content, loadScripts);
55905     },
55906
55907     ignoreResize : function(w, h){
55908         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
55909             return true;
55910         }else{
55911             this.lastSize = {width: w, height: h};
55912             return false;
55913         }
55914     },
55915     /**
55916      * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
55917      * @return {Roo.UpdateManager} The UpdateManager
55918      */
55919     getUpdateManager : function(){
55920         return this.el.getUpdateManager();
55921     },
55922      /**
55923      * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
55924      * @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:
55925 <pre><code>
55926 panel.load({
55927     url: "your-url.php",
55928     params: {param1: "foo", param2: "bar"}, // or a URL encoded string
55929     callback: yourFunction,
55930     scope: yourObject, //(optional scope)
55931     discardUrl: false,
55932     nocache: false,
55933     text: "Loading...",
55934     timeout: 30,
55935     scripts: false
55936 });
55937 </code></pre>
55938      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
55939      * 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.
55940      * @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}
55941      * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
55942      * @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.
55943      * @return {Roo.ContentPanel} this
55944      */
55945     load : function(){
55946         var um = this.el.getUpdateManager();
55947         um.update.apply(um, arguments);
55948         return this;
55949     },
55950
55951
55952     /**
55953      * 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.
55954      * @param {String/Function} url The URL to load the content from or a function to call to get the URL
55955      * @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)
55956      * @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)
55957      * @return {Roo.UpdateManager} The UpdateManager
55958      */
55959     setUrl : function(url, params, loadOnce){
55960         if(this.refreshDelegate){
55961             this.removeListener("activate", this.refreshDelegate);
55962         }
55963         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
55964         this.on("activate", this.refreshDelegate);
55965         return this.el.getUpdateManager();
55966     },
55967     
55968     _handleRefresh : function(url, params, loadOnce){
55969         if(!loadOnce || !this.loaded){
55970             var updater = this.el.getUpdateManager();
55971             updater.update(url, params, this._setLoaded.createDelegate(this));
55972         }
55973     },
55974     
55975     _setLoaded : function(){
55976         this.loaded = true;
55977     }, 
55978     
55979     /**
55980      * Returns this panel's id
55981      * @return {String} 
55982      */
55983     getId : function(){
55984         return this.el.id;
55985     },
55986     
55987     /** 
55988      * Returns this panel's element - used by regiosn to add.
55989      * @return {Roo.Element} 
55990      */
55991     getEl : function(){
55992         return this.wrapEl || this.el;
55993     },
55994     
55995     adjustForComponents : function(width, height)
55996     {
55997         //Roo.log('adjustForComponents ');
55998         if(this.resizeEl != this.el){
55999             width -= this.el.getFrameWidth('lr');
56000             height -= this.el.getFrameWidth('tb');
56001         }
56002         if(this.toolbar){
56003             var te = this.toolbar.getEl();
56004             height -= te.getHeight();
56005             te.setWidth(width);
56006         }
56007         if(this.footer){
56008             var te = this.footer.getEl();
56009             //Roo.log("footer:" + te.getHeight());
56010             
56011             height -= te.getHeight();
56012             te.setWidth(width);
56013         }
56014         
56015         
56016         if(this.adjustments){
56017             width += this.adjustments[0];
56018             height += this.adjustments[1];
56019         }
56020         return {"width": width, "height": height};
56021     },
56022     
56023     setSize : function(width, height){
56024         if(this.fitToFrame && !this.ignoreResize(width, height)){
56025             if(this.fitContainer && this.resizeEl != this.el){
56026                 this.el.setSize(width, height);
56027             }
56028             var size = this.adjustForComponents(width, height);
56029             this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
56030             this.fireEvent('resize', this, size.width, size.height);
56031         }
56032     },
56033     
56034     /**
56035      * Returns this panel's title
56036      * @return {String} 
56037      */
56038     getTitle : function(){
56039         return this.title;
56040     },
56041     
56042     /**
56043      * Set this panel's title
56044      * @param {String} title
56045      */
56046     setTitle : function(title){
56047         this.title = title;
56048         if(this.region){
56049             this.region.updatePanelTitle(this, title);
56050         }
56051     },
56052     
56053     /**
56054      * Returns true is this panel was configured to be closable
56055      * @return {Boolean} 
56056      */
56057     isClosable : function(){
56058         return this.closable;
56059     },
56060     
56061     beforeSlide : function(){
56062         this.el.clip();
56063         this.resizeEl.clip();
56064     },
56065     
56066     afterSlide : function(){
56067         this.el.unclip();
56068         this.resizeEl.unclip();
56069     },
56070     
56071     /**
56072      *   Force a content refresh from the URL specified in the {@link #setUrl} method.
56073      *   Will fail silently if the {@link #setUrl} method has not been called.
56074      *   This does not activate the panel, just updates its content.
56075      */
56076     refresh : function(){
56077         if(this.refreshDelegate){
56078            this.loaded = false;
56079            this.refreshDelegate();
56080         }
56081     },
56082     
56083     /**
56084      * Destroys this panel
56085      */
56086     destroy : function(){
56087         this.el.removeAllListeners();
56088         var tempEl = document.createElement("span");
56089         tempEl.appendChild(this.el.dom);
56090         tempEl.innerHTML = "";
56091         this.el.remove();
56092         this.el = null;
56093     },
56094     
56095     /**
56096      * form - if the content panel contains a form - this is a reference to it.
56097      * @type {Roo.form.Form}
56098      */
56099     form : false,
56100     /**
56101      * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
56102      *    This contains a reference to it.
56103      * @type {Roo.View}
56104      */
56105     view : false,
56106     
56107       /**
56108      * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
56109      * <pre><code>
56110
56111 layout.addxtype({
56112        xtype : 'Form',
56113        items: [ .... ]
56114    }
56115 );
56116
56117 </code></pre>
56118      * @param {Object} cfg Xtype definition of item to add.
56119      */
56120     
56121     addxtype : function(cfg) {
56122         // add form..
56123         if (cfg.xtype.match(/^Form$/)) {
56124             
56125             var el;
56126             //if (this.footer) {
56127             //    el = this.footer.container.insertSibling(false, 'before');
56128             //} else {
56129                 el = this.el.createChild();
56130             //}
56131
56132             this.form = new  Roo.form.Form(cfg);
56133             
56134             
56135             if ( this.form.allItems.length) {
56136                 this.form.render(el.dom);
56137             }
56138             return this.form;
56139         }
56140         // should only have one of theses..
56141         if ([ 'View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
56142             // views.. should not be just added - used named prop 'view''
56143             
56144             cfg.el = this.el.appendChild(document.createElement("div"));
56145             // factory?
56146             
56147             var ret = new Roo.factory(cfg);
56148              
56149              ret.render && ret.render(false, ''); // render blank..
56150             this.view = ret;
56151             return ret;
56152         }
56153         return false;
56154     }
56155 });
56156
56157 /**
56158  * @class Roo.GridPanel
56159  * @extends Roo.ContentPanel
56160  * @constructor
56161  * Create a new GridPanel.
56162  * @param {Roo.grid.Grid} grid The grid for this panel
56163  * @param {String/Object} config A string to set only the panel's title, or a config object
56164  */
56165 Roo.GridPanel = function(grid, config){
56166     
56167   
56168     this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
56169         {tag: "div", cls: "x-layout-grid-wrapper x-layout-inactive-content"}, true);
56170         
56171     this.wrapper.dom.appendChild(grid.getGridEl().dom);
56172     
56173     Roo.GridPanel.superclass.constructor.call(this, this.wrapper, config);
56174     
56175     if(this.toolbar){
56176         this.toolbar.el.insertBefore(this.wrapper.dom.firstChild);
56177     }
56178     // xtype created footer. - not sure if will work as we normally have to render first..
56179     if (this.footer && !this.footer.el && this.footer.xtype) {
56180         
56181         this.footer.container = this.grid.getView().getFooterPanel(true);
56182         this.footer.dataSource = this.grid.dataSource;
56183         this.footer = Roo.factory(this.footer, Roo);
56184         
56185     }
56186     
56187     grid.monitorWindowResize = false; // turn off autosizing
56188     grid.autoHeight = false;
56189     grid.autoWidth = false;
56190     this.grid = grid;
56191     this.grid.getGridEl().replaceClass("x-layout-inactive-content", "x-layout-component-panel");
56192 };
56193
56194 Roo.extend(Roo.GridPanel, Roo.ContentPanel, {
56195     getId : function(){
56196         return this.grid.id;
56197     },
56198     
56199     /**
56200      * Returns the grid for this panel
56201      * @return {Roo.grid.Grid} 
56202      */
56203     getGrid : function(){
56204         return this.grid;    
56205     },
56206     
56207     setSize : function(width, height){
56208         if(!this.ignoreResize(width, height)){
56209             var grid = this.grid;
56210             var size = this.adjustForComponents(width, height);
56211             grid.getGridEl().setSize(size.width, size.height);
56212             grid.autoSize();
56213         }
56214     },
56215     
56216     beforeSlide : function(){
56217         this.grid.getView().scroller.clip();
56218     },
56219     
56220     afterSlide : function(){
56221         this.grid.getView().scroller.unclip();
56222     },
56223     
56224     destroy : function(){
56225         this.grid.destroy();
56226         delete this.grid;
56227         Roo.GridPanel.superclass.destroy.call(this); 
56228     }
56229 });
56230
56231
56232 /**
56233  * @class Roo.NestedLayoutPanel
56234  * @extends Roo.ContentPanel
56235  * @constructor
56236  * Create a new NestedLayoutPanel.
56237  * 
56238  * 
56239  * @param {Roo.BorderLayout} layout [required] The layout for this panel
56240  * @param {String/Object} config A string to set only the title or a config object
56241  */
56242 Roo.NestedLayoutPanel = function(layout, config)
56243 {
56244     // construct with only one argument..
56245     /* FIXME - implement nicer consturctors
56246     if (layout.layout) {
56247         config = layout;
56248         layout = config.layout;
56249         delete config.layout;
56250     }
56251     if (layout.xtype && !layout.getEl) {
56252         // then layout needs constructing..
56253         layout = Roo.factory(layout, Roo);
56254     }
56255     */
56256     
56257     
56258     Roo.NestedLayoutPanel.superclass.constructor.call(this, layout.getEl(), config);
56259     
56260     layout.monitorWindowResize = false; // turn off autosizing
56261     this.layout = layout;
56262     this.layout.getEl().addClass("x-layout-nested-layout");
56263     
56264     
56265     
56266     
56267 };
56268
56269 Roo.extend(Roo.NestedLayoutPanel, Roo.ContentPanel, {
56270
56271     setSize : function(width, height){
56272         if(!this.ignoreResize(width, height)){
56273             var size = this.adjustForComponents(width, height);
56274             var el = this.layout.getEl();
56275             el.setSize(size.width, size.height);
56276             var touch = el.dom.offsetWidth;
56277             this.layout.layout();
56278             // ie requires a double layout on the first pass
56279             if(Roo.isIE && !this.initialized){
56280                 this.initialized = true;
56281                 this.layout.layout();
56282             }
56283         }
56284     },
56285     
56286     // activate all subpanels if not currently active..
56287     
56288     setActiveState : function(active){
56289         this.active = active;
56290         if(!active){
56291             this.fireEvent("deactivate", this);
56292             return;
56293         }
56294         
56295         this.fireEvent("activate", this);
56296         // not sure if this should happen before or after..
56297         if (!this.layout) {
56298             return; // should not happen..
56299         }
56300         var reg = false;
56301         for (var r in this.layout.regions) {
56302             reg = this.layout.getRegion(r);
56303             if (reg.getActivePanel()) {
56304                 //reg.showPanel(reg.getActivePanel()); // force it to activate.. 
56305                 reg.setActivePanel(reg.getActivePanel());
56306                 continue;
56307             }
56308             if (!reg.panels.length) {
56309                 continue;
56310             }
56311             reg.showPanel(reg.getPanel(0));
56312         }
56313         
56314         
56315         
56316         
56317     },
56318     
56319     /**
56320      * Returns the nested BorderLayout for this panel
56321      * @return {Roo.BorderLayout} 
56322      */
56323     getLayout : function(){
56324         return this.layout;
56325     },
56326     
56327      /**
56328      * Adds a xtype elements to the layout of the nested panel
56329      * <pre><code>
56330
56331 panel.addxtype({
56332        xtype : 'ContentPanel',
56333        region: 'west',
56334        items: [ .... ]
56335    }
56336 );
56337
56338 panel.addxtype({
56339         xtype : 'NestedLayoutPanel',
56340         region: 'west',
56341         layout: {
56342            center: { },
56343            west: { }   
56344         },
56345         items : [ ... list of content panels or nested layout panels.. ]
56346    }
56347 );
56348 </code></pre>
56349      * @param {Object} cfg Xtype definition of item to add.
56350      */
56351     addxtype : function(cfg) {
56352         return this.layout.addxtype(cfg);
56353     
56354     }
56355 });
56356
56357 Roo.ScrollPanel = function(el, config, content){
56358     config = config || {};
56359     config.fitToFrame = true;
56360     Roo.ScrollPanel.superclass.constructor.call(this, el, config, content);
56361     
56362     this.el.dom.style.overflow = "hidden";
56363     var wrap = this.el.wrap({cls: "x-scroller x-layout-inactive-content"});
56364     this.el.removeClass("x-layout-inactive-content");
56365     this.el.on("mousewheel", this.onWheel, this);
56366
56367     var up = wrap.createChild({cls: "x-scroller-up", html: "&#160;"}, this.el.dom);
56368     var down = wrap.createChild({cls: "x-scroller-down", html: "&#160;"});
56369     up.unselectable(); down.unselectable();
56370     up.on("click", this.scrollUp, this);
56371     down.on("click", this.scrollDown, this);
56372     up.addClassOnOver("x-scroller-btn-over");
56373     down.addClassOnOver("x-scroller-btn-over");
56374     up.addClassOnClick("x-scroller-btn-click");
56375     down.addClassOnClick("x-scroller-btn-click");
56376     this.adjustments = [0, -(up.getHeight() + down.getHeight())];
56377
56378     this.resizeEl = this.el;
56379     this.el = wrap; this.up = up; this.down = down;
56380 };
56381
56382 Roo.extend(Roo.ScrollPanel, Roo.ContentPanel, {
56383     increment : 100,
56384     wheelIncrement : 5,
56385     scrollUp : function(){
56386         this.resizeEl.scroll("up", this.increment, {callback: this.afterScroll, scope: this});
56387     },
56388
56389     scrollDown : function(){
56390         this.resizeEl.scroll("down", this.increment, {callback: this.afterScroll, scope: this});
56391     },
56392
56393     afterScroll : function(){
56394         var el = this.resizeEl;
56395         var t = el.dom.scrollTop, h = el.dom.scrollHeight, ch = el.dom.clientHeight;
56396         this.up[t == 0 ? "addClass" : "removeClass"]("x-scroller-btn-disabled");
56397         this.down[h - t <= ch ? "addClass" : "removeClass"]("x-scroller-btn-disabled");
56398     },
56399
56400     setSize : function(){
56401         Roo.ScrollPanel.superclass.setSize.apply(this, arguments);
56402         this.afterScroll();
56403     },
56404
56405     onWheel : function(e){
56406         var d = e.getWheelDelta();
56407         this.resizeEl.dom.scrollTop -= (d*this.wheelIncrement);
56408         this.afterScroll();
56409         e.stopEvent();
56410     },
56411
56412     setContent : function(content, loadScripts){
56413         this.resizeEl.update(content, loadScripts);
56414     }
56415
56416 });
56417
56418
56419
56420 /**
56421  * @class Roo.TreePanel
56422  * @extends Roo.ContentPanel
56423  * Treepanel component
56424  * 
56425  * @constructor
56426  * Create a new TreePanel. - defaults to fit/scoll contents.
56427  * @param {String/Object} config A string to set only the panel's title, or a config object
56428  */
56429 Roo.TreePanel = function(config){
56430     var el = config.el;
56431     var tree = config.tree;
56432     delete config.tree; 
56433     delete config.el; // hopefull!
56434     
56435     // wrapper for IE7 strict & safari scroll issue
56436     
56437     var treeEl = el.createChild();
56438     config.resizeEl = treeEl;
56439     
56440     
56441     
56442     Roo.TreePanel.superclass.constructor.call(this, el, config);
56443  
56444  
56445     this.tree = new Roo.tree.TreePanel(treeEl , tree);
56446     //console.log(tree);
56447     this.on('activate', function()
56448     {
56449         if (this.tree.rendered) {
56450             return;
56451         }
56452         //console.log('render tree');
56453         this.tree.render();
56454     });
56455     // this should not be needed.. - it's actually the 'el' that resizes?
56456     // actuall it breaks the containerScroll - dragging nodes auto scroll at top
56457     
56458     //this.on('resize',  function (cp, w, h) {
56459     //        this.tree.innerCt.setWidth(w);
56460     //        this.tree.innerCt.setHeight(h);
56461     //        //this.tree.innerCt.setStyle('overflow-y', 'auto');
56462     //});
56463
56464         
56465     
56466 };
56467
56468 Roo.extend(Roo.TreePanel, Roo.ContentPanel, {   
56469     fitToFrame : true,
56470     autoScroll : true,
56471     /*
56472      * @cfg {Roo.tree.TreePanel} tree [required] The tree TreePanel, with config etc.
56473      */
56474     tree : false
56475
56476 });
56477
56478
56479
56480
56481
56482
56483
56484
56485
56486
56487
56488 /*
56489  * Based on:
56490  * Ext JS Library 1.1.1
56491  * Copyright(c) 2006-2007, Ext JS, LLC.
56492  *
56493  * Originally Released Under LGPL - original licence link has changed is not relivant.
56494  *
56495  * Fork - LGPL
56496  * <script type="text/javascript">
56497  */
56498  
56499
56500 /**
56501  * @class Roo.ReaderLayout
56502  * @extends Roo.BorderLayout
56503  * This is a pre-built layout that represents a classic, 5-pane application.  It consists of a header, a primary
56504  * center region containing two nested regions (a top one for a list view and one for item preview below),
56505  * and regions on either side that can be used for navigation, application commands, informational displays, etc.
56506  * The setup and configuration work exactly the same as it does for a {@link Roo.BorderLayout} - this class simply
56507  * expedites the setup of the overall layout and regions for this common application style.
56508  * Example:
56509  <pre><code>
56510 var reader = new Roo.ReaderLayout();
56511 var CP = Roo.ContentPanel;  // shortcut for adding
56512
56513 reader.beginUpdate();
56514 reader.add("north", new CP("north", "North"));
56515 reader.add("west", new CP("west", {title: "West"}));
56516 reader.add("east", new CP("east", {title: "East"}));
56517
56518 reader.regions.listView.add(new CP("listView", "List"));
56519 reader.regions.preview.add(new CP("preview", "Preview"));
56520 reader.endUpdate();
56521 </code></pre>
56522 * @constructor
56523 * Create a new ReaderLayout
56524 * @param {Object} config Configuration options
56525 * @param {String/HTMLElement/Element} container (optional) The container this layout is bound to (defaults to
56526 * document.body if omitted)
56527 */
56528 Roo.ReaderLayout = function(config, renderTo){
56529     var c = config || {size:{}};
56530     Roo.ReaderLayout.superclass.constructor.call(this, renderTo || document.body, {
56531         north: c.north !== false ? Roo.apply({
56532             split:false,
56533             initialSize: 32,
56534             titlebar: false
56535         }, c.north) : false,
56536         west: c.west !== false ? Roo.apply({
56537             split:true,
56538             initialSize: 200,
56539             minSize: 175,
56540             maxSize: 400,
56541             titlebar: true,
56542             collapsible: true,
56543             animate: true,
56544             margins:{left:5,right:0,bottom:5,top:5},
56545             cmargins:{left:5,right:5,bottom:5,top:5}
56546         }, c.west) : false,
56547         east: c.east !== false ? Roo.apply({
56548             split:true,
56549             initialSize: 200,
56550             minSize: 175,
56551             maxSize: 400,
56552             titlebar: true,
56553             collapsible: true,
56554             animate: true,
56555             margins:{left:0,right:5,bottom:5,top:5},
56556             cmargins:{left:5,right:5,bottom:5,top:5}
56557         }, c.east) : false,
56558         center: Roo.apply({
56559             tabPosition: 'top',
56560             autoScroll:false,
56561             closeOnTab: true,
56562             titlebar:false,
56563             margins:{left:c.west!==false ? 0 : 5,right:c.east!==false ? 0 : 5,bottom:5,top:2}
56564         }, c.center)
56565     });
56566
56567     this.el.addClass('x-reader');
56568
56569     this.beginUpdate();
56570
56571     var inner = new Roo.BorderLayout(Roo.get(document.body).createChild(), {
56572         south: c.preview !== false ? Roo.apply({
56573             split:true,
56574             initialSize: 200,
56575             minSize: 100,
56576             autoScroll:true,
56577             collapsible:true,
56578             titlebar: true,
56579             cmargins:{top:5,left:0, right:0, bottom:0}
56580         }, c.preview) : false,
56581         center: Roo.apply({
56582             autoScroll:false,
56583             titlebar:false,
56584             minHeight:200
56585         }, c.listView)
56586     });
56587     this.add('center', new Roo.NestedLayoutPanel(inner,
56588             Roo.apply({title: c.mainTitle || '',tabTip:''},c.innerPanelCfg)));
56589
56590     this.endUpdate();
56591
56592     this.regions.preview = inner.getRegion('south');
56593     this.regions.listView = inner.getRegion('center');
56594 };
56595
56596 Roo.extend(Roo.ReaderLayout, Roo.BorderLayout);/*
56597  * Based on:
56598  * Ext JS Library 1.1.1
56599  * Copyright(c) 2006-2007, Ext JS, LLC.
56600  *
56601  * Originally Released Under LGPL - original licence link has changed is not relivant.
56602  *
56603  * Fork - LGPL
56604  * <script type="text/javascript">
56605  */
56606  
56607 /**
56608  * @class Roo.grid.Grid
56609  * @extends Roo.util.Observable
56610  * This class represents the primary interface of a component based grid control.
56611  * <br><br>Usage:<pre><code>
56612  var grid = new Roo.grid.Grid("my-container-id", {
56613      ds: myDataStore,
56614      cm: myColModel,
56615      selModel: mySelectionModel,
56616      autoSizeColumns: true,
56617      monitorWindowResize: false,
56618      trackMouseOver: true
56619  });
56620  // set any options
56621  grid.render();
56622  * </code></pre>
56623  * <b>Common Problems:</b><br/>
56624  * - Grid does not resize properly when going smaller: Setting overflow hidden on the container
56625  * element will correct this<br/>
56626  * - If you get el.style[camel]= NaNpx or -2px or something related, be certain you have given your container element
56627  * dimensions. The grid adapts to your container's size, if your container has no size defined then the results
56628  * are unpredictable.<br/>
56629  * - Do not render the grid into an element with display:none. Try using visibility:hidden. Otherwise there is no way for the
56630  * grid to calculate dimensions/offsets.<br/>
56631   * @constructor
56632  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
56633  * The container MUST have some type of size defined for the grid to fill. The container will be
56634  * automatically set to position relative if it isn't already.
56635  * @param {Object} config A config object that sets properties on this grid.
56636  */
56637 Roo.grid.Grid = function(container, config){
56638         // initialize the container
56639         this.container = Roo.get(container);
56640         this.container.update("");
56641         this.container.setStyle("overflow", "hidden");
56642     this.container.addClass('x-grid-container');
56643
56644     this.id = this.container.id;
56645
56646     Roo.apply(this, config);
56647     // check and correct shorthanded configs
56648     if(this.ds){
56649         this.dataSource = this.ds;
56650         delete this.ds;
56651     }
56652     if(this.cm){
56653         this.colModel = this.cm;
56654         delete this.cm;
56655     }
56656     if(this.sm){
56657         this.selModel = this.sm;
56658         delete this.sm;
56659     }
56660
56661     if (this.selModel) {
56662         this.selModel = Roo.factory(this.selModel, Roo.grid);
56663         this.sm = this.selModel;
56664         this.sm.xmodule = this.xmodule || false;
56665     }
56666     if (typeof(this.colModel.config) == 'undefined') {
56667         this.colModel = new Roo.grid.ColumnModel(this.colModel);
56668         this.cm = this.colModel;
56669         this.cm.xmodule = this.xmodule || false;
56670     }
56671     if (this.dataSource) {
56672         this.dataSource= Roo.factory(this.dataSource, Roo.data);
56673         this.ds = this.dataSource;
56674         this.ds.xmodule = this.xmodule || false;
56675          
56676     }
56677     
56678     
56679     
56680     if(this.width){
56681         this.container.setWidth(this.width);
56682     }
56683
56684     if(this.height){
56685         this.container.setHeight(this.height);
56686     }
56687     /** @private */
56688         this.addEvents({
56689         // raw events
56690         /**
56691          * @event click
56692          * The raw click event for the entire grid.
56693          * @param {Roo.EventObject} e
56694          */
56695         "click" : true,
56696         /**
56697          * @event dblclick
56698          * The raw dblclick event for the entire grid.
56699          * @param {Roo.EventObject} e
56700          */
56701         "dblclick" : true,
56702         /**
56703          * @event contextmenu
56704          * The raw contextmenu event for the entire grid.
56705          * @param {Roo.EventObject} e
56706          */
56707         "contextmenu" : true,
56708         /**
56709          * @event mousedown
56710          * The raw mousedown event for the entire grid.
56711          * @param {Roo.EventObject} e
56712          */
56713         "mousedown" : true,
56714         /**
56715          * @event mouseup
56716          * The raw mouseup event for the entire grid.
56717          * @param {Roo.EventObject} e
56718          */
56719         "mouseup" : true,
56720         /**
56721          * @event mouseover
56722          * The raw mouseover event for the entire grid.
56723          * @param {Roo.EventObject} e
56724          */
56725         "mouseover" : true,
56726         /**
56727          * @event mouseout
56728          * The raw mouseout event for the entire grid.
56729          * @param {Roo.EventObject} e
56730          */
56731         "mouseout" : true,
56732         /**
56733          * @event keypress
56734          * The raw keypress event for the entire grid.
56735          * @param {Roo.EventObject} e
56736          */
56737         "keypress" : true,
56738         /**
56739          * @event keydown
56740          * The raw keydown event for the entire grid.
56741          * @param {Roo.EventObject} e
56742          */
56743         "keydown" : true,
56744
56745         // custom events
56746
56747         /**
56748          * @event cellclick
56749          * Fires when a cell is clicked
56750          * @param {Grid} this
56751          * @param {Number} rowIndex
56752          * @param {Number} columnIndex
56753          * @param {Roo.EventObject} e
56754          */
56755         "cellclick" : true,
56756         /**
56757          * @event celldblclick
56758          * Fires when a cell is double clicked
56759          * @param {Grid} this
56760          * @param {Number} rowIndex
56761          * @param {Number} columnIndex
56762          * @param {Roo.EventObject} e
56763          */
56764         "celldblclick" : true,
56765         /**
56766          * @event rowclick
56767          * Fires when a row is clicked
56768          * @param {Grid} this
56769          * @param {Number} rowIndex
56770          * @param {Roo.EventObject} e
56771          */
56772         "rowclick" : true,
56773         /**
56774          * @event rowdblclick
56775          * Fires when a row is double clicked
56776          * @param {Grid} this
56777          * @param {Number} rowIndex
56778          * @param {Roo.EventObject} e
56779          */
56780         "rowdblclick" : true,
56781         /**
56782          * @event headerclick
56783          * Fires when a header is clicked
56784          * @param {Grid} this
56785          * @param {Number} columnIndex
56786          * @param {Roo.EventObject} e
56787          */
56788         "headerclick" : true,
56789         /**
56790          * @event headerdblclick
56791          * Fires when a header cell is double clicked
56792          * @param {Grid} this
56793          * @param {Number} columnIndex
56794          * @param {Roo.EventObject} e
56795          */
56796         "headerdblclick" : true,
56797         /**
56798          * @event rowcontextmenu
56799          * Fires when a row is right clicked
56800          * @param {Grid} this
56801          * @param {Number} rowIndex
56802          * @param {Roo.EventObject} e
56803          */
56804         "rowcontextmenu" : true,
56805         /**
56806          * @event cellcontextmenu
56807          * Fires when a cell is right clicked
56808          * @param {Grid} this
56809          * @param {Number} rowIndex
56810          * @param {Number} cellIndex
56811          * @param {Roo.EventObject} e
56812          */
56813          "cellcontextmenu" : true,
56814         /**
56815          * @event headercontextmenu
56816          * Fires when a header is right clicked
56817          * @param {Grid} this
56818          * @param {Number} columnIndex
56819          * @param {Roo.EventObject} e
56820          */
56821         "headercontextmenu" : true,
56822         /**
56823          * @event bodyscroll
56824          * Fires when the body element is scrolled
56825          * @param {Number} scrollLeft
56826          * @param {Number} scrollTop
56827          */
56828         "bodyscroll" : true,
56829         /**
56830          * @event columnresize
56831          * Fires when the user resizes a column
56832          * @param {Number} columnIndex
56833          * @param {Number} newSize
56834          */
56835         "columnresize" : true,
56836         /**
56837          * @event columnmove
56838          * Fires when the user moves a column
56839          * @param {Number} oldIndex
56840          * @param {Number} newIndex
56841          */
56842         "columnmove" : true,
56843         /**
56844          * @event startdrag
56845          * Fires when row(s) start being dragged
56846          * @param {Grid} this
56847          * @param {Roo.GridDD} dd The drag drop object
56848          * @param {event} e The raw browser event
56849          */
56850         "startdrag" : true,
56851         /**
56852          * @event enddrag
56853          * Fires when a drag operation is complete
56854          * @param {Grid} this
56855          * @param {Roo.GridDD} dd The drag drop object
56856          * @param {event} e The raw browser event
56857          */
56858         "enddrag" : true,
56859         /**
56860          * @event dragdrop
56861          * Fires when dragged row(s) are dropped on a valid DD target
56862          * @param {Grid} this
56863          * @param {Roo.GridDD} dd The drag drop object
56864          * @param {String} targetId The target drag drop object
56865          * @param {event} e The raw browser event
56866          */
56867         "dragdrop" : true,
56868         /**
56869          * @event dragover
56870          * Fires while row(s) are being dragged. "targetId" is the id of the Yahoo.util.DD object the selected rows are being dragged over.
56871          * @param {Grid} this
56872          * @param {Roo.GridDD} dd The drag drop object
56873          * @param {String} targetId The target drag drop object
56874          * @param {event} e The raw browser event
56875          */
56876         "dragover" : true,
56877         /**
56878          * @event dragenter
56879          *  Fires when the dragged row(s) first cross another DD target while being dragged
56880          * @param {Grid} this
56881          * @param {Roo.GridDD} dd The drag drop object
56882          * @param {String} targetId The target drag drop object
56883          * @param {event} e The raw browser event
56884          */
56885         "dragenter" : true,
56886         /**
56887          * @event dragout
56888          * Fires when the dragged row(s) leave another DD target while being dragged
56889          * @param {Grid} this
56890          * @param {Roo.GridDD} dd The drag drop object
56891          * @param {String} targetId The target drag drop object
56892          * @param {event} e The raw browser event
56893          */
56894         "dragout" : true,
56895         /**
56896          * @event rowclass
56897          * Fires when a row is rendered, so you can change add a style to it.
56898          * @param {GridView} gridview   The grid view
56899          * @param {Object} rowcfg   contains record  rowIndex and rowClass - set rowClass to add a style.
56900          */
56901         'rowclass' : true,
56902
56903         /**
56904          * @event render
56905          * Fires when the grid is rendered
56906          * @param {Grid} grid
56907          */
56908         'render' : true
56909     });
56910
56911     Roo.grid.Grid.superclass.constructor.call(this);
56912 };
56913 Roo.extend(Roo.grid.Grid, Roo.util.Observable, {
56914     
56915     /**
56916          * @cfg {Roo.grid.AbstractSelectionModel} sm The selection Model (default = Roo.grid.RowSelectionModel)
56917          */
56918         /**
56919          * @cfg {Roo.grid.GridView} view  The view that renders the grid (default = Roo.grid.GridView)
56920          */
56921         /**
56922          * @cfg {Roo.grid.ColumnModel} cm[] The columns of the grid
56923          */
56924         /**
56925          * @cfg {Roo.grid.Store} ds The data store for the grid
56926          */
56927         /**
56928          * @cfg {Roo.Toolbar} toolbar a toolbar for buttons etc.
56929          */
56930         /**
56931      * @cfg {String} ddGroup - drag drop group.
56932      */
56933       /**
56934      * @cfg {String} dragGroup - drag group (?? not sure if needed.)
56935      */
56936
56937     /**
56938      * @cfg {Number} minColumnWidth The minimum width a column can be resized to. Default is 25.
56939      */
56940     minColumnWidth : 25,
56941
56942     /**
56943      * @cfg {Boolean} autoSizeColumns True to automatically resize the columns to fit their content
56944      * <b>on initial render.</b> It is more efficient to explicitly size the columns
56945      * through the ColumnModel's {@link Roo.grid.ColumnModel#width} config option.  Default is false.
56946      */
56947     autoSizeColumns : false,
56948
56949     /**
56950      * @cfg {Boolean} autoSizeHeaders True to measure headers with column data when auto sizing columns. Default is true.
56951      */
56952     autoSizeHeaders : true,
56953
56954     /**
56955      * @cfg {Boolean} monitorWindowResize True to autoSize the grid when the window resizes. Default is true.
56956      */
56957     monitorWindowResize : true,
56958
56959     /**
56960      * @cfg {Boolean} maxRowsToMeasure If autoSizeColumns is on, maxRowsToMeasure can be used to limit the number of
56961      * rows measured to get a columns size. Default is 0 (all rows).
56962      */
56963     maxRowsToMeasure : 0,
56964
56965     /**
56966      * @cfg {Boolean} trackMouseOver True to highlight rows when the mouse is over. Default is true.
56967      */
56968     trackMouseOver : true,
56969
56970     /**
56971     * @cfg {Boolean} enableDrag  True to enable drag of rows. Default is false. (double check if this is needed?)
56972     */
56973       /**
56974     * @cfg {Boolean} enableDrop  True to enable drop of elements. Default is false. (double check if this is needed?)
56975     */
56976     
56977     /**
56978     * @cfg {Boolean} enableDragDrop True to enable drag and drop of rows. Default is false.
56979     */
56980     enableDragDrop : false,
56981     
56982     /**
56983     * @cfg {Boolean} enableColumnMove True to enable drag and drop reorder of columns. Default is true.
56984     */
56985     enableColumnMove : true,
56986     
56987     /**
56988     * @cfg {Boolean} enableColumnHide True to enable hiding of columns with the header context menu. Default is true.
56989     */
56990     enableColumnHide : true,
56991     
56992     /**
56993     * @cfg {Boolean} enableRowHeightSync True to manually sync row heights across locked and not locked rows. Default is false.
56994     */
56995     enableRowHeightSync : false,
56996     
56997     /**
56998     * @cfg {Boolean} stripeRows True to stripe the rows.  Default is true.
56999     */
57000     stripeRows : true,
57001     
57002     /**
57003     * @cfg {Boolean} autoHeight True to fit the height of the grid container to the height of the data. Default is false.
57004     */
57005     autoHeight : false,
57006
57007     /**
57008      * @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.
57009      */
57010     autoExpandColumn : false,
57011
57012     /**
57013     * @cfg {Number} autoExpandMin The minimum width the autoExpandColumn can have (if enabled).
57014     * Default is 50.
57015     */
57016     autoExpandMin : 50,
57017
57018     /**
57019     * @cfg {Number} autoExpandMax The maximum width the autoExpandColumn can have (if enabled). Default is 1000.
57020     */
57021     autoExpandMax : 1000,
57022
57023     /**
57024     * @cfg {Object} view The {@link Roo.grid.GridView} used by the grid. This can be set before a call to render().
57025     */
57026     view : null,
57027
57028     /**
57029     * @cfg {Object} loadMask An {@link Roo.LoadMask} config or true to mask the grid while loading. Default is false.
57030     */
57031     loadMask : false,
57032     /**
57033     * @cfg {Roo.dd.DropTarget} dropTarget An {@link Roo.dd.DropTarget} config
57034     */
57035     dropTarget: false,
57036     
57037    
57038     
57039     // private
57040     rendered : false,
57041
57042     /**
57043     * @cfg {Boolean} autoWidth True to set the grid's width to the default total width of the grid's columns instead
57044     * of a fixed width. Default is false.
57045     */
57046     /**
57047     * @cfg {Number} maxHeight Sets the maximum height of the grid - ignored if autoHeight is not on.
57048     */
57049     
57050     
57051     /**
57052     * @cfg {String} ddText Configures the text is the drag proxy (defaults to "%0 selected row(s)").
57053     * %0 is replaced with the number of selected rows.
57054     */
57055     ddText : "{0} selected row{1}",
57056     
57057     
57058     /**
57059      * Called once after all setup has been completed and the grid is ready to be rendered.
57060      * @return {Roo.grid.Grid} this
57061      */
57062     render : function()
57063     {
57064         var c = this.container;
57065         // try to detect autoHeight/width mode
57066         if((!c.dom.offsetHeight || c.dom.offsetHeight < 20) || c.getStyle("height") == "auto"){
57067             this.autoHeight = true;
57068         }
57069         var view = this.getView();
57070         view.init(this);
57071
57072         c.on("click", this.onClick, this);
57073         c.on("dblclick", this.onDblClick, this);
57074         c.on("contextmenu", this.onContextMenu, this);
57075         c.on("keydown", this.onKeyDown, this);
57076         if (Roo.isTouch) {
57077             c.on("touchstart", this.onTouchStart, this);
57078         }
57079
57080         this.relayEvents(c, ["mousedown","mouseup","mouseover","mouseout","keypress"]);
57081
57082         this.getSelectionModel().init(this);
57083
57084         view.render();
57085
57086         if(this.loadMask){
57087             this.loadMask = new Roo.LoadMask(this.container,
57088                     Roo.apply({store:this.dataSource}, this.loadMask));
57089         }
57090         
57091         
57092         if (this.toolbar && this.toolbar.xtype) {
57093             this.toolbar.container = this.getView().getHeaderPanel(true);
57094             this.toolbar = new Roo.Toolbar(this.toolbar);
57095         }
57096         if (this.footer && this.footer.xtype) {
57097             this.footer.dataSource = this.getDataSource();
57098             this.footer.container = this.getView().getFooterPanel(true);
57099             this.footer = Roo.factory(this.footer, Roo);
57100         }
57101         if (this.dropTarget && this.dropTarget.xtype) {
57102             delete this.dropTarget.xtype;
57103             this.dropTarget =  new Roo.dd.DropTarget(this.getView().mainBody, this.dropTarget);
57104         }
57105         
57106         
57107         this.rendered = true;
57108         this.fireEvent('render', this);
57109         return this;
57110     },
57111
57112     /**
57113      * Reconfigures the grid to use a different Store and Column Model.
57114      * The View will be bound to the new objects and refreshed.
57115      * @param {Roo.data.Store} dataSource The new {@link Roo.data.Store} object
57116      * @param {Roo.grid.ColumnModel} The new {@link Roo.grid.ColumnModel} object
57117      */
57118     reconfigure : function(dataSource, colModel){
57119         if(this.loadMask){
57120             this.loadMask.destroy();
57121             this.loadMask = new Roo.LoadMask(this.container,
57122                     Roo.apply({store:dataSource}, this.loadMask));
57123         }
57124         this.view.bind(dataSource, colModel);
57125         this.dataSource = dataSource;
57126         this.colModel = colModel;
57127         this.view.refresh(true);
57128     },
57129     /**
57130      * addColumns
57131      * Add's a column, default at the end..
57132      
57133      * @param {int} position to add (default end)
57134      * @param {Array} of objects of column configuration see {@link Roo.grid.ColumnModel} 
57135      */
57136     addColumns : function(pos, ar)
57137     {
57138         
57139         for (var i =0;i< ar.length;i++) {
57140             var cfg = ar[i];
57141             cfg.id = typeof(cfg.id) == 'undefined' ? Roo.id() : cfg.id; // don't normally use this..
57142             this.cm.lookup[cfg.id] = cfg;
57143         }
57144         
57145         
57146         if (typeof(pos) == 'undefined' || pos >= this.cm.config.length) {
57147             pos = this.cm.config.length; //this.cm.config.push(cfg);
57148         } 
57149         pos = Math.max(0,pos);
57150         ar.unshift(0);
57151         ar.unshift(pos);
57152         this.cm.config.splice.apply(this.cm.config, ar);
57153         
57154         
57155         
57156         this.view.generateRules(this.cm);
57157         this.view.refresh(true);
57158         
57159     },
57160     
57161     
57162     
57163     
57164     // private
57165     onKeyDown : function(e){
57166         this.fireEvent("keydown", e);
57167     },
57168
57169     /**
57170      * Destroy this grid.
57171      * @param {Boolean} removeEl True to remove the element
57172      */
57173     destroy : function(removeEl, keepListeners){
57174         if(this.loadMask){
57175             this.loadMask.destroy();
57176         }
57177         var c = this.container;
57178         c.removeAllListeners();
57179         this.view.destroy();
57180         this.colModel.purgeListeners();
57181         if(!keepListeners){
57182             this.purgeListeners();
57183         }
57184         c.update("");
57185         if(removeEl === true){
57186             c.remove();
57187         }
57188     },
57189
57190     // private
57191     processEvent : function(name, e){
57192         // does this fire select???
57193         //Roo.log('grid:processEvent '  + name);
57194         
57195         if (name != 'touchstart' ) {
57196             this.fireEvent(name, e);    
57197         }
57198         
57199         var t = e.getTarget();
57200         var v = this.view;
57201         var header = v.findHeaderIndex(t);
57202         if(header !== false){
57203             var ename = name == 'touchstart' ? 'click' : name;
57204              
57205             this.fireEvent("header" + ename, this, header, e);
57206         }else{
57207             var row = v.findRowIndex(t);
57208             var cell = v.findCellIndex(t);
57209             if (name == 'touchstart') {
57210                 // first touch is always a click.
57211                 // hopefull this happens after selection is updated.?
57212                 name = false;
57213                 
57214                 if (typeof(this.selModel.getSelectedCell) != 'undefined') {
57215                     var cs = this.selModel.getSelectedCell();
57216                     if (row == cs[0] && cell == cs[1]){
57217                         name = 'dblclick';
57218                     }
57219                 }
57220                 if (typeof(this.selModel.getSelections) != 'undefined') {
57221                     var cs = this.selModel.getSelections();
57222                     var ds = this.dataSource;
57223                     if (cs.length == 1 && ds.getAt(row) == cs[0]){
57224                         name = 'dblclick';
57225                     }
57226                 }
57227                 if (!name) {
57228                     return;
57229                 }
57230             }
57231             
57232             
57233             if(row !== false){
57234                 this.fireEvent("row" + name, this, row, e);
57235                 if(cell !== false){
57236                     this.fireEvent("cell" + name, this, row, cell, e);
57237                 }
57238             }
57239         }
57240     },
57241
57242     // private
57243     onClick : function(e){
57244         this.processEvent("click", e);
57245     },
57246    // private
57247     onTouchStart : function(e){
57248         this.processEvent("touchstart", e);
57249     },
57250
57251     // private
57252     onContextMenu : function(e, t){
57253         this.processEvent("contextmenu", e);
57254     },
57255
57256     // private
57257     onDblClick : function(e){
57258         this.processEvent("dblclick", e);
57259     },
57260
57261     // private
57262     walkCells : function(row, col, step, fn, scope){
57263         var cm = this.colModel, clen = cm.getColumnCount();
57264         var ds = this.dataSource, rlen = ds.getCount(), first = true;
57265         if(step < 0){
57266             if(col < 0){
57267                 row--;
57268                 first = false;
57269             }
57270             while(row >= 0){
57271                 if(!first){
57272                     col = clen-1;
57273                 }
57274                 first = false;
57275                 while(col >= 0){
57276                     if(fn.call(scope || this, row, col, cm) === true){
57277                         return [row, col];
57278                     }
57279                     col--;
57280                 }
57281                 row--;
57282             }
57283         } else {
57284             if(col >= clen){
57285                 row++;
57286                 first = false;
57287             }
57288             while(row < rlen){
57289                 if(!first){
57290                     col = 0;
57291                 }
57292                 first = false;
57293                 while(col < clen){
57294                     if(fn.call(scope || this, row, col, cm) === true){
57295                         return [row, col];
57296                     }
57297                     col++;
57298                 }
57299                 row++;
57300             }
57301         }
57302         return null;
57303     },
57304
57305     // private
57306     getSelections : function(){
57307         return this.selModel.getSelections();
57308     },
57309
57310     /**
57311      * Causes the grid to manually recalculate its dimensions. Generally this is done automatically,
57312      * but if manual update is required this method will initiate it.
57313      */
57314     autoSize : function(){
57315         if(this.rendered){
57316             this.view.layout();
57317             if(this.view.adjustForScroll){
57318                 this.view.adjustForScroll();
57319             }
57320         }
57321     },
57322
57323     /**
57324      * Returns the grid's underlying element.
57325      * @return {Element} The element
57326      */
57327     getGridEl : function(){
57328         return this.container;
57329     },
57330
57331     // private for compatibility, overridden by editor grid
57332     stopEditing : function(){},
57333
57334     /**
57335      * Returns the grid's SelectionModel.
57336      * @return {SelectionModel}
57337      */
57338     getSelectionModel : function(){
57339         if(!this.selModel){
57340             this.selModel = new Roo.grid.RowSelectionModel();
57341         }
57342         return this.selModel;
57343     },
57344
57345     /**
57346      * Returns the grid's DataSource.
57347      * @return {DataSource}
57348      */
57349     getDataSource : function(){
57350         return this.dataSource;
57351     },
57352
57353     /**
57354      * Returns the grid's ColumnModel.
57355      * @return {ColumnModel}
57356      */
57357     getColumnModel : function(){
57358         return this.colModel;
57359     },
57360
57361     /**
57362      * Returns the grid's GridView object.
57363      * @return {GridView}
57364      */
57365     getView : function(){
57366         if(!this.view){
57367             this.view = new Roo.grid.GridView(this.viewConfig);
57368             this.relayEvents(this.view, [
57369                 "beforerowremoved", "beforerowsinserted",
57370                 "beforerefresh", "rowremoved",
57371                 "rowsinserted", "rowupdated" ,"refresh"
57372             ]);
57373         }
57374         return this.view;
57375     },
57376     /**
57377      * Called to get grid's drag proxy text, by default returns this.ddText.
57378      * Override this to put something different in the dragged text.
57379      * @return {String}
57380      */
57381     getDragDropText : function(){
57382         var count = this.selModel.getCount();
57383         return String.format(this.ddText, count, count == 1 ? '' : 's');
57384     }
57385 });
57386 /*
57387  * Based on:
57388  * Ext JS Library 1.1.1
57389  * Copyright(c) 2006-2007, Ext JS, LLC.
57390  *
57391  * Originally Released Under LGPL - original licence link has changed is not relivant.
57392  *
57393  * Fork - LGPL
57394  * <script type="text/javascript">
57395  */
57396  /**
57397  * @class Roo.grid.AbstractGridView
57398  * @extends Roo.util.Observable
57399  * @abstract
57400  * Abstract base class for grid Views
57401  * @constructor
57402  */
57403 Roo.grid.AbstractGridView = function(){
57404         this.grid = null;
57405         
57406         this.events = {
57407             "beforerowremoved" : true,
57408             "beforerowsinserted" : true,
57409             "beforerefresh" : true,
57410             "rowremoved" : true,
57411             "rowsinserted" : true,
57412             "rowupdated" : true,
57413             "refresh" : true
57414         };
57415     Roo.grid.AbstractGridView.superclass.constructor.call(this);
57416 };
57417
57418 Roo.extend(Roo.grid.AbstractGridView, Roo.util.Observable, {
57419     rowClass : "x-grid-row",
57420     cellClass : "x-grid-cell",
57421     tdClass : "x-grid-td",
57422     hdClass : "x-grid-hd",
57423     splitClass : "x-grid-hd-split",
57424     
57425     init: function(grid){
57426         this.grid = grid;
57427                 var cid = this.grid.getGridEl().id;
57428         this.colSelector = "#" + cid + " ." + this.cellClass + "-";
57429         this.tdSelector = "#" + cid + " ." + this.tdClass + "-";
57430         this.hdSelector = "#" + cid + " ." + this.hdClass + "-";
57431         this.splitSelector = "#" + cid + " ." + this.splitClass + "-";
57432         },
57433         
57434     getColumnRenderers : function(){
57435         var renderers = [];
57436         var cm = this.grid.colModel;
57437         var colCount = cm.getColumnCount();
57438         for(var i = 0; i < colCount; i++){
57439             renderers[i] = cm.getRenderer(i);
57440         }
57441         return renderers;
57442     },
57443     
57444     getColumnIds : function(){
57445         var ids = [];
57446         var cm = this.grid.colModel;
57447         var colCount = cm.getColumnCount();
57448         for(var i = 0; i < colCount; i++){
57449             ids[i] = cm.getColumnId(i);
57450         }
57451         return ids;
57452     },
57453     
57454     getDataIndexes : function(){
57455         if(!this.indexMap){
57456             this.indexMap = this.buildIndexMap();
57457         }
57458         return this.indexMap.colToData;
57459     },
57460     
57461     getColumnIndexByDataIndex : function(dataIndex){
57462         if(!this.indexMap){
57463             this.indexMap = this.buildIndexMap();
57464         }
57465         return this.indexMap.dataToCol[dataIndex];
57466     },
57467     
57468     /**
57469      * Set a css style for a column dynamically. 
57470      * @param {Number} colIndex The index of the column
57471      * @param {String} name The css property name
57472      * @param {String} value The css value
57473      */
57474     setCSSStyle : function(colIndex, name, value){
57475         var selector = "#" + this.grid.id + " .x-grid-col-" + colIndex;
57476         Roo.util.CSS.updateRule(selector, name, value);
57477     },
57478     
57479     generateRules : function(cm){
57480         var ruleBuf = [], rulesId = this.grid.id + '-cssrules';
57481         Roo.util.CSS.removeStyleSheet(rulesId);
57482         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
57483             var cid = cm.getColumnId(i);
57484             ruleBuf.push(this.colSelector, cid, " {\n", cm.config[i].css, "}\n",
57485                          this.tdSelector, cid, " {\n}\n",
57486                          this.hdSelector, cid, " {\n}\n",
57487                          this.splitSelector, cid, " {\n}\n");
57488         }
57489         return Roo.util.CSS.createStyleSheet(ruleBuf.join(""), rulesId);
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 // private
57503 // This is a support class used internally by the Grid components
57504 Roo.grid.HeaderDragZone = function(grid, hd, hd2){
57505     this.grid = grid;
57506     this.view = grid.getView();
57507     this.ddGroup = "gridHeader" + this.grid.getGridEl().id;
57508     Roo.grid.HeaderDragZone.superclass.constructor.call(this, hd);
57509     if(hd2){
57510         this.setHandleElId(Roo.id(hd));
57511         this.setOuterHandleElId(Roo.id(hd2));
57512     }
57513     this.scroll = false;
57514 };
57515 Roo.extend(Roo.grid.HeaderDragZone, Roo.dd.DragZone, {
57516     maxDragWidth: 120,
57517     getDragData : function(e){
57518         var t = Roo.lib.Event.getTarget(e);
57519         var h = this.view.findHeaderCell(t);
57520         if(h){
57521             return {ddel: h.firstChild, header:h};
57522         }
57523         return false;
57524     },
57525
57526     onInitDrag : function(e){
57527         this.view.headersDisabled = true;
57528         var clone = this.dragData.ddel.cloneNode(true);
57529         clone.id = Roo.id();
57530         clone.style.width = Math.min(this.dragData.header.offsetWidth,this.maxDragWidth) + "px";
57531         this.proxy.update(clone);
57532         return true;
57533     },
57534
57535     afterValidDrop : function(){
57536         var v = this.view;
57537         setTimeout(function(){
57538             v.headersDisabled = false;
57539         }, 50);
57540     },
57541
57542     afterInvalidDrop : function(){
57543         var v = this.view;
57544         setTimeout(function(){
57545             v.headersDisabled = false;
57546         }, 50);
57547     }
57548 });
57549 /*
57550  * Based on:
57551  * Ext JS Library 1.1.1
57552  * Copyright(c) 2006-2007, Ext JS, LLC.
57553  *
57554  * Originally Released Under LGPL - original licence link has changed is not relivant.
57555  *
57556  * Fork - LGPL
57557  * <script type="text/javascript">
57558  */
57559 // private
57560 // This is a support class used internally by the Grid components
57561 Roo.grid.HeaderDropZone = function(grid, hd, hd2){
57562     this.grid = grid;
57563     this.view = grid.getView();
57564     // split the proxies so they don't interfere with mouse events
57565     this.proxyTop = Roo.DomHelper.append(document.body, {
57566         cls:"col-move-top", html:"&#160;"
57567     }, true);
57568     this.proxyBottom = Roo.DomHelper.append(document.body, {
57569         cls:"col-move-bottom", html:"&#160;"
57570     }, true);
57571     this.proxyTop.hide = this.proxyBottom.hide = function(){
57572         this.setLeftTop(-100,-100);
57573         this.setStyle("visibility", "hidden");
57574     };
57575     this.ddGroup = "gridHeader" + this.grid.getGridEl().id;
57576     // temporarily disabled
57577     //Roo.dd.ScrollManager.register(this.view.scroller.dom);
57578     Roo.grid.HeaderDropZone.superclass.constructor.call(this, grid.getGridEl().dom);
57579 };
57580 Roo.extend(Roo.grid.HeaderDropZone, Roo.dd.DropZone, {
57581     proxyOffsets : [-4, -9],
57582     fly: Roo.Element.fly,
57583
57584     getTargetFromEvent : function(e){
57585         var t = Roo.lib.Event.getTarget(e);
57586         var cindex = this.view.findCellIndex(t);
57587         if(cindex !== false){
57588             return this.view.getHeaderCell(cindex);
57589         }
57590         return null;
57591     },
57592
57593     nextVisible : function(h){
57594         var v = this.view, cm = this.grid.colModel;
57595         h = h.nextSibling;
57596         while(h){
57597             if(!cm.isHidden(v.getCellIndex(h))){
57598                 return h;
57599             }
57600             h = h.nextSibling;
57601         }
57602         return null;
57603     },
57604
57605     prevVisible : function(h){
57606         var v = this.view, cm = this.grid.colModel;
57607         h = h.prevSibling;
57608         while(h){
57609             if(!cm.isHidden(v.getCellIndex(h))){
57610                 return h;
57611             }
57612             h = h.prevSibling;
57613         }
57614         return null;
57615     },
57616
57617     positionIndicator : function(h, n, e){
57618         var x = Roo.lib.Event.getPageX(e);
57619         var r = Roo.lib.Dom.getRegion(n.firstChild);
57620         var px, pt, py = r.top + this.proxyOffsets[1];
57621         if((r.right - x) <= (r.right-r.left)/2){
57622             px = r.right+this.view.borderWidth;
57623             pt = "after";
57624         }else{
57625             px = r.left;
57626             pt = "before";
57627         }
57628         var oldIndex = this.view.getCellIndex(h);
57629         var newIndex = this.view.getCellIndex(n);
57630
57631         if(this.grid.colModel.isFixed(newIndex)){
57632             return false;
57633         }
57634
57635         var locked = this.grid.colModel.isLocked(newIndex);
57636
57637         if(pt == "after"){
57638             newIndex++;
57639         }
57640         if(oldIndex < newIndex){
57641             newIndex--;
57642         }
57643         if(oldIndex == newIndex && (locked == this.grid.colModel.isLocked(oldIndex))){
57644             return false;
57645         }
57646         px +=  this.proxyOffsets[0];
57647         this.proxyTop.setLeftTop(px, py);
57648         this.proxyTop.show();
57649         if(!this.bottomOffset){
57650             this.bottomOffset = this.view.mainHd.getHeight();
57651         }
57652         this.proxyBottom.setLeftTop(px, py+this.proxyTop.dom.offsetHeight+this.bottomOffset);
57653         this.proxyBottom.show();
57654         return pt;
57655     },
57656
57657     onNodeEnter : function(n, dd, e, data){
57658         if(data.header != n){
57659             this.positionIndicator(data.header, n, e);
57660         }
57661     },
57662
57663     onNodeOver : function(n, dd, e, data){
57664         var result = false;
57665         if(data.header != n){
57666             result = this.positionIndicator(data.header, n, e);
57667         }
57668         if(!result){
57669             this.proxyTop.hide();
57670             this.proxyBottom.hide();
57671         }
57672         return result ? this.dropAllowed : this.dropNotAllowed;
57673     },
57674
57675     onNodeOut : function(n, dd, e, data){
57676         this.proxyTop.hide();
57677         this.proxyBottom.hide();
57678     },
57679
57680     onNodeDrop : function(n, dd, e, data){
57681         var h = data.header;
57682         if(h != n){
57683             var cm = this.grid.colModel;
57684             var x = Roo.lib.Event.getPageX(e);
57685             var r = Roo.lib.Dom.getRegion(n.firstChild);
57686             var pt = (r.right - x) <= ((r.right-r.left)/2) ? "after" : "before";
57687             var oldIndex = this.view.getCellIndex(h);
57688             var newIndex = this.view.getCellIndex(n);
57689             var locked = cm.isLocked(newIndex);
57690             if(pt == "after"){
57691                 newIndex++;
57692             }
57693             if(oldIndex < newIndex){
57694                 newIndex--;
57695             }
57696             if(oldIndex == newIndex && (locked == cm.isLocked(oldIndex))){
57697                 return false;
57698             }
57699             cm.setLocked(oldIndex, locked, true);
57700             cm.moveColumn(oldIndex, newIndex);
57701             this.grid.fireEvent("columnmove", oldIndex, newIndex);
57702             return true;
57703         }
57704         return false;
57705     }
57706 });
57707 /*
57708  * Based on:
57709  * Ext JS Library 1.1.1
57710  * Copyright(c) 2006-2007, Ext JS, LLC.
57711  *
57712  * Originally Released Under LGPL - original licence link has changed is not relivant.
57713  *
57714  * Fork - LGPL
57715  * <script type="text/javascript">
57716  */
57717   
57718 /**
57719  * @class Roo.grid.GridView
57720  * @extends Roo.util.Observable
57721  *
57722  * @constructor
57723  * @param {Object} config
57724  */
57725 Roo.grid.GridView = function(config){
57726     Roo.grid.GridView.superclass.constructor.call(this);
57727     this.el = null;
57728
57729     Roo.apply(this, config);
57730 };
57731
57732 Roo.extend(Roo.grid.GridView, Roo.grid.AbstractGridView, {
57733
57734     unselectable :  'unselectable="on"',
57735     unselectableCls :  'x-unselectable',
57736     
57737     
57738     rowClass : "x-grid-row",
57739
57740     cellClass : "x-grid-col",
57741
57742     tdClass : "x-grid-td",
57743
57744     hdClass : "x-grid-hd",
57745
57746     splitClass : "x-grid-split",
57747
57748     sortClasses : ["sort-asc", "sort-desc"],
57749
57750     enableMoveAnim : false,
57751
57752     hlColor: "C3DAF9",
57753
57754     dh : Roo.DomHelper,
57755
57756     fly : Roo.Element.fly,
57757
57758     css : Roo.util.CSS,
57759
57760     borderWidth: 1,
57761
57762     splitOffset: 3,
57763
57764     scrollIncrement : 22,
57765
57766     cellRE: /(?:.*?)x-grid-(?:hd|cell|csplit)-(?:[\d]+)-([\d]+)(?:.*?)/,
57767
57768     findRE: /\s?(?:x-grid-hd|x-grid-col|x-grid-csplit)\s/,
57769
57770     bind : function(ds, cm){
57771         if(this.ds){
57772             this.ds.un("load", this.onLoad, this);
57773             this.ds.un("datachanged", this.onDataChange, this);
57774             this.ds.un("add", this.onAdd, this);
57775             this.ds.un("remove", this.onRemove, this);
57776             this.ds.un("update", this.onUpdate, this);
57777             this.ds.un("clear", this.onClear, this);
57778         }
57779         if(ds){
57780             ds.on("load", this.onLoad, this);
57781             ds.on("datachanged", this.onDataChange, this);
57782             ds.on("add", this.onAdd, this);
57783             ds.on("remove", this.onRemove, this);
57784             ds.on("update", this.onUpdate, this);
57785             ds.on("clear", this.onClear, this);
57786         }
57787         this.ds = ds;
57788
57789         if(this.cm){
57790             this.cm.un("widthchange", this.onColWidthChange, this);
57791             this.cm.un("headerchange", this.onHeaderChange, this);
57792             this.cm.un("hiddenchange", this.onHiddenChange, this);
57793             this.cm.un("columnmoved", this.onColumnMove, this);
57794             this.cm.un("columnlockchange", this.onColumnLock, this);
57795         }
57796         if(cm){
57797             this.generateRules(cm);
57798             cm.on("widthchange", this.onColWidthChange, this);
57799             cm.on("headerchange", this.onHeaderChange, this);
57800             cm.on("hiddenchange", this.onHiddenChange, this);
57801             cm.on("columnmoved", this.onColumnMove, this);
57802             cm.on("columnlockchange", this.onColumnLock, this);
57803         }
57804         this.cm = cm;
57805     },
57806
57807     init: function(grid){
57808         Roo.grid.GridView.superclass.init.call(this, grid);
57809
57810         this.bind(grid.dataSource, grid.colModel);
57811
57812         grid.on("headerclick", this.handleHeaderClick, this);
57813
57814         if(grid.trackMouseOver){
57815             grid.on("mouseover", this.onRowOver, this);
57816             grid.on("mouseout", this.onRowOut, this);
57817         }
57818         grid.cancelTextSelection = function(){};
57819         this.gridId = grid.id;
57820
57821         var tpls = this.templates || {};
57822
57823         if(!tpls.master){
57824             tpls.master = new Roo.Template(
57825                '<div class="x-grid" hidefocus="true">',
57826                 '<a href="#" class="x-grid-focus" tabIndex="-1"></a>',
57827                   '<div class="x-grid-topbar"></div>',
57828                   '<div class="x-grid-scroller"><div></div></div>',
57829                   '<div class="x-grid-locked">',
57830                       '<div class="x-grid-header">{lockedHeader}</div>',
57831                       '<div class="x-grid-body">{lockedBody}</div>',
57832                   "</div>",
57833                   '<div class="x-grid-viewport">',
57834                       '<div class="x-grid-header">{header}</div>',
57835                       '<div class="x-grid-body">{body}</div>',
57836                   "</div>",
57837                   '<div class="x-grid-bottombar"></div>',
57838                  
57839                   '<div class="x-grid-resize-proxy">&#160;</div>',
57840                "</div>"
57841             );
57842             tpls.master.disableformats = true;
57843         }
57844
57845         if(!tpls.header){
57846             tpls.header = new Roo.Template(
57847                '<table border="0" cellspacing="0" cellpadding="0">',
57848                '<tbody><tr class="x-grid-hd-row">{cells}</tr></tbody>',
57849                "</table>{splits}"
57850             );
57851             tpls.header.disableformats = true;
57852         }
57853         tpls.header.compile();
57854
57855         if(!tpls.hcell){
57856             tpls.hcell = new Roo.Template(
57857                 '<td class="x-grid-hd x-grid-td-{id} {cellId}"><div title="{title}" class="x-grid-hd-inner x-grid-hd-{id}">',
57858                 '<div class="x-grid-hd-text ' + this.unselectableCls +  '" ' + this.unselectable +'>{value}<img class="x-grid-sort-icon" src="', Roo.BLANK_IMAGE_URL, '" /></div>',
57859                 "</div></td>"
57860              );
57861              tpls.hcell.disableFormats = true;
57862         }
57863         tpls.hcell.compile();
57864
57865         if(!tpls.hsplit){
57866             tpls.hsplit = new Roo.Template('<div class="x-grid-split {splitId} x-grid-split-{id}" style="{style} ' +
57867                                             this.unselectableCls +  '" ' + this.unselectable +'>&#160;</div>');
57868             tpls.hsplit.disableFormats = true;
57869         }
57870         tpls.hsplit.compile();
57871
57872         if(!tpls.body){
57873             tpls.body = new Roo.Template(
57874                '<table border="0" cellspacing="0" cellpadding="0">',
57875                "<tbody>{rows}</tbody>",
57876                "</table>"
57877             );
57878             tpls.body.disableFormats = true;
57879         }
57880         tpls.body.compile();
57881
57882         if(!tpls.row){
57883             tpls.row = new Roo.Template('<tr class="x-grid-row {alt}">{cells}</tr>');
57884             tpls.row.disableFormats = true;
57885         }
57886         tpls.row.compile();
57887
57888         if(!tpls.cell){
57889             tpls.cell = new Roo.Template(
57890                 '<td class="x-grid-col x-grid-td-{id} {cellId} {css}" tabIndex="0">',
57891                 '<div class="x-grid-col-{id} x-grid-cell-inner"><div class="x-grid-cell-text ' +
57892                     this.unselectableCls +  '" ' + this.unselectable +'" {attr}>{value}</div></div>',
57893                 "</td>"
57894             );
57895             tpls.cell.disableFormats = true;
57896         }
57897         tpls.cell.compile();
57898
57899         this.templates = tpls;
57900     },
57901
57902     // remap these for backwards compat
57903     onColWidthChange : function(){
57904         this.updateColumns.apply(this, arguments);
57905     },
57906     onHeaderChange : function(){
57907         this.updateHeaders.apply(this, arguments);
57908     }, 
57909     onHiddenChange : function(){
57910         this.handleHiddenChange.apply(this, arguments);
57911     },
57912     onColumnMove : function(){
57913         this.handleColumnMove.apply(this, arguments);
57914     },
57915     onColumnLock : function(){
57916         this.handleLockChange.apply(this, arguments);
57917     },
57918
57919     onDataChange : function(){
57920         this.refresh();
57921         this.updateHeaderSortState();
57922     },
57923
57924     onClear : function(){
57925         this.refresh();
57926     },
57927
57928     onUpdate : function(ds, record){
57929         this.refreshRow(record);
57930     },
57931
57932     refreshRow : function(record){
57933         var ds = this.ds, index;
57934         if(typeof record == 'number'){
57935             index = record;
57936             record = ds.getAt(index);
57937         }else{
57938             index = ds.indexOf(record);
57939         }
57940         this.insertRows(ds, index, index, true);
57941         this.onRemove(ds, record, index+1, true);
57942         this.syncRowHeights(index, index);
57943         this.layout();
57944         this.fireEvent("rowupdated", this, index, record);
57945     },
57946
57947     onAdd : function(ds, records, index){
57948         this.insertRows(ds, index, index + (records.length-1));
57949     },
57950
57951     onRemove : function(ds, record, index, isUpdate){
57952         if(isUpdate !== true){
57953             this.fireEvent("beforerowremoved", this, index, record);
57954         }
57955         var bt = this.getBodyTable(), lt = this.getLockedTable();
57956         if(bt.rows[index]){
57957             bt.firstChild.removeChild(bt.rows[index]);
57958         }
57959         if(lt.rows[index]){
57960             lt.firstChild.removeChild(lt.rows[index]);
57961         }
57962         if(isUpdate !== true){
57963             this.stripeRows(index);
57964             this.syncRowHeights(index, index);
57965             this.layout();
57966             this.fireEvent("rowremoved", this, index, record);
57967         }
57968     },
57969
57970     onLoad : function(){
57971         this.scrollToTop();
57972     },
57973
57974     /**
57975      * Scrolls the grid to the top
57976      */
57977     scrollToTop : function(){
57978         if(this.scroller){
57979             this.scroller.dom.scrollTop = 0;
57980             this.syncScroll();
57981         }
57982     },
57983
57984     /**
57985      * Gets a panel in the header of the grid that can be used for toolbars etc.
57986      * After modifying the contents of this panel a call to grid.autoSize() may be
57987      * required to register any changes in size.
57988      * @param {Boolean} doShow By default the header is hidden. Pass true to show the panel
57989      * @return Roo.Element
57990      */
57991     getHeaderPanel : function(doShow){
57992         if(doShow){
57993             this.headerPanel.show();
57994         }
57995         return this.headerPanel;
57996     },
57997
57998     /**
57999      * Gets a panel in the footer of the grid that can be used for toolbars etc.
58000      * After modifying the contents of this panel a call to grid.autoSize() may be
58001      * required to register any changes in size.
58002      * @param {Boolean} doShow By default the footer is hidden. Pass true to show the panel
58003      * @return Roo.Element
58004      */
58005     getFooterPanel : function(doShow){
58006         if(doShow){
58007             this.footerPanel.show();
58008         }
58009         return this.footerPanel;
58010     },
58011
58012     initElements : function(){
58013         var E = Roo.Element;
58014         var el = this.grid.getGridEl().dom.firstChild;
58015         var cs = el.childNodes;
58016
58017         this.el = new E(el);
58018         
58019          this.focusEl = new E(el.firstChild);
58020         this.focusEl.swallowEvent("click", true);
58021         
58022         this.headerPanel = new E(cs[1]);
58023         this.headerPanel.enableDisplayMode("block");
58024
58025         this.scroller = new E(cs[2]);
58026         this.scrollSizer = new E(this.scroller.dom.firstChild);
58027
58028         this.lockedWrap = new E(cs[3]);
58029         this.lockedHd = new E(this.lockedWrap.dom.firstChild);
58030         this.lockedBody = new E(this.lockedWrap.dom.childNodes[1]);
58031
58032         this.mainWrap = new E(cs[4]);
58033         this.mainHd = new E(this.mainWrap.dom.firstChild);
58034         this.mainBody = new E(this.mainWrap.dom.childNodes[1]);
58035
58036         this.footerPanel = new E(cs[5]);
58037         this.footerPanel.enableDisplayMode("block");
58038
58039         this.resizeProxy = new E(cs[6]);
58040
58041         this.headerSelector = String.format(
58042            '#{0} td.x-grid-hd, #{1} td.x-grid-hd',
58043            this.lockedHd.id, this.mainHd.id
58044         );
58045
58046         this.splitterSelector = String.format(
58047            '#{0} div.x-grid-split, #{1} div.x-grid-split',
58048            this.idToCssName(this.lockedHd.id), this.idToCssName(this.mainHd.id)
58049         );
58050     },
58051     idToCssName : function(s)
58052     {
58053         return s.replace(/[^a-z0-9]+/ig, '-');
58054     },
58055
58056     getHeaderCell : function(index){
58057         return Roo.DomQuery.select(this.headerSelector)[index];
58058     },
58059
58060     getHeaderCellMeasure : function(index){
58061         return this.getHeaderCell(index).firstChild;
58062     },
58063
58064     getHeaderCellText : function(index){
58065         return this.getHeaderCell(index).firstChild.firstChild;
58066     },
58067
58068     getLockedTable : function(){
58069         return this.lockedBody.dom.firstChild;
58070     },
58071
58072     getBodyTable : function(){
58073         return this.mainBody.dom.firstChild;
58074     },
58075
58076     getLockedRow : function(index){
58077         return this.getLockedTable().rows[index];
58078     },
58079
58080     getRow : function(index){
58081         return this.getBodyTable().rows[index];
58082     },
58083
58084     getRowComposite : function(index){
58085         if(!this.rowEl){
58086             this.rowEl = new Roo.CompositeElementLite();
58087         }
58088         var els = [], lrow, mrow;
58089         if(lrow = this.getLockedRow(index)){
58090             els.push(lrow);
58091         }
58092         if(mrow = this.getRow(index)){
58093             els.push(mrow);
58094         }
58095         this.rowEl.elements = els;
58096         return this.rowEl;
58097     },
58098     /**
58099      * Gets the 'td' of the cell
58100      * 
58101      * @param {Integer} rowIndex row to select
58102      * @param {Integer} colIndex column to select
58103      * 
58104      * @return {Object} 
58105      */
58106     getCell : function(rowIndex, colIndex){
58107         var locked = this.cm.getLockedCount();
58108         var source;
58109         if(colIndex < locked){
58110             source = this.lockedBody.dom.firstChild;
58111         }else{
58112             source = this.mainBody.dom.firstChild;
58113             colIndex -= locked;
58114         }
58115         return source.rows[rowIndex].childNodes[colIndex];
58116     },
58117
58118     getCellText : function(rowIndex, colIndex){
58119         return this.getCell(rowIndex, colIndex).firstChild.firstChild;
58120     },
58121
58122     getCellBox : function(cell){
58123         var b = this.fly(cell).getBox();
58124         if(Roo.isOpera){ // opera fails to report the Y
58125             b.y = cell.offsetTop + this.mainBody.getY();
58126         }
58127         return b;
58128     },
58129
58130     getCellIndex : function(cell){
58131         var id = String(cell.className).match(this.cellRE);
58132         if(id){
58133             return parseInt(id[1], 10);
58134         }
58135         return 0;
58136     },
58137
58138     findHeaderIndex : function(n){
58139         var r = Roo.fly(n).findParent("td." + this.hdClass, 6);
58140         return r ? this.getCellIndex(r) : false;
58141     },
58142
58143     findHeaderCell : function(n){
58144         var r = Roo.fly(n).findParent("td." + this.hdClass, 6);
58145         return r ? r : false;
58146     },
58147
58148     findRowIndex : function(n){
58149         if(!n){
58150             return false;
58151         }
58152         var r = Roo.fly(n).findParent("tr." + this.rowClass, 6);
58153         return r ? r.rowIndex : false;
58154     },
58155
58156     findCellIndex : function(node){
58157         var stop = this.el.dom;
58158         while(node && node != stop){
58159             if(this.findRE.test(node.className)){
58160                 return this.getCellIndex(node);
58161             }
58162             node = node.parentNode;
58163         }
58164         return false;
58165     },
58166
58167     getColumnId : function(index){
58168         return this.cm.getColumnId(index);
58169     },
58170
58171     getSplitters : function()
58172     {
58173         if(this.splitterSelector){
58174            return Roo.DomQuery.select(this.splitterSelector);
58175         }else{
58176             return null;
58177       }
58178     },
58179
58180     getSplitter : function(index){
58181         return this.getSplitters()[index];
58182     },
58183
58184     onRowOver : function(e, t){
58185         var row;
58186         if((row = this.findRowIndex(t)) !== false){
58187             this.getRowComposite(row).addClass("x-grid-row-over");
58188         }
58189     },
58190
58191     onRowOut : function(e, t){
58192         var row;
58193         if((row = this.findRowIndex(t)) !== false && row !== this.findRowIndex(e.getRelatedTarget())){
58194             this.getRowComposite(row).removeClass("x-grid-row-over");
58195         }
58196     },
58197
58198     renderHeaders : function(){
58199         var cm = this.cm;
58200         var ct = this.templates.hcell, ht = this.templates.header, st = this.templates.hsplit;
58201         var cb = [], lb = [], sb = [], lsb = [], p = {};
58202         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
58203             p.cellId = "x-grid-hd-0-" + i;
58204             p.splitId = "x-grid-csplit-0-" + i;
58205             p.id = cm.getColumnId(i);
58206             p.value = cm.getColumnHeader(i) || "";
58207             p.title = cm.getColumnTooltip(i) || (''+p.value).match(/\</)  ? '' :  p.value  || "";
58208             p.style = (this.grid.enableColumnResize === false || !cm.isResizable(i) || cm.isFixed(i)) ? 'cursor:default' : '';
58209             if(!cm.isLocked(i)){
58210                 cb[cb.length] = ct.apply(p);
58211                 sb[sb.length] = st.apply(p);
58212             }else{
58213                 lb[lb.length] = ct.apply(p);
58214                 lsb[lsb.length] = st.apply(p);
58215             }
58216         }
58217         return [ht.apply({cells: lb.join(""), splits:lsb.join("")}),
58218                 ht.apply({cells: cb.join(""), splits:sb.join("")})];
58219     },
58220
58221     updateHeaders : function(){
58222         var html = this.renderHeaders();
58223         this.lockedHd.update(html[0]);
58224         this.mainHd.update(html[1]);
58225     },
58226
58227     /**
58228      * Focuses the specified row.
58229      * @param {Number} row The row index
58230      */
58231     focusRow : function(row)
58232     {
58233         //Roo.log('GridView.focusRow');
58234         var x = this.scroller.dom.scrollLeft;
58235         this.focusCell(row, 0, false);
58236         this.scroller.dom.scrollLeft = x;
58237     },
58238
58239     /**
58240      * Focuses the specified cell.
58241      * @param {Number} row The row index
58242      * @param {Number} col The column index
58243      * @param {Boolean} hscroll false to disable horizontal scrolling
58244      */
58245     focusCell : function(row, col, hscroll)
58246     {
58247         //Roo.log('GridView.focusCell');
58248         var el = this.ensureVisible(row, col, hscroll);
58249         this.focusEl.alignTo(el, "tl-tl");
58250         if(Roo.isGecko){
58251             this.focusEl.focus();
58252         }else{
58253             this.focusEl.focus.defer(1, this.focusEl);
58254         }
58255     },
58256
58257     /**
58258      * Scrolls the specified cell into view
58259      * @param {Number} row The row index
58260      * @param {Number} col The column index
58261      * @param {Boolean} hscroll false to disable horizontal scrolling
58262      */
58263     ensureVisible : function(row, col, hscroll)
58264     {
58265         //Roo.log('GridView.ensureVisible,' + row + ',' + col);
58266         //return null; //disable for testing.
58267         if(typeof row != "number"){
58268             row = row.rowIndex;
58269         }
58270         if(row < 0 && row >= this.ds.getCount()){
58271             return  null;
58272         }
58273         col = (col !== undefined ? col : 0);
58274         var cm = this.grid.colModel;
58275         while(cm.isHidden(col)){
58276             col++;
58277         }
58278
58279         var el = this.getCell(row, col);
58280         if(!el){
58281             return null;
58282         }
58283         var c = this.scroller.dom;
58284
58285         var ctop = parseInt(el.offsetTop, 10);
58286         var cleft = parseInt(el.offsetLeft, 10);
58287         var cbot = ctop + el.offsetHeight;
58288         var cright = cleft + el.offsetWidth;
58289         
58290         var ch = c.clientHeight - this.mainHd.dom.offsetHeight;
58291         var stop = parseInt(c.scrollTop, 10);
58292         var sleft = parseInt(c.scrollLeft, 10);
58293         var sbot = stop + ch;
58294         var sright = sleft + c.clientWidth;
58295         /*
58296         Roo.log('GridView.ensureVisible:' +
58297                 ' ctop:' + ctop +
58298                 ' c.clientHeight:' + c.clientHeight +
58299                 ' this.mainHd.dom.offsetHeight:' + this.mainHd.dom.offsetHeight +
58300                 ' stop:' + stop +
58301                 ' cbot:' + cbot +
58302                 ' sbot:' + sbot +
58303                 ' ch:' + ch  
58304                 );
58305         */
58306         if(ctop < stop){
58307             c.scrollTop = ctop;
58308             //Roo.log("set scrolltop to ctop DISABLE?");
58309         }else if(cbot > sbot){
58310             //Roo.log("set scrolltop to cbot-ch");
58311             c.scrollTop = cbot-ch;
58312         }
58313         
58314         if(hscroll !== false){
58315             if(cleft < sleft){
58316                 c.scrollLeft = cleft;
58317             }else if(cright > sright){
58318                 c.scrollLeft = cright-c.clientWidth;
58319             }
58320         }
58321          
58322         return el;
58323     },
58324
58325     updateColumns : function(){
58326         this.grid.stopEditing();
58327         var cm = this.grid.colModel, colIds = this.getColumnIds();
58328         //var totalWidth = cm.getTotalWidth();
58329         var pos = 0;
58330         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
58331             //if(cm.isHidden(i)) continue;
58332             var w = cm.getColumnWidth(i);
58333             this.css.updateRule(this.colSelector+this.idToCssName(colIds[i]), "width", (w - this.borderWidth) + "px");
58334             this.css.updateRule(this.hdSelector+this.idToCssName(colIds[i]), "width", (w - this.borderWidth) + "px");
58335         }
58336         this.updateSplitters();
58337     },
58338
58339     generateRules : function(cm){
58340         var ruleBuf = [], rulesId = this.idToCssName(this.grid.id)+ '-cssrules';
58341         Roo.util.CSS.removeStyleSheet(rulesId);
58342         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
58343             var cid = cm.getColumnId(i);
58344             var align = '';
58345             if(cm.config[i].align){
58346                 align = 'text-align:'+cm.config[i].align+';';
58347             }
58348             var hidden = '';
58349             if(cm.isHidden(i)){
58350                 hidden = 'display:none;';
58351             }
58352             var width = "width:" + (cm.getColumnWidth(i) - this.borderWidth) + "px;";
58353             ruleBuf.push(
58354                     this.colSelector, cid, " {\n", cm.config[i].css, align, width, "\n}\n",
58355                     this.hdSelector, cid, " {\n", align, width, "}\n",
58356                     this.tdSelector, cid, " {\n",hidden,"\n}\n",
58357                     this.splitSelector, cid, " {\n", hidden , "\n}\n");
58358         }
58359         return Roo.util.CSS.createStyleSheet(ruleBuf.join(""), rulesId);
58360     },
58361
58362     updateSplitters : function(){
58363         var cm = this.cm, s = this.getSplitters();
58364         if(s){ // splitters not created yet
58365             var pos = 0, locked = true;
58366             for(var i = 0, len = cm.getColumnCount(); i < len; i++){
58367                 if(cm.isHidden(i)) {
58368                     continue;
58369                 }
58370                 var w = cm.getColumnWidth(i); // make sure it's a number
58371                 if(!cm.isLocked(i) && locked){
58372                     pos = 0;
58373                     locked = false;
58374                 }
58375                 pos += w;
58376                 s[i].style.left = (pos-this.splitOffset) + "px";
58377             }
58378         }
58379     },
58380
58381     handleHiddenChange : function(colModel, colIndex, hidden){
58382         if(hidden){
58383             this.hideColumn(colIndex);
58384         }else{
58385             this.unhideColumn(colIndex);
58386         }
58387     },
58388
58389     hideColumn : function(colIndex){
58390         var cid = this.getColumnId(colIndex);
58391         this.css.updateRule(this.tdSelector+this.idToCssName(cid), "display", "none");
58392         this.css.updateRule(this.splitSelector+this.idToCssName(cid), "display", "none");
58393         if(Roo.isSafari){
58394             this.updateHeaders();
58395         }
58396         this.updateSplitters();
58397         this.layout();
58398     },
58399
58400     unhideColumn : function(colIndex){
58401         var cid = this.getColumnId(colIndex);
58402         this.css.updateRule(this.tdSelector+this.idToCssName(cid), "display", "");
58403         this.css.updateRule(this.splitSelector+this.idToCssName(cid), "display", "");
58404
58405         if(Roo.isSafari){
58406             this.updateHeaders();
58407         }
58408         this.updateSplitters();
58409         this.layout();
58410     },
58411
58412     insertRows : function(dm, firstRow, lastRow, isUpdate){
58413         if(firstRow == 0 && lastRow == dm.getCount()-1){
58414             this.refresh();
58415         }else{
58416             if(!isUpdate){
58417                 this.fireEvent("beforerowsinserted", this, firstRow, lastRow);
58418             }
58419             var s = this.getScrollState();
58420             var markup = this.renderRows(firstRow, lastRow);
58421             this.bufferRows(markup[0], this.getLockedTable(), firstRow);
58422             this.bufferRows(markup[1], this.getBodyTable(), firstRow);
58423             this.restoreScroll(s);
58424             if(!isUpdate){
58425                 this.fireEvent("rowsinserted", this, firstRow, lastRow);
58426                 this.syncRowHeights(firstRow, lastRow);
58427                 this.stripeRows(firstRow);
58428                 this.layout();
58429             }
58430         }
58431     },
58432
58433     bufferRows : function(markup, target, index){
58434         var before = null, trows = target.rows, tbody = target.tBodies[0];
58435         if(index < trows.length){
58436             before = trows[index];
58437         }
58438         var b = document.createElement("div");
58439         b.innerHTML = "<table><tbody>"+markup+"</tbody></table>";
58440         var rows = b.firstChild.rows;
58441         for(var i = 0, len = rows.length; i < len; i++){
58442             if(before){
58443                 tbody.insertBefore(rows[0], before);
58444             }else{
58445                 tbody.appendChild(rows[0]);
58446             }
58447         }
58448         b.innerHTML = "";
58449         b = null;
58450     },
58451
58452     deleteRows : function(dm, firstRow, lastRow){
58453         if(dm.getRowCount()<1){
58454             this.fireEvent("beforerefresh", this);
58455             this.mainBody.update("");
58456             this.lockedBody.update("");
58457             this.fireEvent("refresh", this);
58458         }else{
58459             this.fireEvent("beforerowsdeleted", this, firstRow, lastRow);
58460             var bt = this.getBodyTable();
58461             var tbody = bt.firstChild;
58462             var rows = bt.rows;
58463             for(var rowIndex = firstRow; rowIndex <= lastRow; rowIndex++){
58464                 tbody.removeChild(rows[firstRow]);
58465             }
58466             this.stripeRows(firstRow);
58467             this.fireEvent("rowsdeleted", this, firstRow, lastRow);
58468         }
58469     },
58470
58471     updateRows : function(dataSource, firstRow, lastRow){
58472         var s = this.getScrollState();
58473         this.refresh();
58474         this.restoreScroll(s);
58475     },
58476
58477     handleSort : function(dataSource, sortColumnIndex, sortDir, noRefresh){
58478         if(!noRefresh){
58479            this.refresh();
58480         }
58481         this.updateHeaderSortState();
58482     },
58483
58484     getScrollState : function(){
58485         
58486         var sb = this.scroller.dom;
58487         return {left: sb.scrollLeft, top: sb.scrollTop};
58488     },
58489
58490     stripeRows : function(startRow){
58491         if(!this.grid.stripeRows || this.ds.getCount() < 1){
58492             return;
58493         }
58494         startRow = startRow || 0;
58495         var rows = this.getBodyTable().rows;
58496         var lrows = this.getLockedTable().rows;
58497         var cls = ' x-grid-row-alt ';
58498         for(var i = startRow, len = rows.length; i < len; i++){
58499             var row = rows[i], lrow = lrows[i];
58500             var isAlt = ((i+1) % 2 == 0);
58501             var hasAlt = (' '+row.className + ' ').indexOf(cls) != -1;
58502             if(isAlt == hasAlt){
58503                 continue;
58504             }
58505             if(isAlt){
58506                 row.className += " x-grid-row-alt";
58507             }else{
58508                 row.className = row.className.replace("x-grid-row-alt", "");
58509             }
58510             if(lrow){
58511                 lrow.className = row.className;
58512             }
58513         }
58514     },
58515
58516     restoreScroll : function(state){
58517         //Roo.log('GridView.restoreScroll');
58518         var sb = this.scroller.dom;
58519         sb.scrollLeft = state.left;
58520         sb.scrollTop = state.top;
58521         this.syncScroll();
58522     },
58523
58524     syncScroll : function(){
58525         //Roo.log('GridView.syncScroll');
58526         var sb = this.scroller.dom;
58527         var sh = this.mainHd.dom;
58528         var bs = this.mainBody.dom;
58529         var lv = this.lockedBody.dom;
58530         sh.scrollLeft = bs.scrollLeft = sb.scrollLeft;
58531         lv.scrollTop = bs.scrollTop = sb.scrollTop;
58532     },
58533
58534     handleScroll : function(e){
58535         this.syncScroll();
58536         var sb = this.scroller.dom;
58537         this.grid.fireEvent("bodyscroll", sb.scrollLeft, sb.scrollTop);
58538         e.stopEvent();
58539     },
58540
58541     handleWheel : function(e){
58542         var d = e.getWheelDelta();
58543         this.scroller.dom.scrollTop -= d*22;
58544         // set this here to prevent jumpy scrolling on large tables
58545         this.lockedBody.dom.scrollTop = this.mainBody.dom.scrollTop = this.scroller.dom.scrollTop;
58546         e.stopEvent();
58547     },
58548
58549     renderRows : function(startRow, endRow){
58550         // pull in all the crap needed to render rows
58551         var g = this.grid, cm = g.colModel, ds = g.dataSource, stripe = g.stripeRows;
58552         var colCount = cm.getColumnCount();
58553
58554         if(ds.getCount() < 1){
58555             return ["", ""];
58556         }
58557
58558         // build a map for all the columns
58559         var cs = [];
58560         for(var i = 0; i < colCount; i++){
58561             var name = cm.getDataIndex(i);
58562             cs[i] = {
58563                 name : typeof name == 'undefined' ? ds.fields.get(i).name : name,
58564                 renderer : cm.getRenderer(i),
58565                 id : cm.getColumnId(i),
58566                 locked : cm.isLocked(i),
58567                 has_editor : cm.isCellEditable(i)
58568             };
58569         }
58570
58571         startRow = startRow || 0;
58572         endRow = typeof endRow == "undefined"? ds.getCount()-1 : endRow;
58573
58574         // records to render
58575         var rs = ds.getRange(startRow, endRow);
58576
58577         return this.doRender(cs, rs, ds, startRow, colCount, stripe);
58578     },
58579
58580     // As much as I hate to duplicate code, this was branched because FireFox really hates
58581     // [].join("") on strings. The performance difference was substantial enough to
58582     // branch this function
58583     doRender : Roo.isGecko ?
58584             function(cs, rs, ds, startRow, colCount, stripe){
58585                 var ts = this.templates, ct = ts.cell, rt = ts.row;
58586                 // buffers
58587                 var buf = "", lbuf = "", cb, lcb, c, p = {}, rp = {}, r, rowIndex;
58588                 
58589                 var hasListener = this.grid.hasListener('rowclass');
58590                 var rowcfg = {};
58591                 for(var j = 0, len = rs.length; j < len; j++){
58592                     r = rs[j]; cb = ""; lcb = ""; rowIndex = (j+startRow);
58593                     for(var i = 0; i < colCount; i++){
58594                         c = cs[i];
58595                         p.cellId = "x-grid-cell-" + rowIndex + "-" + i;
58596                         p.id = c.id;
58597                         p.css = p.attr = "";
58598                         p.value = c.renderer(r.data[c.name], p, r, rowIndex, i, ds);
58599                         if(p.value == undefined || p.value === "") {
58600                             p.value = "&#160;";
58601                         }
58602                         if(c.has_editor){
58603                             p.css += ' x-grid-editable-cell';
58604                         }
58605                         if(c.dirty && typeof r.modified[c.name] !== 'undefined'){
58606                             p.css +=  ' x-grid-dirty-cell';
58607                         }
58608                         var markup = ct.apply(p);
58609                         if(!c.locked){
58610                             cb+= markup;
58611                         }else{
58612                             lcb+= markup;
58613                         }
58614                     }
58615                     var alt = [];
58616                     if(stripe && ((rowIndex+1) % 2 == 0)){
58617                         alt.push("x-grid-row-alt")
58618                     }
58619                     if(r.dirty){
58620                         alt.push(  " x-grid-dirty-row");
58621                     }
58622                     rp.cells = lcb;
58623                     if(this.getRowClass){
58624                         alt.push(this.getRowClass(r, rowIndex));
58625                     }
58626                     if (hasListener) {
58627                         rowcfg = {
58628                              
58629                             record: r,
58630                             rowIndex : rowIndex,
58631                             rowClass : ''
58632                         };
58633                         this.grid.fireEvent('rowclass', this, rowcfg);
58634                         alt.push(rowcfg.rowClass);
58635                     }
58636                     rp.alt = alt.join(" ");
58637                     lbuf+= rt.apply(rp);
58638                     rp.cells = cb;
58639                     buf+=  rt.apply(rp);
58640                 }
58641                 return [lbuf, buf];
58642             } :
58643             function(cs, rs, ds, startRow, colCount, stripe){
58644                 var ts = this.templates, ct = ts.cell, rt = ts.row;
58645                 // buffers
58646                 var buf = [], lbuf = [], cb, lcb, c, p = {}, rp = {}, r, rowIndex;
58647                 var hasListener = this.grid.hasListener('rowclass');
58648  
58649                 var rowcfg = {};
58650                 for(var j = 0, len = rs.length; j < len; j++){
58651                     r = rs[j]; cb = []; lcb = []; rowIndex = (j+startRow);
58652                     for(var i = 0; i < colCount; i++){
58653                         c = cs[i];
58654                         p.cellId = "x-grid-cell-" + rowIndex + "-" + i;
58655                         p.id = c.id;
58656                         p.css = p.attr = "";
58657                         p.value = c.renderer(r.data[c.name], p, r, rowIndex, i, ds);
58658                         if(p.value == undefined || p.value === "") {
58659                             p.value = "&#160;";
58660                         }
58661                         //Roo.log(c);
58662                          if(c.has_editor){
58663                             p.css += ' x-grid-editable-cell';
58664                         }
58665                         if(r.dirty && typeof r.modified[c.name] !== 'undefined'){
58666                             p.css += ' x-grid-dirty-cell' 
58667                         }
58668                         
58669                         var markup = ct.apply(p);
58670                         if(!c.locked){
58671                             cb[cb.length] = markup;
58672                         }else{
58673                             lcb[lcb.length] = markup;
58674                         }
58675                     }
58676                     var alt = [];
58677                     if(stripe && ((rowIndex+1) % 2 == 0)){
58678                         alt.push( "x-grid-row-alt");
58679                     }
58680                     if(r.dirty){
58681                         alt.push(" x-grid-dirty-row");
58682                     }
58683                     rp.cells = lcb;
58684                     if(this.getRowClass){
58685                         alt.push( this.getRowClass(r, rowIndex));
58686                     }
58687                     if (hasListener) {
58688                         rowcfg = {
58689                              
58690                             record: r,
58691                             rowIndex : rowIndex,
58692                             rowClass : ''
58693                         };
58694                         this.grid.fireEvent('rowclass', this, rowcfg);
58695                         alt.push(rowcfg.rowClass);
58696                     }
58697                     
58698                     rp.alt = alt.join(" ");
58699                     rp.cells = lcb.join("");
58700                     lbuf[lbuf.length] = rt.apply(rp);
58701                     rp.cells = cb.join("");
58702                     buf[buf.length] =  rt.apply(rp);
58703                 }
58704                 return [lbuf.join(""), buf.join("")];
58705             },
58706
58707     renderBody : function(){
58708         var markup = this.renderRows();
58709         var bt = this.templates.body;
58710         return [bt.apply({rows: markup[0]}), bt.apply({rows: markup[1]})];
58711     },
58712
58713     /**
58714      * Refreshes the grid
58715      * @param {Boolean} headersToo
58716      */
58717     refresh : function(headersToo){
58718         this.fireEvent("beforerefresh", this);
58719         this.grid.stopEditing();
58720         var result = this.renderBody();
58721         this.lockedBody.update(result[0]);
58722         this.mainBody.update(result[1]);
58723         if(headersToo === true){
58724             this.updateHeaders();
58725             this.updateColumns();
58726             this.updateSplitters();
58727             this.updateHeaderSortState();
58728         }
58729         this.syncRowHeights();
58730         this.layout();
58731         this.fireEvent("refresh", this);
58732     },
58733
58734     handleColumnMove : function(cm, oldIndex, newIndex){
58735         this.indexMap = null;
58736         var s = this.getScrollState();
58737         this.refresh(true);
58738         this.restoreScroll(s);
58739         this.afterMove(newIndex);
58740     },
58741
58742     afterMove : function(colIndex){
58743         if(this.enableMoveAnim && Roo.enableFx){
58744             this.fly(this.getHeaderCell(colIndex).firstChild).highlight(this.hlColor);
58745         }
58746         // if multisort - fix sortOrder, and reload..
58747         if (this.grid.dataSource.multiSort) {
58748             // the we can call sort again..
58749             var dm = this.grid.dataSource;
58750             var cm = this.grid.colModel;
58751             var so = [];
58752             for(var i = 0; i < cm.config.length; i++ ) {
58753                 
58754                 if ((typeof(dm.sortToggle[cm.config[i].dataIndex]) == 'undefined')) {
58755                     continue; // dont' bother, it's not in sort list or being set.
58756                 }
58757                 
58758                 so.push(cm.config[i].dataIndex);
58759             };
58760             dm.sortOrder = so;
58761             dm.load(dm.lastOptions);
58762             
58763             
58764         }
58765         
58766     },
58767
58768     updateCell : function(dm, rowIndex, dataIndex){
58769         var colIndex = this.getColumnIndexByDataIndex(dataIndex);
58770         if(typeof colIndex == "undefined"){ // not present in grid
58771             return;
58772         }
58773         var cm = this.grid.colModel;
58774         var cell = this.getCell(rowIndex, colIndex);
58775         var cellText = this.getCellText(rowIndex, colIndex);
58776
58777         var p = {
58778             cellId : "x-grid-cell-" + rowIndex + "-" + colIndex,
58779             id : cm.getColumnId(colIndex),
58780             css: colIndex == cm.getColumnCount()-1 ? "x-grid-col-last" : ""
58781         };
58782         var renderer = cm.getRenderer(colIndex);
58783         var val = renderer(dm.getValueAt(rowIndex, dataIndex), p, rowIndex, colIndex, dm);
58784         if(typeof val == "undefined" || val === "") {
58785             val = "&#160;";
58786         }
58787         cellText.innerHTML = val;
58788         cell.className = this.cellClass + " " + this.idToCssName(p.cellId) + " " + p.css;
58789         this.syncRowHeights(rowIndex, rowIndex);
58790     },
58791
58792     calcColumnWidth : function(colIndex, maxRowsToMeasure){
58793         var maxWidth = 0;
58794         if(this.grid.autoSizeHeaders){
58795             var h = this.getHeaderCellMeasure(colIndex);
58796             maxWidth = Math.max(maxWidth, h.scrollWidth);
58797         }
58798         var tb, index;
58799         if(this.cm.isLocked(colIndex)){
58800             tb = this.getLockedTable();
58801             index = colIndex;
58802         }else{
58803             tb = this.getBodyTable();
58804             index = colIndex - this.cm.getLockedCount();
58805         }
58806         if(tb && tb.rows){
58807             var rows = tb.rows;
58808             var stopIndex = Math.min(maxRowsToMeasure || rows.length, rows.length);
58809             for(var i = 0; i < stopIndex; i++){
58810                 var cell = rows[i].childNodes[index].firstChild;
58811                 maxWidth = Math.max(maxWidth, cell.scrollWidth);
58812             }
58813         }
58814         return maxWidth + /*margin for error in IE*/ 5;
58815     },
58816     /**
58817      * Autofit a column to its content.
58818      * @param {Number} colIndex
58819      * @param {Boolean} forceMinSize true to force the column to go smaller if possible
58820      */
58821      autoSizeColumn : function(colIndex, forceMinSize, suppressEvent){
58822          if(this.cm.isHidden(colIndex)){
58823              return; // can't calc a hidden column
58824          }
58825         if(forceMinSize){
58826             var cid = this.cm.getColumnId(colIndex);
58827             this.css.updateRule(this.colSelector +this.idToCssName( cid), "width", this.grid.minColumnWidth + "px");
58828            if(this.grid.autoSizeHeaders){
58829                this.css.updateRule(this.hdSelector + this.idToCssName(cid), "width", this.grid.minColumnWidth + "px");
58830            }
58831         }
58832         var newWidth = this.calcColumnWidth(colIndex);
58833         this.cm.setColumnWidth(colIndex,
58834             Math.max(this.grid.minColumnWidth, newWidth), suppressEvent);
58835         if(!suppressEvent){
58836             this.grid.fireEvent("columnresize", colIndex, newWidth);
58837         }
58838     },
58839
58840     /**
58841      * Autofits all columns to their content and then expands to fit any extra space in the grid
58842      */
58843      autoSizeColumns : function(){
58844         var cm = this.grid.colModel;
58845         var colCount = cm.getColumnCount();
58846         for(var i = 0; i < colCount; i++){
58847             this.autoSizeColumn(i, true, true);
58848         }
58849         if(cm.getTotalWidth() < this.scroller.dom.clientWidth){
58850             this.fitColumns();
58851         }else{
58852             this.updateColumns();
58853             this.layout();
58854         }
58855     },
58856
58857     /**
58858      * Autofits all columns to the grid's width proportionate with their current size
58859      * @param {Boolean} reserveScrollSpace Reserve space for a scrollbar
58860      */
58861     fitColumns : function(reserveScrollSpace){
58862         var cm = this.grid.colModel;
58863         var colCount = cm.getColumnCount();
58864         var cols = [];
58865         var width = 0;
58866         var i, w;
58867         for (i = 0; i < colCount; i++){
58868             if(!cm.isHidden(i) && !cm.isFixed(i)){
58869                 w = cm.getColumnWidth(i);
58870                 cols.push(i);
58871                 cols.push(w);
58872                 width += w;
58873             }
58874         }
58875         var avail = Math.min(this.scroller.dom.clientWidth, this.el.getWidth());
58876         if(reserveScrollSpace){
58877             avail -= 17;
58878         }
58879         var frac = (avail - cm.getTotalWidth())/width;
58880         while (cols.length){
58881             w = cols.pop();
58882             i = cols.pop();
58883             cm.setColumnWidth(i, Math.floor(w + w*frac), true);
58884         }
58885         this.updateColumns();
58886         this.layout();
58887     },
58888
58889     onRowSelect : function(rowIndex){
58890         var row = this.getRowComposite(rowIndex);
58891         row.addClass("x-grid-row-selected");
58892     },
58893
58894     onRowDeselect : function(rowIndex){
58895         var row = this.getRowComposite(rowIndex);
58896         row.removeClass("x-grid-row-selected");
58897     },
58898
58899     onCellSelect : function(row, col){
58900         var cell = this.getCell(row, col);
58901         if(cell){
58902             Roo.fly(cell).addClass("x-grid-cell-selected");
58903         }
58904     },
58905
58906     onCellDeselect : function(row, col){
58907         var cell = this.getCell(row, col);
58908         if(cell){
58909             Roo.fly(cell).removeClass("x-grid-cell-selected");
58910         }
58911     },
58912
58913     updateHeaderSortState : function(){
58914         
58915         // sort state can be single { field: xxx, direction : yyy}
58916         // or   { xxx=>ASC , yyy : DESC ..... }
58917         
58918         var mstate = {};
58919         if (!this.ds.multiSort) { 
58920             var state = this.ds.getSortState();
58921             if(!state){
58922                 return;
58923             }
58924             mstate[state.field] = state.direction;
58925             // FIXME... - this is not used here.. but might be elsewhere..
58926             this.sortState = state;
58927             
58928         } else {
58929             mstate = this.ds.sortToggle;
58930         }
58931         //remove existing sort classes..
58932         
58933         var sc = this.sortClasses;
58934         var hds = this.el.select(this.headerSelector).removeClass(sc);
58935         
58936         for(var f in mstate) {
58937         
58938             var sortColumn = this.cm.findColumnIndex(f);
58939             
58940             if(sortColumn != -1){
58941                 var sortDir = mstate[f];        
58942                 hds.item(sortColumn).addClass(sc[sortDir == "DESC" ? 1 : 0]);
58943             }
58944         }
58945         
58946          
58947         
58948     },
58949
58950
58951     handleHeaderClick : function(g, index,e){
58952         
58953         Roo.log("header click");
58954         
58955         if (Roo.isTouch) {
58956             // touch events on header are handled by context
58957             this.handleHdCtx(g,index,e);
58958             return;
58959         }
58960         
58961         
58962         if(this.headersDisabled){
58963             return;
58964         }
58965         var dm = g.dataSource, cm = g.colModel;
58966         if(!cm.isSortable(index)){
58967             return;
58968         }
58969         g.stopEditing();
58970         
58971         if (dm.multiSort) {
58972             // update the sortOrder
58973             var so = [];
58974             for(var i = 0; i < cm.config.length; i++ ) {
58975                 
58976                 if ((typeof(dm.sortToggle[cm.config[i].dataIndex]) == 'undefined') && (index != i)) {
58977                     continue; // dont' bother, it's not in sort list or being set.
58978                 }
58979                 
58980                 so.push(cm.config[i].dataIndex);
58981             };
58982             dm.sortOrder = so;
58983         }
58984         
58985         
58986         dm.sort(cm.getDataIndex(index));
58987     },
58988
58989
58990     destroy : function(){
58991         if(this.colMenu){
58992             this.colMenu.removeAll();
58993             Roo.menu.MenuMgr.unregister(this.colMenu);
58994             this.colMenu.getEl().remove();
58995             delete this.colMenu;
58996         }
58997         if(this.hmenu){
58998             this.hmenu.removeAll();
58999             Roo.menu.MenuMgr.unregister(this.hmenu);
59000             this.hmenu.getEl().remove();
59001             delete this.hmenu;
59002         }
59003         if(this.grid.enableColumnMove){
59004             var dds = Roo.dd.DDM.ids['gridHeader' + this.grid.getGridEl().id];
59005             if(dds){
59006                 for(var dd in dds){
59007                     if(!dds[dd].config.isTarget && dds[dd].dragElId){
59008                         var elid = dds[dd].dragElId;
59009                         dds[dd].unreg();
59010                         Roo.get(elid).remove();
59011                     } else if(dds[dd].config.isTarget){
59012                         dds[dd].proxyTop.remove();
59013                         dds[dd].proxyBottom.remove();
59014                         dds[dd].unreg();
59015                     }
59016                     if(Roo.dd.DDM.locationCache[dd]){
59017                         delete Roo.dd.DDM.locationCache[dd];
59018                     }
59019                 }
59020                 delete Roo.dd.DDM.ids['gridHeader' + this.grid.getGridEl().id];
59021             }
59022         }
59023         Roo.util.CSS.removeStyleSheet(this.idToCssName(this.grid.id) + '-cssrules');
59024         this.bind(null, null);
59025         Roo.EventManager.removeResizeListener(this.onWindowResize, this);
59026     },
59027
59028     handleLockChange : function(){
59029         this.refresh(true);
59030     },
59031
59032     onDenyColumnLock : function(){
59033
59034     },
59035
59036     onDenyColumnHide : function(){
59037
59038     },
59039
59040     handleHdMenuClick : function(item){
59041         var index = this.hdCtxIndex;
59042         var cm = this.cm, ds = this.ds;
59043         switch(item.id){
59044             case "asc":
59045                 ds.sort(cm.getDataIndex(index), "ASC");
59046                 break;
59047             case "desc":
59048                 ds.sort(cm.getDataIndex(index), "DESC");
59049                 break;
59050             case "lock":
59051                 var lc = cm.getLockedCount();
59052                 if(cm.getColumnCount(true) <= lc+1){
59053                     this.onDenyColumnLock();
59054                     return;
59055                 }
59056                 if(lc != index){
59057                     cm.setLocked(index, true, true);
59058                     cm.moveColumn(index, lc);
59059                     this.grid.fireEvent("columnmove", index, lc);
59060                 }else{
59061                     cm.setLocked(index, true);
59062                 }
59063             break;
59064             case "unlock":
59065                 var lc = cm.getLockedCount();
59066                 if((lc-1) != index){
59067                     cm.setLocked(index, false, true);
59068                     cm.moveColumn(index, lc-1);
59069                     this.grid.fireEvent("columnmove", index, lc-1);
59070                 }else{
59071                     cm.setLocked(index, false);
59072                 }
59073             break;
59074             case 'wider': // used to expand cols on touch..
59075             case 'narrow':
59076                 var cw = cm.getColumnWidth(index);
59077                 cw += (item.id == 'wider' ? 1 : -1) * 50;
59078                 cw = Math.max(0, cw);
59079                 cw = Math.min(cw,4000);
59080                 cm.setColumnWidth(index, cw);
59081                 break;
59082                 
59083             default:
59084                 index = cm.getIndexById(item.id.substr(4));
59085                 if(index != -1){
59086                     if(item.checked && cm.getColumnCount(true) <= 1){
59087                         this.onDenyColumnHide();
59088                         return false;
59089                     }
59090                     cm.setHidden(index, item.checked);
59091                 }
59092         }
59093         return true;
59094     },
59095
59096     beforeColMenuShow : function(){
59097         var cm = this.cm,  colCount = cm.getColumnCount();
59098         this.colMenu.removeAll();
59099         for(var i = 0; i < colCount; i++){
59100             this.colMenu.add(new Roo.menu.CheckItem({
59101                 id: "col-"+cm.getColumnId(i),
59102                 text: cm.getColumnHeader(i),
59103                 checked: !cm.isHidden(i),
59104                 hideOnClick:false
59105             }));
59106         }
59107     },
59108
59109     handleHdCtx : function(g, index, e){
59110         e.stopEvent();
59111         var hd = this.getHeaderCell(index);
59112         this.hdCtxIndex = index;
59113         var ms = this.hmenu.items, cm = this.cm;
59114         ms.get("asc").setDisabled(!cm.isSortable(index));
59115         ms.get("desc").setDisabled(!cm.isSortable(index));
59116         if(this.grid.enableColLock !== false){
59117             ms.get("lock").setDisabled(cm.isLocked(index));
59118             ms.get("unlock").setDisabled(!cm.isLocked(index));
59119         }
59120         this.hmenu.show(hd, "tl-bl");
59121     },
59122
59123     handleHdOver : function(e){
59124         var hd = this.findHeaderCell(e.getTarget());
59125         if(hd && !this.headersDisabled){
59126             if(this.grid.colModel.isSortable(this.getCellIndex(hd))){
59127                this.fly(hd).addClass("x-grid-hd-over");
59128             }
59129         }
59130     },
59131
59132     handleHdOut : function(e){
59133         var hd = this.findHeaderCell(e.getTarget());
59134         if(hd){
59135             this.fly(hd).removeClass("x-grid-hd-over");
59136         }
59137     },
59138
59139     handleSplitDblClick : function(e, t){
59140         var i = this.getCellIndex(t);
59141         if(this.grid.enableColumnResize !== false && this.cm.isResizable(i) && !this.cm.isFixed(i)){
59142             this.autoSizeColumn(i, true);
59143             this.layout();
59144         }
59145     },
59146
59147     render : function(){
59148
59149         var cm = this.cm;
59150         var colCount = cm.getColumnCount();
59151
59152         if(this.grid.monitorWindowResize === true){
59153             Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
59154         }
59155         var header = this.renderHeaders();
59156         var body = this.templates.body.apply({rows:""});
59157         var html = this.templates.master.apply({
59158             lockedBody: body,
59159             body: body,
59160             lockedHeader: header[0],
59161             header: header[1]
59162         });
59163
59164         //this.updateColumns();
59165
59166         this.grid.getGridEl().dom.innerHTML = html;
59167
59168         this.initElements();
59169         
59170         // a kludge to fix the random scolling effect in webkit
59171         this.el.on("scroll", function() {
59172             this.el.dom.scrollTop=0; // hopefully not recursive..
59173         },this);
59174
59175         this.scroller.on("scroll", this.handleScroll, this);
59176         this.lockedBody.on("mousewheel", this.handleWheel, this);
59177         this.mainBody.on("mousewheel", this.handleWheel, this);
59178
59179         this.mainHd.on("mouseover", this.handleHdOver, this);
59180         this.mainHd.on("mouseout", this.handleHdOut, this);
59181         this.mainHd.on("dblclick", this.handleSplitDblClick, this,
59182                 {delegate: "."+this.splitClass});
59183
59184         this.lockedHd.on("mouseover", this.handleHdOver, this);
59185         this.lockedHd.on("mouseout", this.handleHdOut, this);
59186         this.lockedHd.on("dblclick", this.handleSplitDblClick, this,
59187                 {delegate: "."+this.splitClass});
59188
59189         if(this.grid.enableColumnResize !== false && Roo.grid.SplitDragZone){
59190             new Roo.grid.SplitDragZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
59191         }
59192
59193         this.updateSplitters();
59194
59195         if(this.grid.enableColumnMove && Roo.grid.HeaderDragZone){
59196             new Roo.grid.HeaderDragZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
59197             new Roo.grid.HeaderDropZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
59198         }
59199
59200         if(this.grid.enableCtxMenu !== false && Roo.menu.Menu){
59201             this.hmenu = new Roo.menu.Menu({id: this.grid.id + "-hctx"});
59202             this.hmenu.add(
59203                 {id:"asc", text: this.sortAscText, cls: "xg-hmenu-sort-asc"},
59204                 {id:"desc", text: this.sortDescText, cls: "xg-hmenu-sort-desc"}
59205             );
59206             if(this.grid.enableColLock !== false){
59207                 this.hmenu.add('-',
59208                     {id:"lock", text: this.lockText, cls: "xg-hmenu-lock"},
59209                     {id:"unlock", text: this.unlockText, cls: "xg-hmenu-unlock"}
59210                 );
59211             }
59212             if (Roo.isTouch) {
59213                  this.hmenu.add('-',
59214                     {id:"wider", text: this.columnsWiderText},
59215                     {id:"narrow", text: this.columnsNarrowText }
59216                 );
59217                 
59218                  
59219             }
59220             
59221             if(this.grid.enableColumnHide !== false){
59222
59223                 this.colMenu = new Roo.menu.Menu({id:this.grid.id + "-hcols-menu"});
59224                 this.colMenu.on("beforeshow", this.beforeColMenuShow, this);
59225                 this.colMenu.on("itemclick", this.handleHdMenuClick, this);
59226
59227                 this.hmenu.add('-',
59228                     {id:"columns", text: this.columnsText, menu: this.colMenu}
59229                 );
59230             }
59231             this.hmenu.on("itemclick", this.handleHdMenuClick, this);
59232
59233             this.grid.on("headercontextmenu", this.handleHdCtx, this);
59234         }
59235
59236         if((this.grid.enableDragDrop || this.grid.enableDrag) && Roo.grid.GridDragZone){
59237             this.dd = new Roo.grid.GridDragZone(this.grid, {
59238                 ddGroup : this.grid.ddGroup || 'GridDD'
59239             });
59240             
59241         }
59242
59243         /*
59244         for(var i = 0; i < colCount; i++){
59245             if(cm.isHidden(i)){
59246                 this.hideColumn(i);
59247             }
59248             if(cm.config[i].align){
59249                 this.css.updateRule(this.colSelector + i, "textAlign", cm.config[i].align);
59250                 this.css.updateRule(this.hdSelector + i, "textAlign", cm.config[i].align);
59251             }
59252         }*/
59253         
59254         this.updateHeaderSortState();
59255
59256         this.beforeInitialResize();
59257         this.layout(true);
59258
59259         // two part rendering gives faster view to the user
59260         this.renderPhase2.defer(1, this);
59261     },
59262
59263     renderPhase2 : function(){
59264         // render the rows now
59265         this.refresh();
59266         if(this.grid.autoSizeColumns){
59267             this.autoSizeColumns();
59268         }
59269     },
59270
59271     beforeInitialResize : function(){
59272
59273     },
59274
59275     onColumnSplitterMoved : function(i, w){
59276         this.userResized = true;
59277         var cm = this.grid.colModel;
59278         cm.setColumnWidth(i, w, true);
59279         var cid = cm.getColumnId(i);
59280         this.css.updateRule(this.colSelector + this.idToCssName(cid), "width", (w-this.borderWidth) + "px");
59281         this.css.updateRule(this.hdSelector + this.idToCssName(cid), "width", (w-this.borderWidth) + "px");
59282         this.updateSplitters();
59283         this.layout();
59284         this.grid.fireEvent("columnresize", i, w);
59285     },
59286
59287     syncRowHeights : function(startIndex, endIndex){
59288         if(this.grid.enableRowHeightSync === true && this.cm.getLockedCount() > 0){
59289             startIndex = startIndex || 0;
59290             var mrows = this.getBodyTable().rows;
59291             var lrows = this.getLockedTable().rows;
59292             var len = mrows.length-1;
59293             endIndex = Math.min(endIndex || len, len);
59294             for(var i = startIndex; i <= endIndex; i++){
59295                 var m = mrows[i], l = lrows[i];
59296                 var h = Math.max(m.offsetHeight, l.offsetHeight);
59297                 m.style.height = l.style.height = h + "px";
59298             }
59299         }
59300     },
59301
59302     layout : function(initialRender, is2ndPass)
59303     {
59304         var g = this.grid;
59305         var auto = g.autoHeight;
59306         var scrollOffset = 16;
59307         var c = g.getGridEl(), cm = this.cm,
59308                 expandCol = g.autoExpandColumn,
59309                 gv = this;
59310         //c.beginMeasure();
59311
59312         if(!c.dom.offsetWidth){ // display:none?
59313             if(initialRender){
59314                 this.lockedWrap.show();
59315                 this.mainWrap.show();
59316             }
59317             return;
59318         }
59319
59320         var hasLock = this.cm.isLocked(0);
59321
59322         var tbh = this.headerPanel.getHeight();
59323         var bbh = this.footerPanel.getHeight();
59324
59325         if(auto){
59326             var ch = this.getBodyTable().offsetHeight + tbh + bbh + this.mainHd.getHeight();
59327             var newHeight = ch + c.getBorderWidth("tb");
59328             if(g.maxHeight){
59329                 newHeight = Math.min(g.maxHeight, newHeight);
59330             }
59331             c.setHeight(newHeight);
59332         }
59333
59334         if(g.autoWidth){
59335             c.setWidth(cm.getTotalWidth()+c.getBorderWidth('lr'));
59336         }
59337
59338         var s = this.scroller;
59339
59340         var csize = c.getSize(true);
59341
59342         this.el.setSize(csize.width, csize.height);
59343
59344         this.headerPanel.setWidth(csize.width);
59345         this.footerPanel.setWidth(csize.width);
59346
59347         var hdHeight = this.mainHd.getHeight();
59348         var vw = csize.width;
59349         var vh = csize.height - (tbh + bbh);
59350
59351         s.setSize(vw, vh);
59352
59353         var bt = this.getBodyTable();
59354         
59355         if(cm.getLockedCount() == cm.config.length){
59356             bt = this.getLockedTable();
59357         }
59358         
59359         var ltWidth = hasLock ?
59360                       Math.max(this.getLockedTable().offsetWidth, this.lockedHd.dom.firstChild.offsetWidth) : 0;
59361
59362         var scrollHeight = bt.offsetHeight;
59363         var scrollWidth = ltWidth + bt.offsetWidth;
59364         var vscroll = false, hscroll = false;
59365
59366         this.scrollSizer.setSize(scrollWidth, scrollHeight+hdHeight);
59367
59368         var lw = this.lockedWrap, mw = this.mainWrap;
59369         var lb = this.lockedBody, mb = this.mainBody;
59370
59371         setTimeout(function(){
59372             var t = s.dom.offsetTop;
59373             var w = s.dom.clientWidth,
59374                 h = s.dom.clientHeight;
59375
59376             lw.setTop(t);
59377             lw.setSize(ltWidth, h);
59378
59379             mw.setLeftTop(ltWidth, t);
59380             mw.setSize(w-ltWidth, h);
59381
59382             lb.setHeight(h-hdHeight);
59383             mb.setHeight(h-hdHeight);
59384
59385             if(is2ndPass !== true && !gv.userResized && expandCol){
59386                 // high speed resize without full column calculation
59387                 
59388                 var ci = cm.getIndexById(expandCol);
59389                 if (ci < 0) {
59390                     ci = cm.findColumnIndex(expandCol);
59391                 }
59392                 ci = Math.max(0, ci); // make sure it's got at least the first col.
59393                 var expandId = cm.getColumnId(ci);
59394                 var  tw = cm.getTotalWidth(false);
59395                 var currentWidth = cm.getColumnWidth(ci);
59396                 var cw = Math.min(Math.max(((w-tw)+currentWidth-2)-/*scrollbar*/(w <= s.dom.offsetWidth ? 0 : 18), g.autoExpandMin), g.autoExpandMax);
59397                 if(currentWidth != cw){
59398                     cm.setColumnWidth(ci, cw, true);
59399                     gv.css.updateRule(gv.colSelector+gv.idToCssName(expandId), "width", (cw - gv.borderWidth) + "px");
59400                     gv.css.updateRule(gv.hdSelector+gv.idToCssName(expandId), "width", (cw - gv.borderWidth) + "px");
59401                     gv.updateSplitters();
59402                     gv.layout(false, true);
59403                 }
59404             }
59405
59406             if(initialRender){
59407                 lw.show();
59408                 mw.show();
59409             }
59410             //c.endMeasure();
59411         }, 10);
59412     },
59413
59414     onWindowResize : function(){
59415         if(!this.grid.monitorWindowResize || this.grid.autoHeight){
59416             return;
59417         }
59418         this.layout();
59419     },
59420
59421     appendFooter : function(parentEl){
59422         return null;
59423     },
59424
59425     sortAscText : "Sort Ascending",
59426     sortDescText : "Sort Descending",
59427     lockText : "Lock Column",
59428     unlockText : "Unlock Column",
59429     columnsText : "Columns",
59430  
59431     columnsWiderText : "Wider",
59432     columnsNarrowText : "Thinner"
59433 });
59434
59435
59436 Roo.grid.GridView.ColumnDragZone = function(grid, hd){
59437     Roo.grid.GridView.ColumnDragZone.superclass.constructor.call(this, grid, hd, null);
59438     this.proxy.el.addClass('x-grid3-col-dd');
59439 };
59440
59441 Roo.extend(Roo.grid.GridView.ColumnDragZone, Roo.grid.HeaderDragZone, {
59442     handleMouseDown : function(e){
59443
59444     },
59445
59446     callHandleMouseDown : function(e){
59447         Roo.grid.GridView.ColumnDragZone.superclass.handleMouseDown.call(this, e);
59448     }
59449 });
59450 /*
59451  * Based on:
59452  * Ext JS Library 1.1.1
59453  * Copyright(c) 2006-2007, Ext JS, LLC.
59454  *
59455  * Originally Released Under LGPL - original licence link has changed is not relivant.
59456  *
59457  * Fork - LGPL
59458  * <script type="text/javascript">
59459  */
59460  /**
59461  * @extends Roo.dd.DDProxy
59462  * @class Roo.grid.SplitDragZone
59463  * Support for Column Header resizing
59464  * @constructor
59465  * @param {Object} config
59466  */
59467 // private
59468 // This is a support class used internally by the Grid components
59469 Roo.grid.SplitDragZone = function(grid, hd, hd2){
59470     this.grid = grid;
59471     this.view = grid.getView();
59472     this.proxy = this.view.resizeProxy;
59473     Roo.grid.SplitDragZone.superclass.constructor.call(
59474         this,
59475         hd, // ID
59476         "gridSplitters" + this.grid.getGridEl().id, // SGROUP
59477         {  // CONFIG
59478             dragElId : Roo.id(this.proxy.dom),
59479             resizeFrame:false
59480         }
59481     );
59482     
59483     this.setHandleElId(Roo.id(hd));
59484     if (hd2 !== false) {
59485         this.setOuterHandleElId(Roo.id(hd2));
59486     }
59487     
59488     this.scroll = false;
59489 };
59490 Roo.extend(Roo.grid.SplitDragZone, Roo.dd.DDProxy, {
59491     fly: Roo.Element.fly,
59492
59493     b4StartDrag : function(x, y){
59494         this.view.headersDisabled = true;
59495         var h = this.view.mainWrap ? this.view.mainWrap.getHeight() : (
59496                     this.view.headEl.getHeight() + this.view.bodyEl.getHeight()
59497         );
59498         this.proxy.setHeight(h);
59499         
59500         // for old system colWidth really stored the actual width?
59501         // in bootstrap we tried using xs/ms/etc.. to do % sizing?
59502         // which in reality did not work.. - it worked only for fixed sizes
59503         // for resizable we need to use actual sizes.
59504         var w = this.cm.getColumnWidth(this.cellIndex);
59505         if (!this.view.mainWrap) {
59506             // bootstrap.
59507             w = this.view.getHeaderIndex(this.cellIndex).getWidth();
59508         }
59509         
59510         
59511         
59512         // this was w-this.grid.minColumnWidth;
59513         // doesnt really make sense? - w = thie curren width or the rendered one?
59514         var minw = Math.max(w-this.grid.minColumnWidth, 0);
59515         this.resetConstraints();
59516         this.setXConstraint(minw, 1000);
59517         this.setYConstraint(0, 0);
59518         this.minX = x - minw;
59519         this.maxX = x + 1000;
59520         this.startPos = x;
59521         if (!this.view.mainWrap) { // this is Bootstrap code..
59522             this.getDragEl().style.display='block';
59523         }
59524         
59525         Roo.dd.DDProxy.prototype.b4StartDrag.call(this, x, y);
59526     },
59527
59528
59529     handleMouseDown : function(e){
59530         ev = Roo.EventObject.setEvent(e);
59531         var t = this.fly(ev.getTarget());
59532         if(t.hasClass("x-grid-split")){
59533             this.cellIndex = this.view.getCellIndex(t.dom);
59534             this.split = t.dom;
59535             this.cm = this.grid.colModel;
59536             if(this.cm.isResizable(this.cellIndex) && !this.cm.isFixed(this.cellIndex)){
59537                 Roo.grid.SplitDragZone.superclass.handleMouseDown.apply(this, arguments);
59538             }
59539         }
59540     },
59541
59542     endDrag : function(e){
59543         this.view.headersDisabled = false;
59544         var endX = Math.max(this.minX, Roo.lib.Event.getPageX(e));
59545         var diff = endX - this.startPos;
59546         // 
59547         var w = this.cm.getColumnWidth(this.cellIndex);
59548         if (!this.view.mainWrap) {
59549             w = 0;
59550         }
59551         this.view.onColumnSplitterMoved(this.cellIndex, w+diff);
59552     },
59553
59554     autoOffset : function(){
59555         this.setDelta(0,0);
59556     }
59557 });/*
59558  * Based on:
59559  * Ext JS Library 1.1.1
59560  * Copyright(c) 2006-2007, Ext JS, LLC.
59561  *
59562  * Originally Released Under LGPL - original licence link has changed is not relivant.
59563  *
59564  * Fork - LGPL
59565  * <script type="text/javascript">
59566  */
59567  
59568 // private
59569 // This is a support class used internally by the Grid components
59570 Roo.grid.GridDragZone = function(grid, config){
59571     this.view = grid.getView();
59572     Roo.grid.GridDragZone.superclass.constructor.call(this, this.view.mainBody.dom, config);
59573     if(this.view.lockedBody){
59574         this.setHandleElId(Roo.id(this.view.mainBody.dom));
59575         this.setOuterHandleElId(Roo.id(this.view.lockedBody.dom));
59576     }
59577     this.scroll = false;
59578     this.grid = grid;
59579     this.ddel = document.createElement('div');
59580     this.ddel.className = 'x-grid-dd-wrap';
59581 };
59582
59583 Roo.extend(Roo.grid.GridDragZone, Roo.dd.DragZone, {
59584     ddGroup : "GridDD",
59585
59586     getDragData : function(e){
59587         var t = Roo.lib.Event.getTarget(e);
59588         var rowIndex = this.view.findRowIndex(t);
59589         var sm = this.grid.selModel;
59590             
59591         //Roo.log(rowIndex);
59592         
59593         if (sm.getSelectedCell) {
59594             // cell selection..
59595             if (!sm.getSelectedCell()) {
59596                 return false;
59597             }
59598             if (rowIndex != sm.getSelectedCell()[0]) {
59599                 return false;
59600             }
59601         
59602         }
59603         if (sm.getSelections && sm.getSelections().length < 1) {
59604             return false;
59605         }
59606         
59607         
59608         // before it used to all dragging of unseleted... - now we dont do that.
59609         if(rowIndex !== false){
59610             
59611             // if editorgrid.. 
59612             
59613             
59614             //Roo.log([ sm.getSelectedCell() ? sm.getSelectedCell()[0] : 'NO' , rowIndex ]);
59615                
59616             //if(!sm.isSelected(rowIndex) || e.hasModifier()){
59617               //  
59618             //}
59619             if (e.hasModifier()){
59620                 sm.handleMouseDown(e, t); // non modifier buttons are handled by row select.
59621             }
59622             
59623             Roo.log("getDragData");
59624             
59625             return {
59626                 grid: this.grid,
59627                 ddel: this.ddel,
59628                 rowIndex: rowIndex,
59629                 selections: sm.getSelections ? sm.getSelections() : (
59630                     sm.getSelectedCell() ? [ this.grid.ds.getAt(sm.getSelectedCell()[0]) ] : [])
59631             };
59632         }
59633         return false;
59634     },
59635     
59636     
59637     onInitDrag : function(e){
59638         var data = this.dragData;
59639         this.ddel.innerHTML = this.grid.getDragDropText();
59640         this.proxy.update(this.ddel);
59641         // fire start drag?
59642     },
59643
59644     afterRepair : function(){
59645         this.dragging = false;
59646     },
59647
59648     getRepairXY : function(e, data){
59649         return false;
59650     },
59651
59652     onEndDrag : function(data, e){
59653         // fire end drag?
59654     },
59655
59656     onValidDrop : function(dd, e, id){
59657         // fire drag drop?
59658         this.hideProxy();
59659     },
59660
59661     beforeInvalidDrop : function(e, id){
59662
59663     }
59664 });/*
59665  * Based on:
59666  * Ext JS Library 1.1.1
59667  * Copyright(c) 2006-2007, Ext JS, LLC.
59668  *
59669  * Originally Released Under LGPL - original licence link has changed is not relivant.
59670  *
59671  * Fork - LGPL
59672  * <script type="text/javascript">
59673  */
59674  
59675
59676 /**
59677  * @class Roo.grid.ColumnModel
59678  * @extends Roo.util.Observable
59679  * This is the default implementation of a ColumnModel used by the Grid. It defines
59680  * the columns in the grid.
59681  * <br>Usage:<br>
59682  <pre><code>
59683  var colModel = new Roo.grid.ColumnModel([
59684         {header: "Ticker", width: 60, sortable: true, locked: true},
59685         {header: "Company Name", width: 150, sortable: true},
59686         {header: "Market Cap.", width: 100, sortable: true},
59687         {header: "$ Sales", width: 100, sortable: true, renderer: money},
59688         {header: "Employees", width: 100, sortable: true, resizable: false}
59689  ]);
59690  </code></pre>
59691  * <p>
59692  
59693  * The config options listed for this class are options which may appear in each
59694  * individual column definition.
59695  * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
59696  * @constructor
59697  * @param {Object} config An Array of column config objects. See this class's
59698  * config objects for details.
59699 */
59700 Roo.grid.ColumnModel = function(config){
59701         /**
59702      * The config passed into the constructor
59703      */
59704     this.config = []; //config;
59705     this.lookup = {};
59706
59707     // if no id, create one
59708     // if the column does not have a dataIndex mapping,
59709     // map it to the order it is in the config
59710     for(var i = 0, len = config.length; i < len; i++){
59711         this.addColumn(config[i]);
59712         
59713     }
59714
59715     /**
59716      * The width of columns which have no width specified (defaults to 100)
59717      * @type Number
59718      */
59719     this.defaultWidth = 100;
59720
59721     /**
59722      * Default sortable of columns which have no sortable specified (defaults to false)
59723      * @type Boolean
59724      */
59725     this.defaultSortable = false;
59726
59727     this.addEvents({
59728         /**
59729              * @event widthchange
59730              * Fires when the width of a column changes.
59731              * @param {ColumnModel} this
59732              * @param {Number} columnIndex The column index
59733              * @param {Number} newWidth The new width
59734              */
59735             "widthchange": true,
59736         /**
59737              * @event headerchange
59738              * Fires when the text of a header changes.
59739              * @param {ColumnModel} this
59740              * @param {Number} columnIndex The column index
59741              * @param {Number} newText The new header text
59742              */
59743             "headerchange": true,
59744         /**
59745              * @event hiddenchange
59746              * Fires when a column is hidden or "unhidden".
59747              * @param {ColumnModel} this
59748              * @param {Number} columnIndex The column index
59749              * @param {Boolean} hidden true if hidden, false otherwise
59750              */
59751             "hiddenchange": true,
59752             /**
59753          * @event columnmoved
59754          * Fires when a column is moved.
59755          * @param {ColumnModel} this
59756          * @param {Number} oldIndex
59757          * @param {Number} newIndex
59758          */
59759         "columnmoved" : true,
59760         /**
59761          * @event columlockchange
59762          * Fires when a column's locked state is changed
59763          * @param {ColumnModel} this
59764          * @param {Number} colIndex
59765          * @param {Boolean} locked true if locked
59766          */
59767         "columnlockchange" : true
59768     });
59769     Roo.grid.ColumnModel.superclass.constructor.call(this);
59770 };
59771 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
59772     /**
59773      * @cfg {String} header The header text to display in the Grid view.
59774      */
59775         /**
59776      * @cfg {String} xsHeader Header at Bootsrap Extra Small width (default for all)
59777      */
59778         /**
59779      * @cfg {String} smHeader Header at Bootsrap Small width
59780      */
59781         /**
59782      * @cfg {String} mdHeader Header at Bootsrap Medium width
59783      */
59784         /**
59785      * @cfg {String} lgHeader Header at Bootsrap Large width
59786      */
59787         /**
59788      * @cfg {String} xlHeader Header at Bootsrap extra Large width
59789      */
59790     /**
59791      * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
59792      * {@link Roo.data.Record} definition from which to draw the column's value. If not
59793      * specified, the column's index is used as an index into the Record's data Array.
59794      */
59795     /**
59796      * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
59797      * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
59798      */
59799     /**
59800      * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
59801      * Defaults to the value of the {@link #defaultSortable} property.
59802      * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
59803      */
59804     /**
59805      * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid.  Defaults to false.
59806      */
59807     /**
59808      * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed.  Defaults to false.
59809      */
59810     /**
59811      * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
59812      */
59813     /**
59814      * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
59815      */
59816     /**
59817      * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
59818      * given the cell's data value. See {@link #setRenderer}. If not specified, the
59819      * default renderer returns the escaped data value. If an object is returned (bootstrap only)
59820      * then it is treated as a Roo Component object instance, and it is rendered after the initial row is rendered
59821      */
59822        /**
59823      * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor 
59824      */
59825     /**
59826      * @cfg {String} align (Optional) Set the CSS text-align property of the column.  Defaults to undefined.
59827      */
59828     /**
59829      * @cfg {String} valign (Optional) Set the CSS vertical-align property of the column (eg. middle, top, bottom etc).  Defaults to undefined.
59830      */
59831     /**
59832      * @cfg {String} cursor (Optional)
59833      */
59834     /**
59835      * @cfg {String} tooltip (Optional)
59836      */
59837     /**
59838      * @cfg {Number} xs (Optional) can be '0' for hidden at this size (number less than 12)
59839      */
59840     /**
59841      * @cfg {Number} sm (Optional) can be '0' for hidden at this size (number less than 12)
59842      */
59843     /**
59844      * @cfg {Number} md (Optional) can be '0' for hidden at this size (number less than 12)
59845      */
59846     /**
59847      * @cfg {Number} lg (Optional) can be '0' for hidden at this size (number less than 12)
59848      */
59849         /**
59850      * @cfg {Number} xl (Optional) can be '0' for hidden at this size (number less than 12)
59851      */
59852     /**
59853      * Returns the id of the column at the specified index.
59854      * @param {Number} index The column index
59855      * @return {String} the id
59856      */
59857     getColumnId : function(index){
59858         return this.config[index].id;
59859     },
59860
59861     /**
59862      * Returns the column for a specified id.
59863      * @param {String} id The column id
59864      * @return {Object} the column
59865      */
59866     getColumnById : function(id){
59867         return this.lookup[id];
59868     },
59869
59870     
59871     /**
59872      * Returns the column Object for a specified dataIndex.
59873      * @param {String} dataIndex The column dataIndex
59874      * @return {Object|Boolean} the column or false if not found
59875      */
59876     getColumnByDataIndex: function(dataIndex){
59877         var index = this.findColumnIndex(dataIndex);
59878         return index > -1 ? this.config[index] : false;
59879     },
59880     
59881     /**
59882      * Returns the index for a specified column id.
59883      * @param {String} id The column id
59884      * @return {Number} the index, or -1 if not found
59885      */
59886     getIndexById : function(id){
59887         for(var i = 0, len = this.config.length; i < len; i++){
59888             if(this.config[i].id == id){
59889                 return i;
59890             }
59891         }
59892         return -1;
59893     },
59894     
59895     /**
59896      * Returns the index for a specified column dataIndex.
59897      * @param {String} dataIndex The column dataIndex
59898      * @return {Number} the index, or -1 if not found
59899      */
59900     
59901     findColumnIndex : function(dataIndex){
59902         for(var i = 0, len = this.config.length; i < len; i++){
59903             if(this.config[i].dataIndex == dataIndex){
59904                 return i;
59905             }
59906         }
59907         return -1;
59908     },
59909     
59910     
59911     moveColumn : function(oldIndex, newIndex){
59912         var c = this.config[oldIndex];
59913         this.config.splice(oldIndex, 1);
59914         this.config.splice(newIndex, 0, c);
59915         this.dataMap = null;
59916         this.fireEvent("columnmoved", this, oldIndex, newIndex);
59917     },
59918
59919     isLocked : function(colIndex){
59920         return this.config[colIndex].locked === true;
59921     },
59922
59923     setLocked : function(colIndex, value, suppressEvent){
59924         if(this.isLocked(colIndex) == value){
59925             return;
59926         }
59927         this.config[colIndex].locked = value;
59928         if(!suppressEvent){
59929             this.fireEvent("columnlockchange", this, colIndex, value);
59930         }
59931     },
59932
59933     getTotalLockedWidth : function(){
59934         var totalWidth = 0;
59935         for(var i = 0; i < this.config.length; i++){
59936             if(this.isLocked(i) && !this.isHidden(i)){
59937                 this.totalWidth += this.getColumnWidth(i);
59938             }
59939         }
59940         return totalWidth;
59941     },
59942
59943     getLockedCount : function(){
59944         for(var i = 0, len = this.config.length; i < len; i++){
59945             if(!this.isLocked(i)){
59946                 return i;
59947             }
59948         }
59949         
59950         return this.config.length;
59951     },
59952
59953     /**
59954      * Returns the number of columns.
59955      * @return {Number}
59956      */
59957     getColumnCount : function(visibleOnly){
59958         if(visibleOnly === true){
59959             var c = 0;
59960             for(var i = 0, len = this.config.length; i < len; i++){
59961                 if(!this.isHidden(i)){
59962                     c++;
59963                 }
59964             }
59965             return c;
59966         }
59967         return this.config.length;
59968     },
59969
59970     /**
59971      * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
59972      * @param {Function} fn
59973      * @param {Object} scope (optional)
59974      * @return {Array} result
59975      */
59976     getColumnsBy : function(fn, scope){
59977         var r = [];
59978         for(var i = 0, len = this.config.length; i < len; i++){
59979             var c = this.config[i];
59980             if(fn.call(scope||this, c, i) === true){
59981                 r[r.length] = c;
59982             }
59983         }
59984         return r;
59985     },
59986
59987     /**
59988      * Returns true if the specified column is sortable.
59989      * @param {Number} col The column index
59990      * @return {Boolean}
59991      */
59992     isSortable : function(col){
59993         if(typeof this.config[col].sortable == "undefined"){
59994             return this.defaultSortable;
59995         }
59996         return this.config[col].sortable;
59997     },
59998
59999     /**
60000      * Returns the rendering (formatting) function defined for the column.
60001      * @param {Number} col The column index.
60002      * @return {Function} The function used to render the cell. See {@link #setRenderer}.
60003      */
60004     getRenderer : function(col){
60005         if(!this.config[col].renderer){
60006             return Roo.grid.ColumnModel.defaultRenderer;
60007         }
60008         return this.config[col].renderer;
60009     },
60010
60011     /**
60012      * Sets the rendering (formatting) function for a column.
60013      * @param {Number} col The column index
60014      * @param {Function} fn The function to use to process the cell's raw data
60015      * to return HTML markup for the grid view. The render function is called with
60016      * the following parameters:<ul>
60017      * <li>Data value.</li>
60018      * <li>Cell metadata. An object in which you may set the following attributes:<ul>
60019      * <li>css A CSS style string to apply to the table cell.</li>
60020      * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
60021      * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
60022      * <li>Row index</li>
60023      * <li>Column index</li>
60024      * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
60025      */
60026     setRenderer : function(col, fn){
60027         this.config[col].renderer = fn;
60028     },
60029
60030     /**
60031      * Returns the width for the specified column.
60032      * @param {Number} col The column index
60033      * @param (optional) {String} gridSize bootstrap width size.
60034      * @return {Number}
60035      */
60036     getColumnWidth : function(col, gridSize)
60037         {
60038                 var cfg = this.config[col];
60039                 
60040                 if (typeof(gridSize) == 'undefined') {
60041                         return cfg.width * 1 || this.defaultWidth;
60042                 }
60043                 if (gridSize === false) { // if we set it..
60044                         return cfg.width || false;
60045                 }
60046                 var sizes = ['xl', 'lg', 'md', 'sm', 'xs'];
60047                 
60048                 for(var i = sizes.indexOf(gridSize); i < sizes.length; i++) {
60049                         if (typeof(cfg[ sizes[i] ] ) == 'undefined') {
60050                                 continue;
60051                         }
60052                         return cfg[ sizes[i] ];
60053                 }
60054                 return 1;
60055                 
60056     },
60057
60058     /**
60059      * Sets the width for a column.
60060      * @param {Number} col The column index
60061      * @param {Number} width The new width
60062      */
60063     setColumnWidth : function(col, width, suppressEvent){
60064         this.config[col].width = width;
60065         this.totalWidth = null;
60066         if(!suppressEvent){
60067              this.fireEvent("widthchange", this, col, width);
60068         }
60069     },
60070
60071     /**
60072      * Returns the total width of all columns.
60073      * @param {Boolean} includeHidden True to include hidden column widths
60074      * @return {Number}
60075      */
60076     getTotalWidth : function(includeHidden){
60077         if(!this.totalWidth){
60078             this.totalWidth = 0;
60079             for(var i = 0, len = this.config.length; i < len; i++){
60080                 if(includeHidden || !this.isHidden(i)){
60081                     this.totalWidth += this.getColumnWidth(i);
60082                 }
60083             }
60084         }
60085         return this.totalWidth;
60086     },
60087
60088     /**
60089      * Returns the header for the specified column.
60090      * @param {Number} col The column index
60091      * @return {String}
60092      */
60093     getColumnHeader : function(col){
60094         return this.config[col].header;
60095     },
60096
60097     /**
60098      * Sets the header for a column.
60099      * @param {Number} col The column index
60100      * @param {String} header The new header
60101      */
60102     setColumnHeader : function(col, header){
60103         this.config[col].header = header;
60104         this.fireEvent("headerchange", this, col, header);
60105     },
60106
60107     /**
60108      * Returns the tooltip for the specified column.
60109      * @param {Number} col The column index
60110      * @return {String}
60111      */
60112     getColumnTooltip : function(col){
60113             return this.config[col].tooltip;
60114     },
60115     /**
60116      * Sets the tooltip for a column.
60117      * @param {Number} col The column index
60118      * @param {String} tooltip The new tooltip
60119      */
60120     setColumnTooltip : function(col, tooltip){
60121             this.config[col].tooltip = tooltip;
60122     },
60123
60124     /**
60125      * Returns the dataIndex for the specified column.
60126      * @param {Number} col The column index
60127      * @return {Number}
60128      */
60129     getDataIndex : function(col){
60130         return this.config[col].dataIndex;
60131     },
60132
60133     /**
60134      * Sets the dataIndex for a column.
60135      * @param {Number} col The column index
60136      * @param {Number} dataIndex The new dataIndex
60137      */
60138     setDataIndex : function(col, dataIndex){
60139         this.config[col].dataIndex = dataIndex;
60140     },
60141
60142     
60143     
60144     /**
60145      * Returns true if the cell is editable.
60146      * @param {Number} colIndex The column index
60147      * @param {Number} rowIndex The row index - this is nto actually used..?
60148      * @return {Boolean}
60149      */
60150     isCellEditable : function(colIndex, rowIndex){
60151         return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
60152     },
60153
60154     /**
60155      * Returns the editor defined for the cell/column.
60156      * return false or null to disable editing.
60157      * @param {Number} colIndex The column index
60158      * @param {Number} rowIndex The row index
60159      * @return {Object}
60160      */
60161     getCellEditor : function(colIndex, rowIndex){
60162         return this.config[colIndex].editor;
60163     },
60164
60165     /**
60166      * Sets if a column is editable.
60167      * @param {Number} col The column index
60168      * @param {Boolean} editable True if the column is editable
60169      */
60170     setEditable : function(col, editable){
60171         this.config[col].editable = editable;
60172     },
60173
60174
60175     /**
60176      * Returns true if the column is hidden.
60177      * @param {Number} colIndex The column index
60178      * @return {Boolean}
60179      */
60180     isHidden : function(colIndex){
60181         return this.config[colIndex].hidden;
60182     },
60183
60184
60185     /**
60186      * Returns true if the column width cannot be changed
60187      */
60188     isFixed : function(colIndex){
60189         return this.config[colIndex].fixed;
60190     },
60191
60192     /**
60193      * Returns true if the column can be resized
60194      * @return {Boolean}
60195      */
60196     isResizable : function(colIndex){
60197         return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
60198     },
60199     /**
60200      * Sets if a column is hidden.
60201      * @param {Number} colIndex The column index
60202      * @param {Boolean} hidden True if the column is hidden
60203      */
60204     setHidden : function(colIndex, hidden){
60205         this.config[colIndex].hidden = hidden;
60206         this.totalWidth = null;
60207         this.fireEvent("hiddenchange", this, colIndex, hidden);
60208     },
60209
60210     /**
60211      * Sets the editor for a column.
60212      * @param {Number} col The column index
60213      * @param {Object} editor The editor object
60214      */
60215     setEditor : function(col, editor){
60216         this.config[col].editor = editor;
60217     },
60218     /**
60219      * Add a column (experimental...) - defaults to adding to the end..
60220      * @param {Object} config 
60221     */
60222     addColumn : function(c)
60223     {
60224     
60225         var i = this.config.length;
60226         this.config[i] = c;
60227         
60228         if(typeof c.dataIndex == "undefined"){
60229             c.dataIndex = i;
60230         }
60231         if(typeof c.renderer == "string"){
60232             c.renderer = Roo.util.Format[c.renderer];
60233         }
60234         if(typeof c.id == "undefined"){
60235             c.id = Roo.id();
60236         }
60237         if(c.editor && c.editor.xtype){
60238             c.editor  = Roo.factory(c.editor, Roo.grid);
60239         }
60240         if(c.editor && c.editor.isFormField){
60241             c.editor = new Roo.grid.GridEditor(c.editor);
60242         }
60243         this.lookup[c.id] = c;
60244     }
60245     
60246 });
60247
60248 Roo.grid.ColumnModel.defaultRenderer = function(value)
60249 {
60250     if(typeof value == "object") {
60251         return value;
60252     }
60253         if(typeof value == "string" && value.length < 1){
60254             return "&#160;";
60255         }
60256     
60257         return String.format("{0}", value);
60258 };
60259
60260 // Alias for backwards compatibility
60261 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
60262 /*
60263  * Based on:
60264  * Ext JS Library 1.1.1
60265  * Copyright(c) 2006-2007, Ext JS, LLC.
60266  *
60267  * Originally Released Under LGPL - original licence link has changed is not relivant.
60268  *
60269  * Fork - LGPL
60270  * <script type="text/javascript">
60271  */
60272
60273 /**
60274  * @class Roo.grid.AbstractSelectionModel
60275  * @extends Roo.util.Observable
60276  * @abstract
60277  * Abstract base class for grid SelectionModels.  It provides the interface that should be
60278  * implemented by descendant classes.  This class should not be directly instantiated.
60279  * @constructor
60280  */
60281 Roo.grid.AbstractSelectionModel = function(){
60282     this.locked = false;
60283     Roo.grid.AbstractSelectionModel.superclass.constructor.call(this);
60284 };
60285
60286 Roo.extend(Roo.grid.AbstractSelectionModel, Roo.util.Observable,  {
60287     /** @ignore Called by the grid automatically. Do not call directly. */
60288     init : function(grid){
60289         this.grid = grid;
60290         this.initEvents();
60291     },
60292
60293     /**
60294      * Locks the selections.
60295      */
60296     lock : function(){
60297         this.locked = true;
60298     },
60299
60300     /**
60301      * Unlocks the selections.
60302      */
60303     unlock : function(){
60304         this.locked = false;
60305     },
60306
60307     /**
60308      * Returns true if the selections are locked.
60309      * @return {Boolean}
60310      */
60311     isLocked : function(){
60312         return this.locked;
60313     }
60314 });/*
60315  * Based on:
60316  * Ext JS Library 1.1.1
60317  * Copyright(c) 2006-2007, Ext JS, LLC.
60318  *
60319  * Originally Released Under LGPL - original licence link has changed is not relivant.
60320  *
60321  * Fork - LGPL
60322  * <script type="text/javascript">
60323  */
60324 /**
60325  * @extends Roo.grid.AbstractSelectionModel
60326  * @class Roo.grid.RowSelectionModel
60327  * The default SelectionModel used by {@link Roo.grid.Grid}.
60328  * It supports multiple selections and keyboard selection/navigation. 
60329  * @constructor
60330  * @param {Object} config
60331  */
60332 Roo.grid.RowSelectionModel = function(config){
60333     Roo.apply(this, config);
60334     this.selections = new Roo.util.MixedCollection(false, function(o){
60335         return o.id;
60336     });
60337
60338     this.last = false;
60339     this.lastActive = false;
60340
60341     this.addEvents({
60342         /**
60343         * @event selectionchange
60344         * Fires when the selection changes
60345         * @param {SelectionModel} this
60346         */
60347        "selectionchange" : true,
60348        /**
60349         * @event afterselectionchange
60350         * Fires after the selection changes (eg. by key press or clicking)
60351         * @param {SelectionModel} this
60352         */
60353        "afterselectionchange" : true,
60354        /**
60355         * @event beforerowselect
60356         * Fires when a row is selected being selected, return false to cancel.
60357         * @param {SelectionModel} this
60358         * @param {Number} rowIndex The selected index
60359         * @param {Boolean} keepExisting False if other selections will be cleared
60360         */
60361        "beforerowselect" : true,
60362        /**
60363         * @event rowselect
60364         * Fires when a row is selected.
60365         * @param {SelectionModel} this
60366         * @param {Number} rowIndex The selected index
60367         * @param {Roo.data.Record} r The record
60368         */
60369        "rowselect" : true,
60370        /**
60371         * @event rowdeselect
60372         * Fires when a row is deselected.
60373         * @param {SelectionModel} this
60374         * @param {Number} rowIndex The selected index
60375         */
60376         "rowdeselect" : true
60377     });
60378     Roo.grid.RowSelectionModel.superclass.constructor.call(this);
60379     this.locked = false;
60380 };
60381
60382 Roo.extend(Roo.grid.RowSelectionModel, Roo.grid.AbstractSelectionModel,  {
60383     /**
60384      * @cfg {Boolean} singleSelect
60385      * True to allow selection of only one row at a time (defaults to false)
60386      */
60387     singleSelect : false,
60388
60389     // private
60390     initEvents : function(){
60391
60392         if(!this.grid.enableDragDrop && !this.grid.enableDrag){
60393             this.grid.on("mousedown", this.handleMouseDown, this);
60394         }else{ // allow click to work like normal
60395             this.grid.on("rowclick", this.handleDragableRowClick, this);
60396         }
60397         // bootstrap does not have a view..
60398         var view = this.grid.view ? this.grid.view : this.grid;
60399         this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
60400             "up" : function(e){
60401                 if(!e.shiftKey){
60402                     this.selectPrevious(e.shiftKey);
60403                 }else if(this.last !== false && this.lastActive !== false){
60404                     var last = this.last;
60405                     this.selectRange(this.last,  this.lastActive-1);
60406                     view.focusRow(this.lastActive);
60407                     if(last !== false){
60408                         this.last = last;
60409                     }
60410                 }else{
60411                     this.selectFirstRow();
60412                 }
60413                 this.fireEvent("afterselectionchange", this);
60414             },
60415             "down" : function(e){
60416                 if(!e.shiftKey){
60417                     this.selectNext(e.shiftKey);
60418                 }else if(this.last !== false && this.lastActive !== false){
60419                     var last = this.last;
60420                     this.selectRange(this.last,  this.lastActive+1);
60421                     view.focusRow(this.lastActive);
60422                     if(last !== false){
60423                         this.last = last;
60424                     }
60425                 }else{
60426                     this.selectFirstRow();
60427                 }
60428                 this.fireEvent("afterselectionchange", this);
60429             },
60430             scope: this
60431         });
60432
60433          
60434         view.on("refresh", this.onRefresh, this);
60435         view.on("rowupdated", this.onRowUpdated, this);
60436         view.on("rowremoved", this.onRemove, this);
60437     },
60438
60439     // private
60440     onRefresh : function(){
60441         var ds = this.grid.ds, i, v = this.grid.view;
60442         var s = this.selections;
60443         s.each(function(r){
60444             if((i = ds.indexOfId(r.id)) != -1){
60445                 v.onRowSelect(i);
60446                 s.add(ds.getAt(i)); // updating the selection relate data
60447             }else{
60448                 s.remove(r);
60449             }
60450         });
60451     },
60452
60453     // private
60454     onRemove : function(v, index, r){
60455         this.selections.remove(r);
60456     },
60457
60458     // private
60459     onRowUpdated : function(v, index, r){
60460         if(this.isSelected(r)){
60461             v.onRowSelect(index);
60462         }
60463     },
60464
60465     /**
60466      * Select records.
60467      * @param {Array} records The records to select
60468      * @param {Boolean} keepExisting (optional) True to keep existing selections
60469      */
60470     selectRecords : function(records, keepExisting){
60471         if(!keepExisting){
60472             this.clearSelections();
60473         }
60474         var ds = this.grid.ds;
60475         for(var i = 0, len = records.length; i < len; i++){
60476             this.selectRow(ds.indexOf(records[i]), true);
60477         }
60478     },
60479
60480     /**
60481      * Gets the number of selected rows.
60482      * @return {Number}
60483      */
60484     getCount : function(){
60485         return this.selections.length;
60486     },
60487
60488     /**
60489      * Selects the first row in the grid.
60490      */
60491     selectFirstRow : function(){
60492         this.selectRow(0);
60493     },
60494
60495     /**
60496      * Select the last row.
60497      * @param {Boolean} keepExisting (optional) True to keep existing selections
60498      */
60499     selectLastRow : function(keepExisting){
60500         this.selectRow(this.grid.ds.getCount() - 1, keepExisting);
60501     },
60502
60503     /**
60504      * Selects the row immediately following the last selected row.
60505      * @param {Boolean} keepExisting (optional) True to keep existing selections
60506      */
60507     selectNext : function(keepExisting){
60508         if(this.last !== false && (this.last+1) < this.grid.ds.getCount()){
60509             this.selectRow(this.last+1, keepExisting);
60510             var view = this.grid.view ? this.grid.view : this.grid;
60511             view.focusRow(this.last);
60512         }
60513     },
60514
60515     /**
60516      * Selects the row that precedes the last selected row.
60517      * @param {Boolean} keepExisting (optional) True to keep existing selections
60518      */
60519     selectPrevious : function(keepExisting){
60520         if(this.last){
60521             this.selectRow(this.last-1, keepExisting);
60522             var view = this.grid.view ? this.grid.view : this.grid;
60523             view.focusRow(this.last);
60524         }
60525     },
60526
60527     /**
60528      * Returns the selected records
60529      * @return {Array} Array of selected records
60530      */
60531     getSelections : function(){
60532         return [].concat(this.selections.items);
60533     },
60534
60535     /**
60536      * Returns the first selected record.
60537      * @return {Record}
60538      */
60539     getSelected : function(){
60540         return this.selections.itemAt(0);
60541     },
60542
60543
60544     /**
60545      * Clears all selections.
60546      */
60547     clearSelections : function(fast){
60548         if(this.locked) {
60549             return;
60550         }
60551         if(fast !== true){
60552             var ds = this.grid.ds;
60553             var s = this.selections;
60554             s.each(function(r){
60555                 this.deselectRow(ds.indexOfId(r.id));
60556             }, this);
60557             s.clear();
60558         }else{
60559             this.selections.clear();
60560         }
60561         this.last = false;
60562     },
60563
60564
60565     /**
60566      * Selects all rows.
60567      */
60568     selectAll : function(){
60569         if(this.locked) {
60570             return;
60571         }
60572         this.selections.clear();
60573         for(var i = 0, len = this.grid.ds.getCount(); i < len; i++){
60574             this.selectRow(i, true);
60575         }
60576     },
60577
60578     /**
60579      * Returns True if there is a selection.
60580      * @return {Boolean}
60581      */
60582     hasSelection : function(){
60583         return this.selections.length > 0;
60584     },
60585
60586     /**
60587      * Returns True if the specified row is selected.
60588      * @param {Number/Record} record The record or index of the record to check
60589      * @return {Boolean}
60590      */
60591     isSelected : function(index){
60592         var r = typeof index == "number" ? this.grid.ds.getAt(index) : index;
60593         return (r && this.selections.key(r.id) ? true : false);
60594     },
60595
60596     /**
60597      * Returns True if the specified record id is selected.
60598      * @param {String} id The id of record to check
60599      * @return {Boolean}
60600      */
60601     isIdSelected : function(id){
60602         return (this.selections.key(id) ? true : false);
60603     },
60604
60605     // private
60606     handleMouseDown : function(e, t)
60607     {
60608         var view = this.grid.view ? this.grid.view : this.grid;
60609         var rowIndex;
60610         if(this.isLocked() || (rowIndex = view.findRowIndex(t)) === false){
60611             return;
60612         };
60613         if(e.shiftKey && this.last !== false){
60614             var last = this.last;
60615             this.selectRange(last, rowIndex, e.ctrlKey);
60616             this.last = last; // reset the last
60617             view.focusRow(rowIndex);
60618         }else{
60619             var isSelected = this.isSelected(rowIndex);
60620             if(e.button !== 0 && isSelected){
60621                 view.focusRow(rowIndex);
60622             }else if(e.ctrlKey && isSelected){
60623                 this.deselectRow(rowIndex);
60624             }else if(!isSelected){
60625                 this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
60626                 view.focusRow(rowIndex);
60627             }
60628         }
60629         this.fireEvent("afterselectionchange", this);
60630     },
60631     // private
60632     handleDragableRowClick :  function(grid, rowIndex, e) 
60633     {
60634         if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
60635             this.selectRow(rowIndex, false);
60636             var view = this.grid.view ? this.grid.view : this.grid;
60637             view.focusRow(rowIndex);
60638              this.fireEvent("afterselectionchange", this);
60639         }
60640     },
60641     
60642     /**
60643      * Selects multiple rows.
60644      * @param {Array} rows Array of the indexes of the row to select
60645      * @param {Boolean} keepExisting (optional) True to keep existing selections
60646      */
60647     selectRows : function(rows, keepExisting){
60648         if(!keepExisting){
60649             this.clearSelections();
60650         }
60651         for(var i = 0, len = rows.length; i < len; i++){
60652             this.selectRow(rows[i], true);
60653         }
60654     },
60655
60656     /**
60657      * Selects a range of rows. All rows in between startRow and endRow are also selected.
60658      * @param {Number} startRow The index of the first row in the range
60659      * @param {Number} endRow The index of the last row in the range
60660      * @param {Boolean} keepExisting (optional) True to retain existing selections
60661      */
60662     selectRange : function(startRow, endRow, keepExisting){
60663         if(this.locked) {
60664             return;
60665         }
60666         if(!keepExisting){
60667             this.clearSelections();
60668         }
60669         if(startRow <= endRow){
60670             for(var i = startRow; i <= endRow; i++){
60671                 this.selectRow(i, true);
60672             }
60673         }else{
60674             for(var i = startRow; i >= endRow; i--){
60675                 this.selectRow(i, true);
60676             }
60677         }
60678     },
60679
60680     /**
60681      * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
60682      * @param {Number} startRow The index of the first row in the range
60683      * @param {Number} endRow The index of the last row in the range
60684      */
60685     deselectRange : function(startRow, endRow, preventViewNotify){
60686         if(this.locked) {
60687             return;
60688         }
60689         for(var i = startRow; i <= endRow; i++){
60690             this.deselectRow(i, preventViewNotify);
60691         }
60692     },
60693
60694     /**
60695      * Selects a row.
60696      * @param {Number} row The index of the row to select
60697      * @param {Boolean} keepExisting (optional) True to keep existing selections
60698      */
60699     selectRow : function(index, keepExisting, preventViewNotify){
60700         if(this.locked || (index < 0 || index >= this.grid.ds.getCount())) {
60701             return;
60702         }
60703         if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
60704             if(!keepExisting || this.singleSelect){
60705                 this.clearSelections();
60706             }
60707             var r = this.grid.ds.getAt(index);
60708             this.selections.add(r);
60709             this.last = this.lastActive = index;
60710             if(!preventViewNotify){
60711                 var view = this.grid.view ? this.grid.view : this.grid;
60712                 view.onRowSelect(index);
60713             }
60714             this.fireEvent("rowselect", this, index, r);
60715             this.fireEvent("selectionchange", this);
60716         }
60717     },
60718
60719     /**
60720      * Deselects a row.
60721      * @param {Number} row The index of the row to deselect
60722      */
60723     deselectRow : function(index, preventViewNotify){
60724         if(this.locked) {
60725             return;
60726         }
60727         if(this.last == index){
60728             this.last = false;
60729         }
60730         if(this.lastActive == index){
60731             this.lastActive = false;
60732         }
60733         var r = this.grid.ds.getAt(index);
60734         this.selections.remove(r);
60735         if(!preventViewNotify){
60736             var view = this.grid.view ? this.grid.view : this.grid;
60737             view.onRowDeselect(index);
60738         }
60739         this.fireEvent("rowdeselect", this, index);
60740         this.fireEvent("selectionchange", this);
60741     },
60742
60743     // private
60744     restoreLast : function(){
60745         if(this._last){
60746             this.last = this._last;
60747         }
60748     },
60749
60750     // private
60751     acceptsNav : function(row, col, cm){
60752         return !cm.isHidden(col) && cm.isCellEditable(col, row);
60753     },
60754
60755     // private
60756     onEditorKey : function(field, e){
60757         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
60758         if(k == e.TAB){
60759             e.stopEvent();
60760             ed.completeEdit();
60761             if(e.shiftKey){
60762                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
60763             }else{
60764                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
60765             }
60766         }else if(k == e.ENTER && !e.ctrlKey){
60767             e.stopEvent();
60768             ed.completeEdit();
60769             if(e.shiftKey){
60770                 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
60771             }else{
60772                 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
60773             }
60774         }else if(k == e.ESC){
60775             ed.cancelEdit();
60776         }
60777         if(newCell){
60778             g.startEditing(newCell[0], newCell[1]);
60779         }
60780     }
60781 });/*
60782  * Based on:
60783  * Ext JS Library 1.1.1
60784  * Copyright(c) 2006-2007, Ext JS, LLC.
60785  *
60786  * Originally Released Under LGPL - original licence link has changed is not relivant.
60787  *
60788  * Fork - LGPL
60789  * <script type="text/javascript">
60790  */
60791 /**
60792  * @class Roo.grid.CellSelectionModel
60793  * @extends Roo.grid.AbstractSelectionModel
60794  * This class provides the basic implementation for cell selection in a grid.
60795  * @constructor
60796  * @param {Object} config The object containing the configuration of this model.
60797  * @cfg {Boolean} enter_is_tab Enter behaves the same as tab. (eg. goes to next cell) default: false
60798  */
60799 Roo.grid.CellSelectionModel = function(config){
60800     Roo.apply(this, config);
60801
60802     this.selection = null;
60803
60804     this.addEvents({
60805         /**
60806              * @event beforerowselect
60807              * Fires before a cell is selected.
60808              * @param {SelectionModel} this
60809              * @param {Number} rowIndex The selected row index
60810              * @param {Number} colIndex The selected cell index
60811              */
60812             "beforecellselect" : true,
60813         /**
60814              * @event cellselect
60815              * Fires when a cell is selected.
60816              * @param {SelectionModel} this
60817              * @param {Number} rowIndex The selected row index
60818              * @param {Number} colIndex The selected cell index
60819              */
60820             "cellselect" : true,
60821         /**
60822              * @event selectionchange
60823              * Fires when the active selection changes.
60824              * @param {SelectionModel} this
60825              * @param {Object} selection null for no selection or an object (o) with two properties
60826                 <ul>
60827                 <li>o.record: the record object for the row the selection is in</li>
60828                 <li>o.cell: An array of [rowIndex, columnIndex]</li>
60829                 </ul>
60830              */
60831             "selectionchange" : true,
60832         /**
60833              * @event tabend
60834              * Fires when the tab (or enter) was pressed on the last editable cell
60835              * You can use this to trigger add new row.
60836              * @param {SelectionModel} this
60837              */
60838             "tabend" : true,
60839          /**
60840              * @event beforeeditnext
60841              * Fires before the next editable sell is made active
60842              * You can use this to skip to another cell or fire the tabend
60843              *    if you set cell to false
60844              * @param {Object} eventdata object : { cell : [ row, col ] } 
60845              */
60846             "beforeeditnext" : true
60847     });
60848     Roo.grid.CellSelectionModel.superclass.constructor.call(this);
60849 };
60850
60851 Roo.extend(Roo.grid.CellSelectionModel, Roo.grid.AbstractSelectionModel,  {
60852     
60853     enter_is_tab: false,
60854
60855     /** @ignore */
60856     initEvents : function(){
60857         this.grid.on("mousedown", this.handleMouseDown, this);
60858         this.grid.getGridEl().on(Roo.isIE ? "keydown" : "keypress", this.handleKeyDown, this);
60859         var view = this.grid.view;
60860         view.on("refresh", this.onViewChange, this);
60861         view.on("rowupdated", this.onRowUpdated, this);
60862         view.on("beforerowremoved", this.clearSelections, this);
60863         view.on("beforerowsinserted", this.clearSelections, this);
60864         if(this.grid.isEditor){
60865             this.grid.on("beforeedit", this.beforeEdit,  this);
60866         }
60867     },
60868
60869         //private
60870     beforeEdit : function(e){
60871         this.select(e.row, e.column, false, true, e.record);
60872     },
60873
60874         //private
60875     onRowUpdated : function(v, index, r){
60876         if(this.selection && this.selection.record == r){
60877             v.onCellSelect(index, this.selection.cell[1]);
60878         }
60879     },
60880
60881         //private
60882     onViewChange : function(){
60883         this.clearSelections(true);
60884     },
60885
60886         /**
60887          * Returns the currently selected cell,.
60888          * @return {Array} The selected cell (row, column) or null if none selected.
60889          */
60890     getSelectedCell : function(){
60891         return this.selection ? this.selection.cell : null;
60892     },
60893
60894     /**
60895      * Clears all selections.
60896      * @param {Boolean} true to prevent the gridview from being notified about the change.
60897      */
60898     clearSelections : function(preventNotify){
60899         var s = this.selection;
60900         if(s){
60901             if(preventNotify !== true){
60902                 this.grid.view.onCellDeselect(s.cell[0], s.cell[1]);
60903             }
60904             this.selection = null;
60905             this.fireEvent("selectionchange", this, null);
60906         }
60907     },
60908
60909     /**
60910      * Returns true if there is a selection.
60911      * @return {Boolean}
60912      */
60913     hasSelection : function(){
60914         return this.selection ? true : false;
60915     },
60916
60917     /** @ignore */
60918     handleMouseDown : function(e, t){
60919         var v = this.grid.getView();
60920         if(this.isLocked()){
60921             return;
60922         };
60923         var row = v.findRowIndex(t);
60924         var cell = v.findCellIndex(t);
60925         if(row !== false && cell !== false){
60926             this.select(row, cell);
60927         }
60928     },
60929
60930     /**
60931      * Selects a cell.
60932      * @param {Number} rowIndex
60933      * @param {Number} collIndex
60934      */
60935     select : function(rowIndex, colIndex, preventViewNotify, preventFocus, /*internal*/ r){
60936         if(this.fireEvent("beforecellselect", this, rowIndex, colIndex) !== false){
60937             this.clearSelections();
60938             r = r || this.grid.dataSource.getAt(rowIndex);
60939             this.selection = {
60940                 record : r,
60941                 cell : [rowIndex, colIndex]
60942             };
60943             if(!preventViewNotify){
60944                 var v = this.grid.getView();
60945                 v.onCellSelect(rowIndex, colIndex);
60946                 if(preventFocus !== true){
60947                     v.focusCell(rowIndex, colIndex);
60948                 }
60949             }
60950             this.fireEvent("cellselect", this, rowIndex, colIndex);
60951             this.fireEvent("selectionchange", this, this.selection);
60952         }
60953     },
60954
60955         //private
60956     isSelectable : function(rowIndex, colIndex, cm){
60957         return !cm.isHidden(colIndex);
60958     },
60959
60960     /** @ignore */
60961     handleKeyDown : function(e){
60962         //Roo.log('Cell Sel Model handleKeyDown');
60963         if(!e.isNavKeyPress()){
60964             return;
60965         }
60966         var g = this.grid, s = this.selection;
60967         if(!s){
60968             e.stopEvent();
60969             var cell = g.walkCells(0, 0, 1, this.isSelectable,  this);
60970             if(cell){
60971                 this.select(cell[0], cell[1]);
60972             }
60973             return;
60974         }
60975         var sm = this;
60976         var walk = function(row, col, step){
60977             return g.walkCells(row, col, step, sm.isSelectable,  sm);
60978         };
60979         var k = e.getKey(), r = s.cell[0], c = s.cell[1];
60980         var newCell;
60981
60982       
60983
60984         switch(k){
60985             case e.TAB:
60986                 // handled by onEditorKey
60987                 if (g.isEditor && g.editing) {
60988                     return;
60989                 }
60990                 if(e.shiftKey) {
60991                     newCell = walk(r, c-1, -1);
60992                 } else {
60993                     newCell = walk(r, c+1, 1);
60994                 }
60995                 break;
60996             
60997             case e.DOWN:
60998                newCell = walk(r+1, c, 1);
60999                 break;
61000             
61001             case e.UP:
61002                 newCell = walk(r-1, c, -1);
61003                 break;
61004             
61005             case e.RIGHT:
61006                 newCell = walk(r, c+1, 1);
61007                 break;
61008             
61009             case e.LEFT:
61010                 newCell = walk(r, c-1, -1);
61011                 break;
61012             
61013             case e.ENTER:
61014                 
61015                 if(g.isEditor && !g.editing){
61016                    g.startEditing(r, c);
61017                    e.stopEvent();
61018                    return;
61019                 }
61020                 
61021                 
61022              break;
61023         };
61024         if(newCell){
61025             this.select(newCell[0], newCell[1]);
61026             e.stopEvent();
61027             
61028         }
61029     },
61030
61031     acceptsNav : function(row, col, cm){
61032         return !cm.isHidden(col) && cm.isCellEditable(col, row);
61033     },
61034     /**
61035      * Selects a cell.
61036      * @param {Number} field (not used) - as it's normally used as a listener
61037      * @param {Number} e - event - fake it by using
61038      *
61039      * var e = Roo.EventObjectImpl.prototype;
61040      * e.keyCode = e.TAB
61041      *
61042      * 
61043      */
61044     onEditorKey : function(field, e){
61045         
61046         var k = e.getKey(),
61047             newCell,
61048             g = this.grid,
61049             ed = g.activeEditor,
61050             forward = false;
61051         ///Roo.log('onEditorKey' + k);
61052         
61053         
61054         if (this.enter_is_tab && k == e.ENTER) {
61055             k = e.TAB;
61056         }
61057         
61058         if(k == e.TAB){
61059             if(e.shiftKey){
61060                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
61061             }else{
61062                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
61063                 forward = true;
61064             }
61065             
61066             e.stopEvent();
61067             
61068         } else if(k == e.ENTER &&  !e.ctrlKey){
61069             ed.completeEdit();
61070             e.stopEvent();
61071             newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
61072         
61073                 } else if(k == e.ESC){
61074             ed.cancelEdit();
61075         }
61076                 
61077         if (newCell) {
61078             var ecall = { cell : newCell, forward : forward };
61079             this.fireEvent('beforeeditnext', ecall );
61080             newCell = ecall.cell;
61081                         forward = ecall.forward;
61082         }
61083                 
61084         if(newCell){
61085             //Roo.log('next cell after edit');
61086             g.startEditing.defer(100, g, [newCell[0], newCell[1]]);
61087         } else if (forward) {
61088             // tabbed past last
61089             this.fireEvent.defer(100, this, ['tabend',this]);
61090         }
61091     }
61092 });/*
61093  * Based on:
61094  * Ext JS Library 1.1.1
61095  * Copyright(c) 2006-2007, Ext JS, LLC.
61096  *
61097  * Originally Released Under LGPL - original licence link has changed is not relivant.
61098  *
61099  * Fork - LGPL
61100  * <script type="text/javascript">
61101  */
61102  
61103 /**
61104  * @class Roo.grid.EditorGrid
61105  * @extends Roo.grid.Grid
61106  * Class for creating and editable grid.
61107  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered - 
61108  * The container MUST have some type of size defined for the grid to fill. The container will be 
61109  * automatically set to position relative if it isn't already.
61110  * @param {Object} dataSource The data model to bind to
61111  * @param {Object} colModel The column model with info about this grid's columns
61112  */
61113 Roo.grid.EditorGrid = function(container, config){
61114     Roo.grid.EditorGrid.superclass.constructor.call(this, container, config);
61115     this.getGridEl().addClass("xedit-grid");
61116
61117     if(!this.selModel){
61118         this.selModel = new Roo.grid.CellSelectionModel();
61119     }
61120
61121     this.activeEditor = null;
61122
61123         this.addEvents({
61124             /**
61125              * @event beforeedit
61126              * Fires before cell editing is triggered. The edit event object has the following properties <br />
61127              * <ul style="padding:5px;padding-left:16px;">
61128              * <li>grid - This grid</li>
61129              * <li>record - The record being edited</li>
61130              * <li>field - The field name being edited</li>
61131              * <li>value - The value for the field being edited.</li>
61132              * <li>row - The grid row index</li>
61133              * <li>column - The grid column index</li>
61134              * <li>cancel - Set this to true to cancel the edit or return false from your handler.</li>
61135              * </ul>
61136              * @param {Object} e An edit event (see above for description)
61137              */
61138             "beforeedit" : true,
61139             /**
61140              * @event afteredit
61141              * Fires after a cell is edited. <br />
61142              * <ul style="padding:5px;padding-left:16px;">
61143              * <li>grid - This grid</li>
61144              * <li>record - The record being edited</li>
61145              * <li>field - The field name being edited</li>
61146              * <li>value - The value being set</li>
61147              * <li>originalValue - The original value for the field, before the edit.</li>
61148              * <li>row - The grid row index</li>
61149              * <li>column - The grid column index</li>
61150              * </ul>
61151              * @param {Object} e An edit event (see above for description)
61152              */
61153             "afteredit" : true,
61154             /**
61155              * @event validateedit
61156              * Fires after a cell is edited, but before the value is set in the record. 
61157          * You can use this to modify the value being set in the field, Return false
61158              * to cancel the change. The edit event object has the following properties <br />
61159              * <ul style="padding:5px;padding-left:16px;">
61160          * <li>editor - This editor</li>
61161              * <li>grid - This grid</li>
61162              * <li>record - The record being edited</li>
61163              * <li>field - The field name being edited</li>
61164              * <li>value - The value being set</li>
61165              * <li>originalValue - The original value for the field, before the edit.</li>
61166              * <li>row - The grid row index</li>
61167              * <li>column - The grid column index</li>
61168              * <li>cancel - Set this to true to cancel the edit or return false from your handler.</li>
61169              * </ul>
61170              * @param {Object} e An edit event (see above for description)
61171              */
61172             "validateedit" : true
61173         });
61174     this.on("bodyscroll", this.stopEditing,  this);
61175     this.on(this.clicksToEdit == 1 ? "cellclick" : "celldblclick", this.onCellDblClick,  this);
61176 };
61177
61178 Roo.extend(Roo.grid.EditorGrid, Roo.grid.Grid, {
61179     /**
61180      * @cfg {Number} clicksToEdit
61181      * The number of clicks on a cell required to display the cell's editor (defaults to 2)
61182      */
61183     clicksToEdit: 2,
61184
61185     // private
61186     isEditor : true,
61187     // private
61188     trackMouseOver: false, // causes very odd FF errors
61189
61190     onCellDblClick : function(g, row, col){
61191         this.startEditing(row, col);
61192     },
61193
61194     onEditComplete : function(ed, value, startValue){
61195         this.editing = false;
61196         this.activeEditor = null;
61197         ed.un("specialkey", this.selModel.onEditorKey, this.selModel);
61198         var r = ed.record;
61199         var field = this.colModel.getDataIndex(ed.col);
61200         var e = {
61201             grid: this,
61202             record: r,
61203             field: field,
61204             originalValue: startValue,
61205             value: value,
61206             row: ed.row,
61207             column: ed.col,
61208             cancel:false,
61209             editor: ed
61210         };
61211         var cell = Roo.get(this.view.getCell(ed.row,ed.col));
61212         cell.show();
61213           
61214         if(String(value) !== String(startValue)){
61215             
61216             if(this.fireEvent("validateedit", e) !== false && !e.cancel){
61217                 r.set(field, e.value);
61218                 // if we are dealing with a combo box..
61219                 // then we also set the 'name' colum to be the displayField
61220                 if (ed.field.displayField && ed.field.name) {
61221                     r.set(ed.field.name, ed.field.el.dom.value);
61222                 }
61223                 
61224                 delete e.cancel; //?? why!!!
61225                 this.fireEvent("afteredit", e);
61226             }
61227         } else {
61228             this.fireEvent("afteredit", e); // always fire it!
61229         }
61230         this.view.focusCell(ed.row, ed.col);
61231     },
61232
61233     /**
61234      * Starts editing the specified for the specified row/column
61235      * @param {Number} rowIndex
61236      * @param {Number} colIndex
61237      */
61238     startEditing : function(row, col){
61239         this.stopEditing();
61240         if(this.colModel.isCellEditable(col, row)){
61241             this.view.ensureVisible(row, col, true);
61242           
61243             var r = this.dataSource.getAt(row);
61244             var field = this.colModel.getDataIndex(col);
61245             var cell = Roo.get(this.view.getCell(row,col));
61246             var e = {
61247                 grid: this,
61248                 record: r,
61249                 field: field,
61250                 value: r.data[field],
61251                 row: row,
61252                 column: col,
61253                 cancel:false 
61254             };
61255             if(this.fireEvent("beforeedit", e) !== false && !e.cancel){
61256                 this.editing = true;
61257                 var ed = this.colModel.getCellEditor(col, row);
61258                 
61259                 if (!ed) {
61260                     return;
61261                 }
61262                 if(!ed.rendered){
61263                     ed.render(ed.parentEl || document.body);
61264                 }
61265                 ed.field.reset();
61266                
61267                 cell.hide();
61268                 
61269                 (function(){ // complex but required for focus issues in safari, ie and opera
61270                     ed.row = row;
61271                     ed.col = col;
61272                     ed.record = r;
61273                     ed.on("complete",   this.onEditComplete,        this,       {single: true});
61274                     ed.on("specialkey", this.selModel.onEditorKey,  this.selModel);
61275                     this.activeEditor = ed;
61276                     var v = r.data[field];
61277                     ed.startEdit(this.view.getCell(row, col), v);
61278                     // combo's with 'displayField and name set
61279                     if (ed.field.displayField && ed.field.name) {
61280                         ed.field.el.dom.value = r.data[ed.field.name];
61281                     }
61282                     
61283                     
61284                 }).defer(50, this);
61285             }
61286         }
61287     },
61288         
61289     /**
61290      * Stops any active editing
61291      */
61292     stopEditing : function(){
61293         if(this.activeEditor){
61294             this.activeEditor.completeEdit();
61295         }
61296         this.activeEditor = null;
61297     },
61298         
61299          /**
61300      * Called to get grid's drag proxy text, by default returns this.ddText.
61301      * @return {String}
61302      */
61303     getDragDropText : function(){
61304         var count = this.selModel.getSelectedCell() ? 1 : 0;
61305         return String.format(this.ddText, count, count == 1 ? '' : 's');
61306     }
61307         
61308 });/*
61309  * Based on:
61310  * Ext JS Library 1.1.1
61311  * Copyright(c) 2006-2007, Ext JS, LLC.
61312  *
61313  * Originally Released Under LGPL - original licence link has changed is not relivant.
61314  *
61315  * Fork - LGPL
61316  * <script type="text/javascript">
61317  */
61318
61319 // private - not really -- you end up using it !
61320 // This is a support class used internally by the Grid components
61321
61322 /**
61323  * @class Roo.grid.GridEditor
61324  * @extends Roo.Editor
61325  * Class for creating and editable grid elements.
61326  * @param {Object} config any settings (must include field)
61327  */
61328 Roo.grid.GridEditor = function(field, config){
61329     if (!config && field.field) {
61330         config = field;
61331         field = Roo.factory(config.field, Roo.form);
61332     }
61333     Roo.grid.GridEditor.superclass.constructor.call(this, field, config);
61334     field.monitorTab = false;
61335 };
61336
61337 Roo.extend(Roo.grid.GridEditor, Roo.Editor, {
61338     
61339     /**
61340      * @cfg {Roo.form.Field} field Field to wrap (or xtyped)
61341      */
61342     
61343     alignment: "tl-tl",
61344     autoSize: "width",
61345     hideEl : false,
61346     cls: "x-small-editor x-grid-editor",
61347     shim:false,
61348     shadow:"frame"
61349 });/*
61350  * Based on:
61351  * Ext JS Library 1.1.1
61352  * Copyright(c) 2006-2007, Ext JS, LLC.
61353  *
61354  * Originally Released Under LGPL - original licence link has changed is not relivant.
61355  *
61356  * Fork - LGPL
61357  * <script type="text/javascript">
61358  */
61359   
61360
61361   
61362 Roo.grid.PropertyRecord = Roo.data.Record.create([
61363     {name:'name',type:'string'},  'value'
61364 ]);
61365
61366
61367 Roo.grid.PropertyStore = function(grid, source){
61368     this.grid = grid;
61369     this.store = new Roo.data.Store({
61370         recordType : Roo.grid.PropertyRecord
61371     });
61372     this.store.on('update', this.onUpdate,  this);
61373     if(source){
61374         this.setSource(source);
61375     }
61376     Roo.grid.PropertyStore.superclass.constructor.call(this);
61377 };
61378
61379
61380
61381 Roo.extend(Roo.grid.PropertyStore, Roo.util.Observable, {
61382     setSource : function(o){
61383         this.source = o;
61384         this.store.removeAll();
61385         var data = [];
61386         for(var k in o){
61387             if(this.isEditableValue(o[k])){
61388                 data.push(new Roo.grid.PropertyRecord({name: k, value: o[k]}, k));
61389             }
61390         }
61391         this.store.loadRecords({records: data}, {}, true);
61392     },
61393
61394     onUpdate : function(ds, record, type){
61395         if(type == Roo.data.Record.EDIT){
61396             var v = record.data['value'];
61397             var oldValue = record.modified['value'];
61398             if(this.grid.fireEvent('beforepropertychange', this.source, record.id, v, oldValue) !== false){
61399                 this.source[record.id] = v;
61400                 record.commit();
61401                 this.grid.fireEvent('propertychange', this.source, record.id, v, oldValue);
61402             }else{
61403                 record.reject();
61404             }
61405         }
61406     },
61407
61408     getProperty : function(row){
61409        return this.store.getAt(row);
61410     },
61411
61412     isEditableValue: function(val){
61413         if(val && val instanceof Date){
61414             return true;
61415         }else if(typeof val == 'object' || typeof val == 'function'){
61416             return false;
61417         }
61418         return true;
61419     },
61420
61421     setValue : function(prop, value){
61422         this.source[prop] = value;
61423         this.store.getById(prop).set('value', value);
61424     },
61425
61426     getSource : function(){
61427         return this.source;
61428     }
61429 });
61430
61431 Roo.grid.PropertyColumnModel = function(grid, store){
61432     this.grid = grid;
61433     var g = Roo.grid;
61434     g.PropertyColumnModel.superclass.constructor.call(this, [
61435         {header: this.nameText, sortable: true, dataIndex:'name', id: 'name'},
61436         {header: this.valueText, resizable:false, dataIndex: 'value', id: 'value'}
61437     ]);
61438     this.store = store;
61439     this.bselect = Roo.DomHelper.append(document.body, {
61440         tag: 'select', style:'display:none', cls: 'x-grid-editor', children: [
61441             {tag: 'option', value: 'true', html: 'true'},
61442             {tag: 'option', value: 'false', html: 'false'}
61443         ]
61444     });
61445     Roo.id(this.bselect);
61446     var f = Roo.form;
61447     this.editors = {
61448         'date' : new g.GridEditor(new f.DateField({selectOnFocus:true})),
61449         'string' : new g.GridEditor(new f.TextField({selectOnFocus:true})),
61450         'number' : new g.GridEditor(new f.NumberField({selectOnFocus:true, style:'text-align:left;'})),
61451         'int' : new g.GridEditor(new f.NumberField({selectOnFocus:true, allowDecimals:false, style:'text-align:left;'})),
61452         'boolean' : new g.GridEditor(new f.Field({el:this.bselect,selectOnFocus:true}))
61453     };
61454     this.renderCellDelegate = this.renderCell.createDelegate(this);
61455     this.renderPropDelegate = this.renderProp.createDelegate(this);
61456 };
61457
61458 Roo.extend(Roo.grid.PropertyColumnModel, Roo.grid.ColumnModel, {
61459     
61460     
61461     nameText : 'Name',
61462     valueText : 'Value',
61463     
61464     dateFormat : 'm/j/Y',
61465     
61466     
61467     renderDate : function(dateVal){
61468         return dateVal.dateFormat(this.dateFormat);
61469     },
61470
61471     renderBool : function(bVal){
61472         return bVal ? 'true' : 'false';
61473     },
61474
61475     isCellEditable : function(colIndex, rowIndex){
61476         return colIndex == 1;
61477     },
61478
61479     getRenderer : function(col){
61480         return col == 1 ?
61481             this.renderCellDelegate : this.renderPropDelegate;
61482     },
61483
61484     renderProp : function(v){
61485         return this.getPropertyName(v);
61486     },
61487
61488     renderCell : function(val){
61489         var rv = val;
61490         if(val instanceof Date){
61491             rv = this.renderDate(val);
61492         }else if(typeof val == 'boolean'){
61493             rv = this.renderBool(val);
61494         }
61495         return Roo.util.Format.htmlEncode(rv);
61496     },
61497
61498     getPropertyName : function(name){
61499         var pn = this.grid.propertyNames;
61500         return pn && pn[name] ? pn[name] : name;
61501     },
61502
61503     getCellEditor : function(colIndex, rowIndex){
61504         var p = this.store.getProperty(rowIndex);
61505         var n = p.data['name'], val = p.data['value'];
61506         
61507         if(typeof(this.grid.customEditors[n]) == 'string'){
61508             return this.editors[this.grid.customEditors[n]];
61509         }
61510         if(typeof(this.grid.customEditors[n]) != 'undefined'){
61511             return this.grid.customEditors[n];
61512         }
61513         if(val instanceof Date){
61514             return this.editors['date'];
61515         }else if(typeof val == 'number'){
61516             return this.editors['number'];
61517         }else if(typeof val == 'boolean'){
61518             return this.editors['boolean'];
61519         }else{
61520             return this.editors['string'];
61521         }
61522     }
61523 });
61524
61525 /**
61526  * @class Roo.grid.PropertyGrid
61527  * @extends Roo.grid.EditorGrid
61528  * This class represents the  interface of a component based property grid control.
61529  * <br><br>Usage:<pre><code>
61530  var grid = new Roo.grid.PropertyGrid("my-container-id", {
61531       
61532  });
61533  // set any options
61534  grid.render();
61535  * </code></pre>
61536   
61537  * @constructor
61538  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
61539  * The container MUST have some type of size defined for the grid to fill. The container will be
61540  * automatically set to position relative if it isn't already.
61541  * @param {Object} config A config object that sets properties on this grid.
61542  */
61543 Roo.grid.PropertyGrid = function(container, config){
61544     config = config || {};
61545     var store = new Roo.grid.PropertyStore(this);
61546     this.store = store;
61547     var cm = new Roo.grid.PropertyColumnModel(this, store);
61548     store.store.sort('name', 'ASC');
61549     Roo.grid.PropertyGrid.superclass.constructor.call(this, container, Roo.apply({
61550         ds: store.store,
61551         cm: cm,
61552         enableColLock:false,
61553         enableColumnMove:false,
61554         stripeRows:false,
61555         trackMouseOver: false,
61556         clicksToEdit:1
61557     }, config));
61558     this.getGridEl().addClass('x-props-grid');
61559     this.lastEditRow = null;
61560     this.on('columnresize', this.onColumnResize, this);
61561     this.addEvents({
61562          /**
61563              * @event beforepropertychange
61564              * Fires before a property changes (return false to stop?)
61565              * @param {Roo.grid.PropertyGrid} grid property grid? (check could be store)
61566              * @param {String} id Record Id
61567              * @param {String} newval New Value
61568          * @param {String} oldval Old Value
61569              */
61570         "beforepropertychange": true,
61571         /**
61572              * @event propertychange
61573              * Fires after a property changes
61574              * @param {Roo.grid.PropertyGrid} grid property grid? (check could be store)
61575              * @param {String} id Record Id
61576              * @param {String} newval New Value
61577          * @param {String} oldval Old Value
61578              */
61579         "propertychange": true
61580     });
61581     this.customEditors = this.customEditors || {};
61582 };
61583 Roo.extend(Roo.grid.PropertyGrid, Roo.grid.EditorGrid, {
61584     
61585      /**
61586      * @cfg {Object} customEditors map of colnames=> custom editors.
61587      * the custom editor can be one of the standard ones (date|string|number|int|boolean), or a
61588      * grid editor eg. Roo.grid.GridEditor(new Roo.form.TextArea({selectOnFocus:true})),
61589      * false disables editing of the field.
61590          */
61591     
61592       /**
61593      * @cfg {Object} propertyNames map of property Names to their displayed value
61594          */
61595     
61596     render : function(){
61597         Roo.grid.PropertyGrid.superclass.render.call(this);
61598         this.autoSize.defer(100, this);
61599     },
61600
61601     autoSize : function(){
61602         Roo.grid.PropertyGrid.superclass.autoSize.call(this);
61603         if(this.view){
61604             this.view.fitColumns();
61605         }
61606     },
61607
61608     onColumnResize : function(){
61609         this.colModel.setColumnWidth(1, this.container.getWidth(true)-this.colModel.getColumnWidth(0));
61610         this.autoSize();
61611     },
61612     /**
61613      * Sets the data for the Grid
61614      * accepts a Key => Value object of all the elements avaiable.
61615      * @param {Object} data  to appear in grid.
61616      */
61617     setSource : function(source){
61618         this.store.setSource(source);
61619         //this.autoSize();
61620     },
61621     /**
61622      * Gets all the data from the grid.
61623      * @return {Object} data  data stored in grid
61624      */
61625     getSource : function(){
61626         return this.store.getSource();
61627     }
61628 });/*
61629   
61630  * Licence LGPL
61631  
61632  */
61633  
61634 /**
61635  * @class Roo.grid.Calendar
61636  * @extends Roo.grid.Grid
61637  * This class extends the Grid to provide a calendar widget
61638  * <br><br>Usage:<pre><code>
61639  var grid = new Roo.grid.Calendar("my-container-id", {
61640      ds: myDataStore,
61641      cm: myColModel,
61642      selModel: mySelectionModel,
61643      autoSizeColumns: true,
61644      monitorWindowResize: false,
61645      trackMouseOver: true
61646      eventstore : real data store..
61647  });
61648  // set any options
61649  grid.render();
61650   
61651   * @constructor
61652  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
61653  * The container MUST have some type of size defined for the grid to fill. The container will be
61654  * automatically set to position relative if it isn't already.
61655  * @param {Object} config A config object that sets properties on this grid.
61656  */
61657 Roo.grid.Calendar = function(container, config){
61658         // initialize the container
61659         this.container = Roo.get(container);
61660         this.container.update("");
61661         this.container.setStyle("overflow", "hidden");
61662     this.container.addClass('x-grid-container');
61663
61664     this.id = this.container.id;
61665
61666     Roo.apply(this, config);
61667     // check and correct shorthanded configs
61668     
61669     var rows = [];
61670     var d =1;
61671     for (var r = 0;r < 6;r++) {
61672         
61673         rows[r]=[];
61674         for (var c =0;c < 7;c++) {
61675             rows[r][c]= '';
61676         }
61677     }
61678     if (this.eventStore) {
61679         this.eventStore= Roo.factory(this.eventStore, Roo.data);
61680         this.eventStore.on('load',this.onLoad, this);
61681         this.eventStore.on('beforeload',this.clearEvents, this);
61682          
61683     }
61684     
61685     this.dataSource = new Roo.data.Store({
61686             proxy: new Roo.data.MemoryProxy(rows),
61687             reader: new Roo.data.ArrayReader({}, [
61688                    'weekday0', 'weekday1', 'weekday2', 'weekday3', 'weekday4', 'weekday5', 'weekday6' ])
61689     });
61690
61691     this.dataSource.load();
61692     this.ds = this.dataSource;
61693     this.ds.xmodule = this.xmodule || false;
61694     
61695     
61696     var cellRender = function(v,x,r)
61697     {
61698         return String.format(
61699             '<div class="fc-day  fc-widget-content"><div>' +
61700                 '<div class="fc-event-container"></div>' +
61701                 '<div class="fc-day-number">{0}</div>'+
61702                 
61703                 '<div class="fc-day-content"><div style="position:relative"></div></div>' +
61704             '</div></div>', v);
61705     
61706     }
61707     
61708     
61709     this.colModel = new Roo.grid.ColumnModel( [
61710         {
61711             xtype: 'ColumnModel',
61712             xns: Roo.grid,
61713             dataIndex : 'weekday0',
61714             header : 'Sunday',
61715             renderer : cellRender
61716         },
61717         {
61718             xtype: 'ColumnModel',
61719             xns: Roo.grid,
61720             dataIndex : 'weekday1',
61721             header : 'Monday',
61722             renderer : cellRender
61723         },
61724         {
61725             xtype: 'ColumnModel',
61726             xns: Roo.grid,
61727             dataIndex : 'weekday2',
61728             header : 'Tuesday',
61729             renderer : cellRender
61730         },
61731         {
61732             xtype: 'ColumnModel',
61733             xns: Roo.grid,
61734             dataIndex : 'weekday3',
61735             header : 'Wednesday',
61736             renderer : cellRender
61737         },
61738         {
61739             xtype: 'ColumnModel',
61740             xns: Roo.grid,
61741             dataIndex : 'weekday4',
61742             header : 'Thursday',
61743             renderer : cellRender
61744         },
61745         {
61746             xtype: 'ColumnModel',
61747             xns: Roo.grid,
61748             dataIndex : 'weekday5',
61749             header : 'Friday',
61750             renderer : cellRender
61751         },
61752         {
61753             xtype: 'ColumnModel',
61754             xns: Roo.grid,
61755             dataIndex : 'weekday6',
61756             header : 'Saturday',
61757             renderer : cellRender
61758         }
61759     ]);
61760     this.cm = this.colModel;
61761     this.cm.xmodule = this.xmodule || false;
61762  
61763         
61764           
61765     //this.selModel = new Roo.grid.CellSelectionModel();
61766     //this.sm = this.selModel;
61767     //this.selModel.init(this);
61768     
61769     
61770     if(this.width){
61771         this.container.setWidth(this.width);
61772     }
61773
61774     if(this.height){
61775         this.container.setHeight(this.height);
61776     }
61777     /** @private */
61778         this.addEvents({
61779         // raw events
61780         /**
61781          * @event click
61782          * The raw click event for the entire grid.
61783          * @param {Roo.EventObject} e
61784          */
61785         "click" : true,
61786         /**
61787          * @event dblclick
61788          * The raw dblclick event for the entire grid.
61789          * @param {Roo.EventObject} e
61790          */
61791         "dblclick" : true,
61792         /**
61793          * @event contextmenu
61794          * The raw contextmenu event for the entire grid.
61795          * @param {Roo.EventObject} e
61796          */
61797         "contextmenu" : true,
61798         /**
61799          * @event mousedown
61800          * The raw mousedown event for the entire grid.
61801          * @param {Roo.EventObject} e
61802          */
61803         "mousedown" : true,
61804         /**
61805          * @event mouseup
61806          * The raw mouseup event for the entire grid.
61807          * @param {Roo.EventObject} e
61808          */
61809         "mouseup" : true,
61810         /**
61811          * @event mouseover
61812          * The raw mouseover event for the entire grid.
61813          * @param {Roo.EventObject} e
61814          */
61815         "mouseover" : true,
61816         /**
61817          * @event mouseout
61818          * The raw mouseout event for the entire grid.
61819          * @param {Roo.EventObject} e
61820          */
61821         "mouseout" : true,
61822         /**
61823          * @event keypress
61824          * The raw keypress event for the entire grid.
61825          * @param {Roo.EventObject} e
61826          */
61827         "keypress" : true,
61828         /**
61829          * @event keydown
61830          * The raw keydown event for the entire grid.
61831          * @param {Roo.EventObject} e
61832          */
61833         "keydown" : true,
61834
61835         // custom events
61836
61837         /**
61838          * @event cellclick
61839          * Fires when a cell is clicked
61840          * @param {Grid} this
61841          * @param {Number} rowIndex
61842          * @param {Number} columnIndex
61843          * @param {Roo.EventObject} e
61844          */
61845         "cellclick" : true,
61846         /**
61847          * @event celldblclick
61848          * Fires when a cell is double clicked
61849          * @param {Grid} this
61850          * @param {Number} rowIndex
61851          * @param {Number} columnIndex
61852          * @param {Roo.EventObject} e
61853          */
61854         "celldblclick" : true,
61855         /**
61856          * @event rowclick
61857          * Fires when a row is clicked
61858          * @param {Grid} this
61859          * @param {Number} rowIndex
61860          * @param {Roo.EventObject} e
61861          */
61862         "rowclick" : true,
61863         /**
61864          * @event rowdblclick
61865          * Fires when a row is double clicked
61866          * @param {Grid} this
61867          * @param {Number} rowIndex
61868          * @param {Roo.EventObject} e
61869          */
61870         "rowdblclick" : true,
61871         /**
61872          * @event headerclick
61873          * Fires when a header is clicked
61874          * @param {Grid} this
61875          * @param {Number} columnIndex
61876          * @param {Roo.EventObject} e
61877          */
61878         "headerclick" : true,
61879         /**
61880          * @event headerdblclick
61881          * Fires when a header cell is double clicked
61882          * @param {Grid} this
61883          * @param {Number} columnIndex
61884          * @param {Roo.EventObject} e
61885          */
61886         "headerdblclick" : true,
61887         /**
61888          * @event rowcontextmenu
61889          * Fires when a row is right clicked
61890          * @param {Grid} this
61891          * @param {Number} rowIndex
61892          * @param {Roo.EventObject} e
61893          */
61894         "rowcontextmenu" : true,
61895         /**
61896          * @event cellcontextmenu
61897          * Fires when a cell is right clicked
61898          * @param {Grid} this
61899          * @param {Number} rowIndex
61900          * @param {Number} cellIndex
61901          * @param {Roo.EventObject} e
61902          */
61903          "cellcontextmenu" : true,
61904         /**
61905          * @event headercontextmenu
61906          * Fires when a header is right clicked
61907          * @param {Grid} this
61908          * @param {Number} columnIndex
61909          * @param {Roo.EventObject} e
61910          */
61911         "headercontextmenu" : true,
61912         /**
61913          * @event bodyscroll
61914          * Fires when the body element is scrolled
61915          * @param {Number} scrollLeft
61916          * @param {Number} scrollTop
61917          */
61918         "bodyscroll" : true,
61919         /**
61920          * @event columnresize
61921          * Fires when the user resizes a column
61922          * @param {Number} columnIndex
61923          * @param {Number} newSize
61924          */
61925         "columnresize" : true,
61926         /**
61927          * @event columnmove
61928          * Fires when the user moves a column
61929          * @param {Number} oldIndex
61930          * @param {Number} newIndex
61931          */
61932         "columnmove" : true,
61933         /**
61934          * @event startdrag
61935          * Fires when row(s) start being dragged
61936          * @param {Grid} this
61937          * @param {Roo.GridDD} dd The drag drop object
61938          * @param {event} e The raw browser event
61939          */
61940         "startdrag" : true,
61941         /**
61942          * @event enddrag
61943          * Fires when a drag operation is complete
61944          * @param {Grid} this
61945          * @param {Roo.GridDD} dd The drag drop object
61946          * @param {event} e The raw browser event
61947          */
61948         "enddrag" : true,
61949         /**
61950          * @event dragdrop
61951          * Fires when dragged row(s) are dropped on a valid DD target
61952          * @param {Grid} this
61953          * @param {Roo.GridDD} dd The drag drop object
61954          * @param {String} targetId The target drag drop object
61955          * @param {event} e The raw browser event
61956          */
61957         "dragdrop" : true,
61958         /**
61959          * @event dragover
61960          * Fires while row(s) are being dragged. "targetId" is the id of the Yahoo.util.DD object the selected rows are being dragged over.
61961          * @param {Grid} this
61962          * @param {Roo.GridDD} dd The drag drop object
61963          * @param {String} targetId The target drag drop object
61964          * @param {event} e The raw browser event
61965          */
61966         "dragover" : true,
61967         /**
61968          * @event dragenter
61969          *  Fires when the dragged row(s) first cross another DD target while being dragged
61970          * @param {Grid} this
61971          * @param {Roo.GridDD} dd The drag drop object
61972          * @param {String} targetId The target drag drop object
61973          * @param {event} e The raw browser event
61974          */
61975         "dragenter" : true,
61976         /**
61977          * @event dragout
61978          * Fires when the dragged row(s) leave another DD target while being dragged
61979          * @param {Grid} this
61980          * @param {Roo.GridDD} dd The drag drop object
61981          * @param {String} targetId The target drag drop object
61982          * @param {event} e The raw browser event
61983          */
61984         "dragout" : true,
61985         /**
61986          * @event rowclass
61987          * Fires when a row is rendered, so you can change add a style to it.
61988          * @param {GridView} gridview   The grid view
61989          * @param {Object} rowcfg   contains record  rowIndex and rowClass - set rowClass to add a style.
61990          */
61991         'rowclass' : true,
61992
61993         /**
61994          * @event render
61995          * Fires when the grid is rendered
61996          * @param {Grid} grid
61997          */
61998         'render' : true,
61999             /**
62000              * @event select
62001              * Fires when a date is selected
62002              * @param {DatePicker} this
62003              * @param {Date} date The selected date
62004              */
62005         'select': true,
62006         /**
62007              * @event monthchange
62008              * Fires when the displayed month changes 
62009              * @param {DatePicker} this
62010              * @param {Date} date The selected month
62011              */
62012         'monthchange': true,
62013         /**
62014              * @event evententer
62015              * Fires when mouse over an event
62016              * @param {Calendar} this
62017              * @param {event} Event
62018              */
62019         'evententer': true,
62020         /**
62021              * @event eventleave
62022              * Fires when the mouse leaves an
62023              * @param {Calendar} this
62024              * @param {event}
62025              */
62026         'eventleave': true,
62027         /**
62028              * @event eventclick
62029              * Fires when the mouse click an
62030              * @param {Calendar} this
62031              * @param {event}
62032              */
62033         'eventclick': true,
62034         /**
62035              * @event eventrender
62036              * Fires before each cell is rendered, so you can modify the contents, like cls / title / qtip
62037              * @param {Calendar} this
62038              * @param {data} data to be modified
62039              */
62040         'eventrender': true
62041         
62042     });
62043
62044     Roo.grid.Grid.superclass.constructor.call(this);
62045     this.on('render', function() {
62046         this.view.el.addClass('x-grid-cal'); 
62047         
62048         (function() { this.setDate(new Date()); }).defer(100,this); //default today..
62049
62050     },this);
62051     
62052     if (!Roo.grid.Calendar.style) {
62053         Roo.grid.Calendar.style = Roo.util.CSS.createStyleSheet({
62054             
62055             
62056             '.x-grid-cal .x-grid-col' :  {
62057                 height: 'auto !important',
62058                 'vertical-align': 'top'
62059             },
62060             '.x-grid-cal  .fc-event-hori' : {
62061                 height: '14px'
62062             }
62063              
62064             
62065         }, Roo.id());
62066     }
62067
62068     
62069     
62070 };
62071 Roo.extend(Roo.grid.Calendar, Roo.grid.Grid, {
62072     /**
62073      * @cfg {Store} eventStore The store that loads events.
62074      */
62075     eventStore : 25,
62076
62077      
62078     activeDate : false,
62079     startDay : 0,
62080     autoWidth : true,
62081     monitorWindowResize : false,
62082
62083     
62084     resizeColumns : function() {
62085         var col = (this.view.el.getWidth() / 7) - 3;
62086         // loop through cols, and setWidth
62087         for(var i =0 ; i < 7 ; i++){
62088             this.cm.setColumnWidth(i, col);
62089         }
62090     },
62091      setDate :function(date) {
62092         
62093         Roo.log('setDate?');
62094         
62095         this.resizeColumns();
62096         var vd = this.activeDate;
62097         this.activeDate = date;
62098 //        if(vd && this.el){
62099 //            var t = date.getTime();
62100 //            if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
62101 //                Roo.log('using add remove');
62102 //                
62103 //                this.fireEvent('monthchange', this, date);
62104 //                
62105 //                this.cells.removeClass("fc-state-highlight");
62106 //                this.cells.each(function(c){
62107 //                   if(c.dateValue == t){
62108 //                       c.addClass("fc-state-highlight");
62109 //                       setTimeout(function(){
62110 //                            try{c.dom.firstChild.focus();}catch(e){}
62111 //                       }, 50);
62112 //                       return false;
62113 //                   }
62114 //                   return true;
62115 //                });
62116 //                return;
62117 //            }
62118 //        }
62119         
62120         var days = date.getDaysInMonth();
62121         
62122         var firstOfMonth = date.getFirstDateOfMonth();
62123         var startingPos = firstOfMonth.getDay()-this.startDay;
62124         
62125         if(startingPos < this.startDay){
62126             startingPos += 7;
62127         }
62128         
62129         var pm = date.add(Date.MONTH, -1);
62130         var prevStart = pm.getDaysInMonth()-startingPos;
62131 //        
62132         
62133         
62134         this.cells = this.view.el.select('.x-grid-row .x-grid-col',true);
62135         
62136         this.textNodes = this.view.el.query('.x-grid-row .x-grid-col .x-grid-cell-text');
62137         //this.cells.addClassOnOver('fc-state-hover');
62138         
62139         var cells = this.cells.elements;
62140         var textEls = this.textNodes;
62141         
62142         //Roo.each(cells, function(cell){
62143         //    cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
62144         //});
62145         
62146         days += startingPos;
62147
62148         // convert everything to numbers so it's fast
62149         var day = 86400000;
62150         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
62151         //Roo.log(d);
62152         //Roo.log(pm);
62153         //Roo.log(prevStart);
62154         
62155         var today = new Date().clearTime().getTime();
62156         var sel = date.clearTime().getTime();
62157         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
62158         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
62159         var ddMatch = this.disabledDatesRE;
62160         var ddText = this.disabledDatesText;
62161         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
62162         var ddaysText = this.disabledDaysText;
62163         var format = this.format;
62164         
62165         var setCellClass = function(cal, cell){
62166             
62167             //Roo.log('set Cell Class');
62168             cell.title = "";
62169             var t = d.getTime();
62170             
62171             //Roo.log(d);
62172             
62173             
62174             cell.dateValue = t;
62175             if(t == today){
62176                 cell.className += " fc-today";
62177                 cell.className += " fc-state-highlight";
62178                 cell.title = cal.todayText;
62179             }
62180             if(t == sel){
62181                 // disable highlight in other month..
62182                 cell.className += " fc-state-highlight";
62183                 
62184             }
62185             // disabling
62186             if(t < min) {
62187                 //cell.className = " fc-state-disabled";
62188                 cell.title = cal.minText;
62189                 return;
62190             }
62191             if(t > max) {
62192                 //cell.className = " fc-state-disabled";
62193                 cell.title = cal.maxText;
62194                 return;
62195             }
62196             if(ddays){
62197                 if(ddays.indexOf(d.getDay()) != -1){
62198                     // cell.title = ddaysText;
62199                    // cell.className = " fc-state-disabled";
62200                 }
62201             }
62202             if(ddMatch && format){
62203                 var fvalue = d.dateFormat(format);
62204                 if(ddMatch.test(fvalue)){
62205                     cell.title = ddText.replace("%0", fvalue);
62206                    cell.className = " fc-state-disabled";
62207                 }
62208             }
62209             
62210             if (!cell.initialClassName) {
62211                 cell.initialClassName = cell.dom.className;
62212             }
62213             
62214             cell.dom.className = cell.initialClassName  + ' ' +  cell.className;
62215         };
62216
62217         var i = 0;
62218         
62219         for(; i < startingPos; i++) {
62220             cells[i].dayName =  (++prevStart);
62221             Roo.log(textEls[i]);
62222             d.setDate(d.getDate()+1);
62223             
62224             //cells[i].className = "fc-past fc-other-month";
62225             setCellClass(this, cells[i]);
62226         }
62227         
62228         var intDay = 0;
62229         
62230         for(; i < days; i++){
62231             intDay = i - startingPos + 1;
62232             cells[i].dayName =  (intDay);
62233             d.setDate(d.getDate()+1);
62234             
62235             cells[i].className = ''; // "x-date-active";
62236             setCellClass(this, cells[i]);
62237         }
62238         var extraDays = 0;
62239         
62240         for(; i < 42; i++) {
62241             //textEls[i].innerHTML = (++extraDays);
62242             
62243             d.setDate(d.getDate()+1);
62244             cells[i].dayName = (++extraDays);
62245             cells[i].className = "fc-future fc-other-month";
62246             setCellClass(this, cells[i]);
62247         }
62248         
62249         //this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
62250         
62251         var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
62252         
62253         // this will cause all the cells to mis
62254         var rows= [];
62255         var i =0;
62256         for (var r = 0;r < 6;r++) {
62257             for (var c =0;c < 7;c++) {
62258                 this.ds.getAt(r).set('weekday' + c ,cells[i++].dayName );
62259             }    
62260         }
62261         
62262         this.cells = this.view.el.select('.x-grid-row .x-grid-col',true);
62263         for(i=0;i<cells.length;i++) {
62264             
62265             this.cells.elements[i].dayName = cells[i].dayName ;
62266             this.cells.elements[i].className = cells[i].className;
62267             this.cells.elements[i].initialClassName = cells[i].initialClassName ;
62268             this.cells.elements[i].title = cells[i].title ;
62269             this.cells.elements[i].dateValue = cells[i].dateValue ;
62270         }
62271         
62272         
62273         
62274         
62275         //this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
62276         //this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
62277         
62278         ////if(totalRows != 6){
62279             //this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
62280            // this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
62281        // }
62282         
62283         this.fireEvent('monthchange', this, date);
62284         
62285         
62286     },
62287  /**
62288      * Returns the grid's SelectionModel.
62289      * @return {SelectionModel}
62290      */
62291     getSelectionModel : function(){
62292         if(!this.selModel){
62293             this.selModel = new Roo.grid.CellSelectionModel();
62294         }
62295         return this.selModel;
62296     },
62297
62298     load: function() {
62299         this.eventStore.load()
62300         
62301         
62302         
62303     },
62304     
62305     findCell : function(dt) {
62306         dt = dt.clearTime().getTime();
62307         var ret = false;
62308         this.cells.each(function(c){
62309             //Roo.log("check " +c.dateValue + '?=' + dt);
62310             if(c.dateValue == dt){
62311                 ret = c;
62312                 return false;
62313             }
62314             return true;
62315         });
62316         
62317         return ret;
62318     },
62319     
62320     findCells : function(rec) {
62321         var s = rec.data.start_dt.clone().clearTime().getTime();
62322        // Roo.log(s);
62323         var e= rec.data.end_dt.clone().clearTime().getTime();
62324        // Roo.log(e);
62325         var ret = [];
62326         this.cells.each(function(c){
62327              ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
62328             
62329             if(c.dateValue > e){
62330                 return ;
62331             }
62332             if(c.dateValue < s){
62333                 return ;
62334             }
62335             ret.push(c);
62336         });
62337         
62338         return ret;    
62339     },
62340     
62341     findBestRow: function(cells)
62342     {
62343         var ret = 0;
62344         
62345         for (var i =0 ; i < cells.length;i++) {
62346             ret  = Math.max(cells[i].rows || 0,ret);
62347         }
62348         return ret;
62349         
62350     },
62351     
62352     
62353     addItem : function(rec)
62354     {
62355         // look for vertical location slot in
62356         var cells = this.findCells(rec);
62357         
62358         rec.row = this.findBestRow(cells);
62359         
62360         // work out the location.
62361         
62362         var crow = false;
62363         var rows = [];
62364         for(var i =0; i < cells.length; i++) {
62365             if (!crow) {
62366                 crow = {
62367                     start : cells[i],
62368                     end :  cells[i]
62369                 };
62370                 continue;
62371             }
62372             if (crow.start.getY() == cells[i].getY()) {
62373                 // on same row.
62374                 crow.end = cells[i];
62375                 continue;
62376             }
62377             // different row.
62378             rows.push(crow);
62379             crow = {
62380                 start: cells[i],
62381                 end : cells[i]
62382             };
62383             
62384         }
62385         
62386         rows.push(crow);
62387         rec.els = [];
62388         rec.rows = rows;
62389         rec.cells = cells;
62390         for (var i = 0; i < cells.length;i++) {
62391             cells[i].rows = Math.max(cells[i].rows || 0 , rec.row + 1 );
62392             
62393         }
62394         
62395         
62396     },
62397     
62398     clearEvents: function() {
62399         
62400         if (!this.eventStore.getCount()) {
62401             return;
62402         }
62403         // reset number of rows in cells.
62404         Roo.each(this.cells.elements, function(c){
62405             c.rows = 0;
62406         });
62407         
62408         this.eventStore.each(function(e) {
62409             this.clearEvent(e);
62410         },this);
62411         
62412     },
62413     
62414     clearEvent : function(ev)
62415     {
62416         if (ev.els) {
62417             Roo.each(ev.els, function(el) {
62418                 el.un('mouseenter' ,this.onEventEnter, this);
62419                 el.un('mouseleave' ,this.onEventLeave, this);
62420                 el.remove();
62421             },this);
62422             ev.els = [];
62423         }
62424     },
62425     
62426     
62427     renderEvent : function(ev,ctr) {
62428         if (!ctr) {
62429              ctr = this.view.el.select('.fc-event-container',true).first();
62430         }
62431         
62432          
62433         this.clearEvent(ev);
62434             //code
62435        
62436         
62437         
62438         ev.els = [];
62439         var cells = ev.cells;
62440         var rows = ev.rows;
62441         this.fireEvent('eventrender', this, ev);
62442         
62443         for(var i =0; i < rows.length; i++) {
62444             
62445             cls = '';
62446             if (i == 0) {
62447                 cls += ' fc-event-start';
62448             }
62449             if ((i+1) == rows.length) {
62450                 cls += ' fc-event-end';
62451             }
62452             
62453             //Roo.log(ev.data);
62454             // how many rows should it span..
62455             var cg = this.eventTmpl.append(ctr,Roo.apply({
62456                 fccls : cls
62457                 
62458             }, ev.data) , true);
62459             
62460             
62461             cg.on('mouseenter' ,this.onEventEnter, this, ev);
62462             cg.on('mouseleave' ,this.onEventLeave, this, ev);
62463             cg.on('click', this.onEventClick, this, ev);
62464             
62465             ev.els.push(cg);
62466             
62467             var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
62468             var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
62469             //Roo.log(cg);
62470              
62471             cg.setXY([sbox.x +2, sbox.y +(ev.row * 20)]);    
62472             cg.setWidth(ebox.right - sbox.x -2);
62473         }
62474     },
62475     
62476     renderEvents: function()
62477     {   
62478         // first make sure there is enough space..
62479         
62480         if (!this.eventTmpl) {
62481             this.eventTmpl = new Roo.Template(
62482                 '<div class="roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable {fccls} {cls}"  style="position: absolute" unselectable="on">' +
62483                     '<div class="fc-event-inner">' +
62484                         '<span class="fc-event-time">{time}</span>' +
62485                         '<span class="fc-event-title" qtip="{qtip}">{title}</span>' +
62486                     '</div>' +
62487                     '<div class="ui-resizable-heandle ui-resizable-e">&nbsp;&nbsp;&nbsp;</div>' +
62488                 '</div>'
62489             );
62490                 
62491         }
62492                
62493         
62494         
62495         this.cells.each(function(c) {
62496             //Roo.log(c.select('.fc-day-content div',true).first());
62497             c.select('.fc-day-content div',true).first().setHeight(Math.max(34, (c.rows || 1) * 20));
62498         });
62499         
62500         var ctr = this.view.el.select('.fc-event-container',true).first();
62501         
62502         var cls;
62503         this.eventStore.each(function(ev){
62504             
62505             this.renderEvent(ev);
62506              
62507              
62508         }, this);
62509         this.view.layout();
62510         
62511     },
62512     
62513     onEventEnter: function (e, el,event,d) {
62514         this.fireEvent('evententer', this, el, event);
62515     },
62516     
62517     onEventLeave: function (e, el,event,d) {
62518         this.fireEvent('eventleave', this, el, event);
62519     },
62520     
62521     onEventClick: function (e, el,event,d) {
62522         this.fireEvent('eventclick', this, el, event);
62523     },
62524     
62525     onMonthChange: function () {
62526         this.store.load();
62527     },
62528     
62529     onLoad: function () {
62530         
62531         //Roo.log('calendar onload');
62532 //         
62533         if(this.eventStore.getCount() > 0){
62534             
62535            
62536             
62537             this.eventStore.each(function(d){
62538                 
62539                 
62540                 // FIXME..
62541                 var add =   d.data;
62542                 if (typeof(add.end_dt) == 'undefined')  {
62543                     Roo.log("Missing End time in calendar data: ");
62544                     Roo.log(d);
62545                     return;
62546                 }
62547                 if (typeof(add.start_dt) == 'undefined')  {
62548                     Roo.log("Missing Start time in calendar data: ");
62549                     Roo.log(d);
62550                     return;
62551                 }
62552                 add.start_dt = typeof(add.start_dt) == 'string' ? Date.parseDate(add.start_dt,'Y-m-d H:i:s') : add.start_dt,
62553                 add.end_dt = typeof(add.end_dt) == 'string' ? Date.parseDate(add.end_dt,'Y-m-d H:i:s') : add.end_dt,
62554                 add.id = add.id || d.id;
62555                 add.title = add.title || '??';
62556                 
62557                 this.addItem(d);
62558                 
62559              
62560             },this);
62561         }
62562         
62563         this.renderEvents();
62564     }
62565     
62566
62567 });
62568 /*
62569  grid : {
62570                 xtype: 'Grid',
62571                 xns: Roo.grid,
62572                 listeners : {
62573                     render : function ()
62574                     {
62575                         _this.grid = this;
62576                         
62577                         if (!this.view.el.hasClass('course-timesheet')) {
62578                             this.view.el.addClass('course-timesheet');
62579                         }
62580                         if (this.tsStyle) {
62581                             this.ds.load({});
62582                             return; 
62583                         }
62584                         Roo.log('width');
62585                         Roo.log(_this.grid.view.el.getWidth());
62586                         
62587                         
62588                         this.tsStyle =  Roo.util.CSS.createStyleSheet({
62589                             '.course-timesheet .x-grid-row' : {
62590                                 height: '80px'
62591                             },
62592                             '.x-grid-row td' : {
62593                                 'vertical-align' : 0
62594                             },
62595                             '.course-edit-link' : {
62596                                 'color' : 'blue',
62597                                 'text-overflow' : 'ellipsis',
62598                                 'overflow' : 'hidden',
62599                                 'white-space' : 'nowrap',
62600                                 'cursor' : 'pointer'
62601                             },
62602                             '.sub-link' : {
62603                                 'color' : 'green'
62604                             },
62605                             '.de-act-sup-link' : {
62606                                 'color' : 'purple',
62607                                 'text-decoration' : 'line-through'
62608                             },
62609                             '.de-act-link' : {
62610                                 'color' : 'red',
62611                                 'text-decoration' : 'line-through'
62612                             },
62613                             '.course-timesheet .course-highlight' : {
62614                                 'border-top-style': 'dashed !important',
62615                                 'border-bottom-bottom': 'dashed !important'
62616                             },
62617                             '.course-timesheet .course-item' : {
62618                                 'font-family'   : 'tahoma, arial, helvetica',
62619                                 'font-size'     : '11px',
62620                                 'overflow'      : 'hidden',
62621                                 'padding-left'  : '10px',
62622                                 'padding-right' : '10px',
62623                                 'padding-top' : '10px' 
62624                             }
62625                             
62626                         }, Roo.id());
62627                                 this.ds.load({});
62628                     }
62629                 },
62630                 autoWidth : true,
62631                 monitorWindowResize : false,
62632                 cellrenderer : function(v,x,r)
62633                 {
62634                     return v;
62635                 },
62636                 sm : {
62637                     xtype: 'CellSelectionModel',
62638                     xns: Roo.grid
62639                 },
62640                 dataSource : {
62641                     xtype: 'Store',
62642                     xns: Roo.data,
62643                     listeners : {
62644                         beforeload : function (_self, options)
62645                         {
62646                             options.params = options.params || {};
62647                             options.params._month = _this.monthField.getValue();
62648                             options.params.limit = 9999;
62649                             options.params['sort'] = 'when_dt';    
62650                             options.params['dir'] = 'ASC';    
62651                             this.proxy.loadResponse = this.loadResponse;
62652                             Roo.log("load?");
62653                             //this.addColumns();
62654                         },
62655                         load : function (_self, records, options)
62656                         {
62657                             _this.grid.view.el.select('.course-edit-link', true).on('click', function() {
62658                                 // if you click on the translation.. you can edit it...
62659                                 var el = Roo.get(this);
62660                                 var id = el.dom.getAttribute('data-id');
62661                                 var d = el.dom.getAttribute('data-date');
62662                                 var t = el.dom.getAttribute('data-time');
62663                                 //var id = this.child('span').dom.textContent;
62664                                 
62665                                 //Roo.log(this);
62666                                 Pman.Dialog.CourseCalendar.show({
62667                                     id : id,
62668                                     when_d : d,
62669                                     when_t : t,
62670                                     productitem_active : id ? 1 : 0
62671                                 }, function() {
62672                                     _this.grid.ds.load({});
62673                                 });
62674                            
62675                            });
62676                            
62677                            _this.panel.fireEvent('resize', [ '', '' ]);
62678                         }
62679                     },
62680                     loadResponse : function(o, success, response){
62681                             // this is overridden on before load..
62682                             
62683                             Roo.log("our code?");       
62684                             //Roo.log(success);
62685                             //Roo.log(response)
62686                             delete this.activeRequest;
62687                             if(!success){
62688                                 this.fireEvent("loadexception", this, o, response);
62689                                 o.request.callback.call(o.request.scope, null, o.request.arg, false);
62690                                 return;
62691                             }
62692                             var result;
62693                             try {
62694                                 result = o.reader.read(response);
62695                             }catch(e){
62696                                 Roo.log("load exception?");
62697                                 this.fireEvent("loadexception", this, o, response, e);
62698                                 o.request.callback.call(o.request.scope, null, o.request.arg, false);
62699                                 return;
62700                             }
62701                             Roo.log("ready...");        
62702                             // loop through result.records;
62703                             // and set this.tdate[date] = [] << array of records..
62704                             _this.tdata  = {};
62705                             Roo.each(result.records, function(r){
62706                                 //Roo.log(r.data);
62707                                 if(typeof(_this.tdata[r.data.when_dt.format('j')]) == 'undefined'){
62708                                     _this.tdata[r.data.when_dt.format('j')] = [];
62709                                 }
62710                                 _this.tdata[r.data.when_dt.format('j')].push(r.data);
62711                             });
62712                             
62713                             //Roo.log(_this.tdata);
62714                             
62715                             result.records = [];
62716                             result.totalRecords = 6;
62717                     
62718                             // let's generate some duumy records for the rows.
62719                             //var st = _this.dateField.getValue();
62720                             
62721                             // work out monday..
62722                             //st = st.add(Date.DAY, -1 * st.format('w'));
62723                             
62724                             var date = Date.parseDate(_this.monthField.getValue(), "Y-m-d");
62725                             
62726                             var firstOfMonth = date.getFirstDayOfMonth();
62727                             var days = date.getDaysInMonth();
62728                             var d = 1;
62729                             var firstAdded = false;
62730                             for (var i = 0; i < result.totalRecords ; i++) {
62731                                 //var d= st.add(Date.DAY, i);
62732                                 var row = {};
62733                                 var added = 0;
62734                                 for(var w = 0 ; w < 7 ; w++){
62735                                     if(!firstAdded && firstOfMonth != w){
62736                                         continue;
62737                                     }
62738                                     if(d > days){
62739                                         continue;
62740                                     }
62741                                     firstAdded = true;
62742                                     var dd = (d > 0 && d < 10) ? "0"+d : d;
62743                                     row['weekday'+w] = String.format(
62744                                                     '<span style="font-size: 16px;"><b>{0}</b></span>'+
62745                                                     '<span class="course-edit-link" style="color:blue;" data-id="0" data-date="{1}"> Add New</span>',
62746                                                     d,
62747                                                     date.format('Y-m-')+dd
62748                                                 );
62749                                     added++;
62750                                     if(typeof(_this.tdata[d]) != 'undefined'){
62751                                         Roo.each(_this.tdata[d], function(r){
62752                                             var is_sub = '';
62753                                             var deactive = '';
62754                                             var id = r.id;
62755                                             var desc = (r.productitem_id_descrip) ? r.productitem_id_descrip : '';
62756                                             if(r.parent_id*1>0){
62757                                                 is_sub = (r.productitem_id_visible*1 < 1) ? 'de-act-sup-link' :'sub-link';
62758                                                 id = r.parent_id;
62759                                             }
62760                                             if(r.productitem_id_visible*1 < 1 && r.parent_id*1 < 1){
62761                                                 deactive = 'de-act-link';
62762                                             }
62763                                             
62764                                             row['weekday'+w] += String.format(
62765                                                     '<br /><span class="course-edit-link {3} {4}" qtip="{5}" data-id="{0}">{2} - {1}</span>',
62766                                                     id, //0
62767                                                     r.product_id_name, //1
62768                                                     r.when_dt.format('h:ia'), //2
62769                                                     is_sub, //3
62770                                                     deactive, //4
62771                                                     desc // 5
62772                                             );
62773                                         });
62774                                     }
62775                                     d++;
62776                                 }
62777                                 
62778                                 // only do this if something added..
62779                                 if(added > 0){ 
62780                                     result.records.push(_this.grid.dataSource.reader.newRow(row));
62781                                 }
62782                                 
62783                                 
62784                                 // push it twice. (second one with an hour..
62785                                 
62786                             }
62787                             //Roo.log(result);
62788                             this.fireEvent("load", this, o, o.request.arg);
62789                             o.request.callback.call(o.request.scope, result, o.request.arg, true);
62790                         },
62791                     sortInfo : {field: 'when_dt', direction : 'ASC' },
62792                     proxy : {
62793                         xtype: 'HttpProxy',
62794                         xns: Roo.data,
62795                         method : 'GET',
62796                         url : baseURL + '/Roo/Shop_course.php'
62797                     },
62798                     reader : {
62799                         xtype: 'JsonReader',
62800                         xns: Roo.data,
62801                         id : 'id',
62802                         fields : [
62803                             {
62804                                 'name': 'id',
62805                                 'type': 'int'
62806                             },
62807                             {
62808                                 'name': 'when_dt',
62809                                 'type': 'string'
62810                             },
62811                             {
62812                                 'name': 'end_dt',
62813                                 'type': 'string'
62814                             },
62815                             {
62816                                 'name': 'parent_id',
62817                                 'type': 'int'
62818                             },
62819                             {
62820                                 'name': 'product_id',
62821                                 'type': 'int'
62822                             },
62823                             {
62824                                 'name': 'productitem_id',
62825                                 'type': 'int'
62826                             },
62827                             {
62828                                 'name': 'guid',
62829                                 'type': 'int'
62830                             }
62831                         ]
62832                     }
62833                 },
62834                 toolbar : {
62835                     xtype: 'Toolbar',
62836                     xns: Roo,
62837                     items : [
62838                         {
62839                             xtype: 'Button',
62840                             xns: Roo.Toolbar,
62841                             listeners : {
62842                                 click : function (_self, e)
62843                                 {
62844                                     var sd = Date.parseDate(_this.monthField.getValue(), "Y-m-d");
62845                                     sd.setMonth(sd.getMonth()-1);
62846                                     _this.monthField.setValue(sd.format('Y-m-d'));
62847                                     _this.grid.ds.load({});
62848                                 }
62849                             },
62850                             text : "Back"
62851                         },
62852                         {
62853                             xtype: 'Separator',
62854                             xns: Roo.Toolbar
62855                         },
62856                         {
62857                             xtype: 'MonthField',
62858                             xns: Roo.form,
62859                             listeners : {
62860                                 render : function (_self)
62861                                 {
62862                                     _this.monthField = _self;
62863                                    // _this.monthField.set  today
62864                                 },
62865                                 select : function (combo, date)
62866                                 {
62867                                     _this.grid.ds.load({});
62868                                 }
62869                             },
62870                             value : (function() { return new Date(); })()
62871                         },
62872                         {
62873                             xtype: 'Separator',
62874                             xns: Roo.Toolbar
62875                         },
62876                         {
62877                             xtype: 'TextItem',
62878                             xns: Roo.Toolbar,
62879                             text : "Blue: in-active, green: in-active sup-event, red: de-active, purple: de-active sup-event"
62880                         },
62881                         {
62882                             xtype: 'Fill',
62883                             xns: Roo.Toolbar
62884                         },
62885                         {
62886                             xtype: 'Button',
62887                             xns: Roo.Toolbar,
62888                             listeners : {
62889                                 click : function (_self, e)
62890                                 {
62891                                     var sd = Date.parseDate(_this.monthField.getValue(), "Y-m-d");
62892                                     sd.setMonth(sd.getMonth()+1);
62893                                     _this.monthField.setValue(sd.format('Y-m-d'));
62894                                     _this.grid.ds.load({});
62895                                 }
62896                             },
62897                             text : "Next"
62898                         }
62899                     ]
62900                 },
62901                  
62902             }
62903         };
62904         
62905         *//*
62906  * Based on:
62907  * Ext JS Library 1.1.1
62908  * Copyright(c) 2006-2007, Ext JS, LLC.
62909  *
62910  * Originally Released Under LGPL - original licence link has changed is not relivant.
62911  *
62912  * Fork - LGPL
62913  * <script type="text/javascript">
62914  */
62915  
62916 /**
62917  * @class Roo.LoadMask
62918  * A simple utility class for generically masking elements while loading data.  If the element being masked has
62919  * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
62920  * process and the mask element will be cached for reuse.  For all other elements, this mask will replace the
62921  * element's UpdateManager load indicator and will be destroyed after the initial load.
62922  * @constructor
62923  * Create a new LoadMask
62924  * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
62925  * @param {Object} config The config object
62926  */
62927 Roo.LoadMask = function(el, config){
62928     this.el = Roo.get(el);
62929     Roo.apply(this, config);
62930     if(this.store){
62931         this.store.on('beforeload', this.onBeforeLoad, this);
62932         this.store.on('load', this.onLoad, this);
62933         this.store.on('loadexception', this.onLoadException, this);
62934         this.removeMask = false;
62935     }else{
62936         var um = this.el.getUpdateManager();
62937         um.showLoadIndicator = false; // disable the default indicator
62938         um.on('beforeupdate', this.onBeforeLoad, this);
62939         um.on('update', this.onLoad, this);
62940         um.on('failure', this.onLoad, this);
62941         this.removeMask = true;
62942     }
62943 };
62944
62945 Roo.LoadMask.prototype = {
62946     /**
62947      * @cfg {Boolean} removeMask
62948      * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
62949      * False to persist the mask element reference for multiple uses (e.g., for paged data widgets).  Defaults to false.
62950      */
62951     removeMask : false,
62952     /**
62953      * @cfg {String} msg
62954      * The text to display in a centered loading message box (defaults to 'Loading...')
62955      */
62956     msg : 'Loading...',
62957     /**
62958      * @cfg {String} msgCls
62959      * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
62960      */
62961     msgCls : 'x-mask-loading',
62962
62963     /**
62964      * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
62965      * @type Boolean
62966      */
62967     disabled: false,
62968
62969     /**
62970      * Disables the mask to prevent it from being displayed
62971      */
62972     disable : function(){
62973        this.disabled = true;
62974     },
62975
62976     /**
62977      * Enables the mask so that it can be displayed
62978      */
62979     enable : function(){
62980         this.disabled = false;
62981     },
62982     
62983     onLoadException : function()
62984     {
62985         Roo.log(arguments);
62986         
62987         if (typeof(arguments[3]) != 'undefined') {
62988             Roo.MessageBox.alert("Error loading",arguments[3]);
62989         } 
62990         /*
62991         try {
62992             if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
62993                 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
62994             }   
62995         } catch(e) {
62996             
62997         }
62998         */
62999     
63000         (function() { this.el.unmask(this.removeMask); }).defer(50, this);
63001     },
63002     // private
63003     onLoad : function()
63004     {
63005         (function() { this.el.unmask(this.removeMask); }).defer(50, this);
63006     },
63007
63008     // private
63009     onBeforeLoad : function(){
63010         if(!this.disabled){
63011             (function() { this.el.mask(this.msg, this.msgCls); }).defer(50, this);
63012         }
63013     },
63014
63015     // private
63016     destroy : function(){
63017         if(this.store){
63018             this.store.un('beforeload', this.onBeforeLoad, this);
63019             this.store.un('load', this.onLoad, this);
63020             this.store.un('loadexception', this.onLoadException, this);
63021         }else{
63022             var um = this.el.getUpdateManager();
63023             um.un('beforeupdate', this.onBeforeLoad, this);
63024             um.un('update', this.onLoad, this);
63025             um.un('failure', this.onLoad, this);
63026         }
63027     }
63028 };/*
63029  * Based on:
63030  * Ext JS Library 1.1.1
63031  * Copyright(c) 2006-2007, Ext JS, LLC.
63032  *
63033  * Originally Released Under LGPL - original licence link has changed is not relivant.
63034  *
63035  * Fork - LGPL
63036  * <script type="text/javascript">
63037  */
63038
63039
63040 /**
63041  * @class Roo.XTemplate
63042  * @extends Roo.Template
63043  * Provides a template that can have nested templates for loops or conditionals. The syntax is:
63044 <pre><code>
63045 var t = new Roo.XTemplate(
63046         '&lt;select name="{name}"&gt;',
63047                 '&lt;tpl for="options"&gt;&lt;option value="{value:trim}"&gt;{text:ellipsis(10)}&lt;/option&gt;&lt;/tpl&gt;',
63048         '&lt;/select&gt;'
63049 );
63050  
63051 // then append, applying the master template values
63052  </code></pre>
63053  *
63054  * Supported features:
63055  *
63056  *  Tags:
63057
63058 <pre><code>
63059       {a_variable} - output encoded.
63060       {a_variable.format:("Y-m-d")} - call a method on the variable
63061       {a_variable:raw} - unencoded output
63062       {a_variable:toFixed(1,2)} - Roo.util.Format."toFixed"
63063       {a_variable:this.method_on_template(...)} - call a method on the template object.
63064  
63065 </code></pre>
63066  *  The tpl tag:
63067 <pre><code>
63068         &lt;tpl for="a_variable or condition.."&gt;&lt;/tpl&gt;
63069         &lt;tpl if="a_variable or condition"&gt;&lt;/tpl&gt;
63070         &lt;tpl exec="some javascript"&gt;&lt;/tpl&gt;
63071         &lt;tpl name="named_template"&gt;&lt;/tpl&gt; (experimental)
63072   
63073         &lt;tpl for="."&gt;&lt;/tpl&gt; - just iterate the property..
63074         &lt;tpl for=".."&gt;&lt;/tpl&gt; - iterates with the parent (probably the template) 
63075 </code></pre>
63076  *      
63077  */
63078 Roo.XTemplate = function()
63079 {
63080     Roo.XTemplate.superclass.constructor.apply(this, arguments);
63081     if (this.html) {
63082         this.compile();
63083     }
63084 };
63085
63086
63087 Roo.extend(Roo.XTemplate, Roo.Template, {
63088
63089     /**
63090      * The various sub templates
63091      */
63092     tpls : false,
63093     /**
63094      *
63095      * basic tag replacing syntax
63096      * WORD:WORD()
63097      *
63098      * // you can fake an object call by doing this
63099      *  x.t:(test,tesT) 
63100      * 
63101      */
63102     re : /\{([\w-\.]+)(?:\:([\w\.]*)(?:\((.*?)?\))?)?\}/g,
63103
63104     /**
63105      * compile the template
63106      *
63107      * This is not recursive, so I'm not sure how nested templates are really going to be handled..
63108      *
63109      */
63110     compile: function()
63111     {
63112         var s = this.html;
63113      
63114         s = ['<tpl>', s, '</tpl>'].join('');
63115     
63116         var re     = /<tpl\b[^>]*>((?:(?=([^<]+))\2|<(?!tpl\b[^>]*>))*?)<\/tpl>/,
63117             nameRe = /^<tpl\b[^>]*?for="(.*?)"/,
63118             ifRe   = /^<tpl\b[^>]*?if="(.*?)"/,
63119             execRe = /^<tpl\b[^>]*?exec="(.*?)"/,
63120             namedRe = /^<tpl\b[^>]*?name="(\w+)"/,  // named templates..
63121             m,
63122             id     = 0,
63123             tpls   = [];
63124     
63125         while(true == !!(m = s.match(re))){
63126             var forMatch   = m[0].match(nameRe),
63127                 ifMatch   = m[0].match(ifRe),
63128                 execMatch   = m[0].match(execRe),
63129                 namedMatch   = m[0].match(namedRe),
63130                 
63131                 exp  = null, 
63132                 fn   = null,
63133                 exec = null,
63134                 name = forMatch && forMatch[1] ? forMatch[1] : '';
63135                 
63136             if (ifMatch) {
63137                 // if - puts fn into test..
63138                 exp = ifMatch && ifMatch[1] ? ifMatch[1] : null;
63139                 if(exp){
63140                    fn = new Function('values', 'parent', 'with(values){ return '+(Roo.util.Format.htmlDecode(exp))+'; }');
63141                 }
63142             }
63143             
63144             if (execMatch) {
63145                 // exec - calls a function... returns empty if true is  returned.
63146                 exp = execMatch && execMatch[1] ? execMatch[1] : null;
63147                 if(exp){
63148                    exec = new Function('values', 'parent', 'with(values){ '+(Roo.util.Format.htmlDecode(exp))+'; }');
63149                 }
63150             }
63151             
63152             
63153             if (name) {
63154                 // for = 
63155                 switch(name){
63156                     case '.':  name = new Function('values', 'parent', 'with(values){ return values; }'); break;
63157                     case '..': name = new Function('values', 'parent', 'with(values){ return parent; }'); break;
63158                     default:   name = new Function('values', 'parent', 'with(values){ return '+name+'; }');
63159                 }
63160             }
63161             var uid = namedMatch ? namedMatch[1] : id;
63162             
63163             
63164             tpls.push({
63165                 id:     namedMatch ? namedMatch[1] : id,
63166                 target: name,
63167                 exec:   exec,
63168                 test:   fn,
63169                 body:   m[1] || ''
63170             });
63171             if (namedMatch) {
63172                 s = s.replace(m[0], '');
63173             } else { 
63174                 s = s.replace(m[0], '{xtpl'+ id + '}');
63175             }
63176             ++id;
63177         }
63178         this.tpls = [];
63179         for(var i = tpls.length-1; i >= 0; --i){
63180             this.compileTpl(tpls[i]);
63181             this.tpls[tpls[i].id] = tpls[i];
63182         }
63183         this.master = tpls[tpls.length-1];
63184         return this;
63185     },
63186     /**
63187      * same as applyTemplate, except it's done to one of the subTemplates
63188      * when using named templates, you can do:
63189      *
63190      * var str = pl.applySubTemplate('your-name', values);
63191      *
63192      * 
63193      * @param {Number} id of the template
63194      * @param {Object} values to apply to template
63195      * @param {Object} parent (normaly the instance of this object)
63196      */
63197     applySubTemplate : function(id, values, parent)
63198     {
63199         
63200         
63201         var t = this.tpls[id];
63202         
63203         
63204         try { 
63205             if(t.test && !t.test.call(this, values, parent)){
63206                 return '';
63207             }
63208         } catch(e) {
63209             Roo.log("Xtemplate.applySubTemplate 'test': Exception thrown");
63210             Roo.log(e.toString());
63211             Roo.log(t.test);
63212             return ''
63213         }
63214         try { 
63215             
63216             if(t.exec && t.exec.call(this, values, parent)){
63217                 return '';
63218             }
63219         } catch(e) {
63220             Roo.log("Xtemplate.applySubTemplate 'exec': Exception thrown");
63221             Roo.log(e.toString());
63222             Roo.log(t.exec);
63223             return ''
63224         }
63225         try {
63226             var vs = t.target ? t.target.call(this, values, parent) : values;
63227             parent = t.target ? values : parent;
63228             if(t.target && vs instanceof Array){
63229                 var buf = [];
63230                 for(var i = 0, len = vs.length; i < len; i++){
63231                     buf[buf.length] = t.compiled.call(this, vs[i], parent);
63232                 }
63233                 return buf.join('');
63234             }
63235             return t.compiled.call(this, vs, parent);
63236         } catch (e) {
63237             Roo.log("Xtemplate.applySubTemplate : Exception thrown");
63238             Roo.log(e.toString());
63239             Roo.log(t.compiled);
63240             return '';
63241         }
63242     },
63243
63244     compileTpl : function(tpl)
63245     {
63246         var fm = Roo.util.Format;
63247         var useF = this.disableFormats !== true;
63248         var sep = Roo.isGecko ? "+" : ",";
63249         var undef = function(str) {
63250             Roo.log("Property not found :"  + str);
63251             return '';
63252         };
63253         
63254         var fn = function(m, name, format, args)
63255         {
63256             //Roo.log(arguments);
63257             args = args ? args.replace(/\\'/g,"'") : args;
63258             //["{TEST:(a,b,c)}", "TEST", "", "a,b,c", 0, "{TEST:(a,b,c)}"]
63259             if (typeof(format) == 'undefined') {
63260                 format= 'htmlEncode';
63261             }
63262             if (format == 'raw' ) {
63263                 format = false;
63264             }
63265             
63266             if(name.substr(0, 4) == 'xtpl'){
63267                 return "'"+ sep +'this.applySubTemplate('+name.substr(4)+', values, parent)'+sep+"'";
63268             }
63269             
63270             // build an array of options to determine if value is undefined..
63271             
63272             // basically get 'xxxx.yyyy' then do
63273             // (typeof(xxxx) == 'undefined' || typeof(xxx.yyyy) == 'undefined') ?
63274             //    (function () { Roo.log("Property not found"); return ''; })() :
63275             //    ......
63276             
63277             var udef_ar = [];
63278             var lookfor = '';
63279             Roo.each(name.split('.'), function(st) {
63280                 lookfor += (lookfor.length ? '.': '') + st;
63281                 udef_ar.push(  "(typeof(" + lookfor + ") == 'undefined')"  );
63282             });
63283             
63284             var udef_st = '((' + udef_ar.join(" || ") +") ? undef('" + name + "') : "; // .. needs )
63285             
63286             
63287             if(format && useF){
63288                 
63289                 args = args ? ',' + args : "";
63290                  
63291                 if(format.substr(0, 5) != "this."){
63292                     format = "fm." + format + '(';
63293                 }else{
63294                     format = 'this.call("'+ format.substr(5) + '", ';
63295                     args = ", values";
63296                 }
63297                 
63298                 return "'"+ sep +   udef_st   +    format + name + args + "))"+sep+"'";
63299             }
63300              
63301             if (args.length) {
63302                 // called with xxyx.yuu:(test,test)
63303                 // change to ()
63304                 return "'"+ sep + udef_st  + name + '(' +  args + "))"+sep+"'";
63305             }
63306             // raw.. - :raw modifier..
63307             return "'"+ sep + udef_st  + name + ")"+sep+"'";
63308             
63309         };
63310         var body;
63311         // branched to use + in gecko and [].join() in others
63312         if(Roo.isGecko){
63313             body = "tpl.compiled = function(values, parent){  with(values) { return '" +
63314                    tpl.body.replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn) +
63315                     "';};};";
63316         }else{
63317             body = ["tpl.compiled = function(values, parent){  with (values) { return ['"];
63318             body.push(tpl.body.replace(/(\r\n|\n)/g,
63319                             '\\n').replace(/'/g, "\\'").replace(this.re, fn));
63320             body.push("'].join('');};};");
63321             body = body.join('');
63322         }
63323         
63324         Roo.debug && Roo.log(body.replace(/\\n/,'\n'));
63325        
63326         /** eval:var:tpl eval:var:fm eval:var:useF eval:var:undef  */
63327         eval(body);
63328         
63329         return this;
63330     },
63331
63332     applyTemplate : function(values){
63333         return this.master.compiled.call(this, values, {});
63334         //var s = this.subs;
63335     },
63336
63337     apply : function(){
63338         return this.applyTemplate.apply(this, arguments);
63339     }
63340
63341  });
63342
63343 Roo.XTemplate.from = function(el){
63344     el = Roo.getDom(el);
63345     return new Roo.XTemplate(el.value || el.innerHTML);
63346 };