roojs-bootstrap.js
[roojs1] / roojs-debug.js
1 /*
2  * Based on:
3  * Ext JS Library 1.1.1
4  * Copyright(c) 2006-2007, Ext JS, LLC.
5  *
6  * Originally Released Under LGPL - original licence link has changed is not relivant.
7  *
8  * Fork - LGPL
9  * <script type="text/javascript">
10  */
11  
12
13
14
15
16 // for old browsers
17 window["undefined"] = window["undefined"];
18
19 /**
20  * @class Roo
21  * Roo core utilities and functions.
22  * @static
23  */
24 var Roo = {}; 
25 /**
26  * Copies all the properties of config to obj.
27  * @param {Object} obj The receiver of the properties
28  * @param {Object} config The source of the properties
29  * @param {Object} defaults A different object that will also be applied for default values
30  * @return {Object} returns obj
31  * @member Roo apply
32  */
33
34  
35 Roo.apply = function(o, c, defaults){
36     if(defaults){
37         // no "this" reference for friendly out of scope calls
38         Roo.apply(o, defaults);
39     }
40     if(o && c && typeof c == 'object'){
41         for(var p in c){
42             o[p] = c[p];
43         }
44     }
45     return o;
46 };
47
48
49 (function(){
50     var idSeed = 0;
51     var ua = navigator.userAgent.toLowerCase();
52
53     var isStrict = document.compatMode == "CSS1Compat",
54         isOpera = ua.indexOf("opera") > -1,
55         isSafari = (/webkit|khtml/).test(ua),
56         isFirefox = ua.indexOf("firefox") > -1,
57         isIE = ua.indexOf("msie") > -1,
58         isIE7 = ua.indexOf("msie 7") > -1,
59         isIE11 = /trident.*rv\:11\./.test(ua),
60         isEdge = ua.indexOf("edge") > -1,
61         isGecko = !isSafari && ua.indexOf("gecko") > -1,
62         isBorderBox = isIE && !isStrict,
63         isWindows = (ua.indexOf("windows") != -1 || ua.indexOf("win32") != -1),
64         isMac = (ua.indexOf("macintosh") != -1 || ua.indexOf("mac os x") != -1),
65         isLinux = (ua.indexOf("linux") != -1),
66         isSecure = window.location.href.toLowerCase().indexOf("https") === 0,
67         isIOS = /iphone|ipad/.test(ua),
68         isAndroid = /android/.test(ua),
69         isTouch =  (function() {
70             try {
71                 if (ua.indexOf('chrome') != -1 && ua.indexOf('android') == -1) {
72                     window.addEventListener('touchstart', function __set_has_touch__ () {
73                         Roo.isTouch = true;
74                         window.removeEventListener('touchstart', __set_has_touch__);
75                     });
76                     return false; // no touch on chrome!?
77                 }
78                 document.createEvent("TouchEvent");  
79                 return true;  
80             } catch (e) {  
81                 return false;  
82             } 
83             
84         })();
85     // remove css image flicker
86         if(isIE && !isIE7){
87         try{
88             document.execCommand("BackgroundImageCache", false, true);
89         }catch(e){}
90     }
91     
92     Roo.apply(Roo, {
93         /**
94          * True if the browser is in strict mode
95          * @type Boolean
96          */
97         isStrict : isStrict,
98         /**
99          * True if the page is running over SSL
100          * @type Boolean
101          */
102         isSecure : isSecure,
103         /**
104          * True when the document is fully initialized and ready for action
105          * @type Boolean
106          */
107         isReady : false,
108         /**
109          * Turn on debugging output (currently only the factory uses this)
110          * @type Boolean
111          */
112         
113         debug: false,
114
115         /**
116          * True to automatically uncache orphaned Roo.Elements periodically (defaults to true)
117          * @type Boolean
118          */
119         enableGarbageCollector : true,
120
121         /**
122          * True to automatically purge event listeners after uncaching an element (defaults to false).
123          * Note: this only happens if enableGarbageCollector is true.
124          * @type Boolean
125          */
126         enableListenerCollection:false,
127
128         /**
129          * URL to a blank file used by Roo when in secure mode for iframe src and onReady src to prevent
130          * the IE insecure content warning (defaults to javascript:false).
131          * @type String
132          */
133         SSL_SECURE_URL : "javascript:false",
134
135         /**
136          * URL to a 1x1 transparent gif image used by Roo to create inline icons with CSS background images. (Defaults to
137          * "http://Roojs.com/s.gif" and you should change this to a URL on your server).
138          * @type String
139          */
140         BLANK_IMAGE_URL : "http:/"+"/localhost/s.gif",
141
142         emptyFn : function(){},
143         
144         /**
145          * Copies all the properties of config to obj if they don't already exist.
146          * @param {Object} obj The receiver of the properties
147          * @param {Object} config The source of the properties
148          * @return {Object} returns obj
149          */
150         applyIf : function(o, c){
151             if(o && c){
152                 for(var p in c){
153                     if(typeof o[p] == "undefined"){ o[p] = c[p]; }
154                 }
155             }
156             return o;
157         },
158
159         /**
160          * Applies event listeners to elements by selectors when the document is ready.
161          * The event name is specified with an @ suffix.
162 <pre><code>
163 Roo.addBehaviors({
164    // add a listener for click on all anchors in element with id foo
165    '#foo a@click' : function(e, t){
166        // do something
167    },
168
169    // add the same listener to multiple selectors (separated by comma BEFORE the @)
170    '#foo a, #bar span.some-class@mouseover' : function(){
171        // do something
172    }
173 });
174 </code></pre>
175          * @param {Object} obj The list of behaviors to apply
176          */
177         addBehaviors : function(o){
178             if(!Roo.isReady){
179                 Roo.onReady(function(){
180                     Roo.addBehaviors(o);
181                 });
182                 return;
183             }
184             var cache = {}; // simple cache for applying multiple behaviors to same selector does query multiple times
185             for(var b in o){
186                 var parts = b.split('@');
187                 if(parts[1]){ // for Object prototype breakers
188                     var s = parts[0];
189                     if(!cache[s]){
190                         cache[s] = Roo.select(s);
191                     }
192                     cache[s].on(parts[1], o[b]);
193                 }
194             }
195             cache = null;
196         },
197
198         /**
199          * Generates unique ids. If the element already has an id, it is unchanged
200          * @param {String/HTMLElement/Element} el (optional) The element to generate an id for
201          * @param {String} prefix (optional) Id prefix (defaults "Roo-gen")
202          * @return {String} The generated Id.
203          */
204         id : function(el, prefix){
205             prefix = prefix || "roo-gen";
206             el = Roo.getDom(el);
207             var id = prefix + (++idSeed);
208             return el ? (el.id ? el.id : (el.id = id)) : id;
209         },
210          
211        
212         /**
213          * Extends one class with another class and optionally overrides members with the passed literal. This class
214          * also adds the function "override()" to the class that can be used to override
215          * members on an instance.
216          * @param {Object} subclass The class inheriting the functionality
217          * @param {Object} superclass The class being extended
218          * @param {Object} overrides (optional) A literal with members
219          * @method extend
220          */
221         extend : function(){
222             // inline overrides
223             var io = function(o){
224                 for(var m in o){
225                     this[m] = o[m];
226                 }
227             };
228             return function(sb, sp, overrides){
229                 if(typeof sp == 'object'){ // eg. prototype, rather than function constructor..
230                     overrides = sp;
231                     sp = sb;
232                     sb = function(){sp.apply(this, arguments);};
233                 }
234                 var F = function(){}, sbp, spp = sp.prototype;
235                 F.prototype = spp;
236                 sbp = sb.prototype = new F();
237                 sbp.constructor=sb;
238                 sb.superclass=spp;
239                 
240                 if(spp.constructor == Object.prototype.constructor){
241                     spp.constructor=sp;
242                    
243                 }
244                 
245                 sb.override = function(o){
246                     Roo.override(sb, o);
247                 };
248                 sbp.override = io;
249                 Roo.override(sb, overrides);
250                 return sb;
251             };
252         }(),
253
254         /**
255          * Adds a list of functions to the prototype of an existing class, overwriting any existing methods with the same name.
256          * Usage:<pre><code>
257 Roo.override(MyClass, {
258     newMethod1: function(){
259         // etc.
260     },
261     newMethod2: function(foo){
262         // etc.
263     }
264 });
265  </code></pre>
266          * @param {Object} origclass The class to override
267          * @param {Object} overrides The list of functions to add to origClass.  This should be specified as an object literal
268          * containing one or more methods.
269          * @method override
270          */
271         override : function(origclass, overrides){
272             if(overrides){
273                 var p = origclass.prototype;
274                 for(var method in overrides){
275                     p[method] = overrides[method];
276                 }
277             }
278         },
279         /**
280          * Creates namespaces to be used for scoping variables and classes so that they are not global.  Usage:
281          * <pre><code>
282 Roo.namespace('Company', 'Company.data');
283 Company.Widget = function() { ... }
284 Company.data.CustomStore = function(config) { ... }
285 </code></pre>
286          * @param {String} namespace1
287          * @param {String} namespace2
288          * @param {String} etc
289          * @method namespace
290          */
291         namespace : function(){
292             var a=arguments, o=null, i, j, d, rt;
293             for (i=0; i<a.length; ++i) {
294                 d=a[i].split(".");
295                 rt = d[0];
296                 /** eval:var:o */
297                 eval('if (typeof ' + rt + ' == "undefined"){' + rt + ' = {};} o = ' + rt + ';');
298                 for (j=1; j<d.length; ++j) {
299                     o[d[j]]=o[d[j]] || {};
300                     o=o[d[j]];
301                 }
302             }
303         },
304         /**
305          * Creates namespaces to be used for scoping variables and classes so that they are not global.  Usage:
306          * <pre><code>
307 Roo.factory({ xns: Roo.data, xtype : 'Store', .....});
308 Roo.factory(conf, Roo.data);
309 </code></pre>
310          * @param {String} classname
311          * @param {String} namespace (optional)
312          * @method factory
313          */
314          
315         factory : function(c, ns)
316         {
317             // no xtype, no ns or c.xns - or forced off by c.xns
318             if (!c.xtype   || (!ns && !c.xns) ||  (c.xns === false)) { // not enough info...
319                 return c;
320             }
321             ns = c.xns ? c.xns : ns; // if c.xns is set, then use that..
322             if (c.constructor == ns[c.xtype]) {// already created...
323                 return c;
324             }
325             if (ns[c.xtype]) {
326                 if (Roo.debug) { Roo.log("Roo.Factory(" + c.xtype + ")"); }
327                 var ret = new ns[c.xtype](c);
328                 ret.xns = false;
329                 return ret;
330             }
331             c.xns = false; // prevent recursion..
332             return c;
333         },
334          /**
335          * Logs to console if it can.
336          *
337          * @param {String|Object} string
338          * @method log
339          */
340         log : function(s)
341         {
342             if ((typeof(console) == 'undefined') || (typeof(console.log) == 'undefined')) {
343                 return; // alerT?
344             }
345             
346             console.log(s);
347         },
348         /**
349          * Takes an object and converts it to an encoded URL. e.g. Roo.urlEncode({foo: 1, bar: 2}); would return "foo=1&bar=2".  Optionally, property values can be arrays, instead of keys and the resulting string that's returned will contain a name/value pair for each array value.
350          * @param {Object} o
351          * @return {String}
352          */
353         urlEncode : function(o){
354             if(!o){
355                 return "";
356             }
357             var buf = [];
358             for(var key in o){
359                 var ov = o[key], k = Roo.encodeURIComponent(key);
360                 var type = typeof ov;
361                 if(type == 'undefined'){
362                     buf.push(k, "=&");
363                 }else if(type != "function" && type != "object"){
364                     buf.push(k, "=", Roo.encodeURIComponent(ov), "&");
365                 }else if(ov instanceof Array){
366                     if (ov.length) {
367                             for(var i = 0, len = ov.length; i < len; i++) {
368                                 buf.push(k, "=", Roo.encodeURIComponent(ov[i] === undefined ? '' : ov[i]), "&");
369                             }
370                         } else {
371                             buf.push(k, "=&");
372                         }
373                 }
374             }
375             buf.pop();
376             return buf.join("");
377         },
378          /**
379          * Safe version of encodeURIComponent
380          * @param {String} data 
381          * @return {String} 
382          */
383         
384         encodeURIComponent : function (data)
385         {
386             try {
387                 return encodeURIComponent(data);
388             } catch(e) {} // should be an uri encode error.
389             
390             if (data == '' || data == null){
391                return '';
392             }
393             // http://stackoverflow.com/questions/2596483/unicode-and-uri-encoding-decoding-and-escaping-in-javascript
394             function nibble_to_hex(nibble){
395                 var chars = '0123456789ABCDEF';
396                 return chars.charAt(nibble);
397             }
398             data = data.toString();
399             var buffer = '';
400             for(var i=0; i<data.length; i++){
401                 var c = data.charCodeAt(i);
402                 var bs = new Array();
403                 if (c > 0x10000){
404                         // 4 bytes
405                     bs[0] = 0xF0 | ((c & 0x1C0000) >>> 18);
406                     bs[1] = 0x80 | ((c & 0x3F000) >>> 12);
407                     bs[2] = 0x80 | ((c & 0xFC0) >>> 6);
408                     bs[3] = 0x80 | (c & 0x3F);
409                 }else if (c > 0x800){
410                          // 3 bytes
411                     bs[0] = 0xE0 | ((c & 0xF000) >>> 12);
412                     bs[1] = 0x80 | ((c & 0xFC0) >>> 6);
413                     bs[2] = 0x80 | (c & 0x3F);
414                 }else if (c > 0x80){
415                        // 2 bytes
416                     bs[0] = 0xC0 | ((c & 0x7C0) >>> 6);
417                     bs[1] = 0x80 | (c & 0x3F);
418                 }else{
419                         // 1 byte
420                     bs[0] = c;
421                 }
422                 for(var j=0; j<bs.length; j++){
423                     var b = bs[j];
424                     var hex = nibble_to_hex((b & 0xF0) >>> 4) 
425                             + nibble_to_hex(b &0x0F);
426                     buffer += '%'+hex;
427                }
428             }
429             return buffer;    
430              
431         },
432
433         /**
434          * Takes an encoded URL and and converts it to an object. e.g. Roo.urlDecode("foo=1&bar=2"); would return {foo: 1, bar: 2} or Roo.urlDecode("foo=1&bar=2&bar=3&bar=4", true); would return {foo: 1, bar: [2, 3, 4]}.
435          * @param {String} string
436          * @param {Boolean} overwrite (optional) Items of the same name will overwrite previous values instead of creating an an array (Defaults to false).
437          * @return {Object} A literal with members
438          */
439         urlDecode : function(string, overwrite){
440             if(!string || !string.length){
441                 return {};
442             }
443             var obj = {};
444             var pairs = string.split('&');
445             var pair, name, value;
446             for(var i = 0, len = pairs.length; i < len; i++){
447                 pair = pairs[i].split('=');
448                 name = decodeURIComponent(pair[0]);
449                 value = decodeURIComponent(pair[1]);
450                 if(overwrite !== true){
451                     if(typeof obj[name] == "undefined"){
452                         obj[name] = value;
453                     }else if(typeof obj[name] == "string"){
454                         obj[name] = [obj[name]];
455                         obj[name].push(value);
456                     }else{
457                         obj[name].push(value);
458                     }
459                 }else{
460                     obj[name] = value;
461                 }
462             }
463             return obj;
464         },
465
466         /**
467          * Iterates an array calling the passed function with each item, stopping if your function returns false. If the
468          * passed array is not really an array, your function is called once with it.
469          * The supplied function is called with (Object item, Number index, Array allItems).
470          * @param {Array/NodeList/Mixed} array
471          * @param {Function} fn
472          * @param {Object} scope
473          */
474         each : function(array, fn, scope){
475             if(typeof array.length == "undefined" || typeof array == "string"){
476                 array = [array];
477             }
478             for(var i = 0, len = array.length; i < len; i++){
479                 if(fn.call(scope || array[i], array[i], i, array) === false){ return i; };
480             }
481         },
482
483         // deprecated
484         combine : function(){
485             var as = arguments, l = as.length, r = [];
486             for(var i = 0; i < l; i++){
487                 var a = as[i];
488                 if(a instanceof Array){
489                     r = r.concat(a);
490                 }else if(a.length !== undefined && !a.substr){
491                     r = r.concat(Array.prototype.slice.call(a, 0));
492                 }else{
493                     r.push(a);
494                 }
495             }
496             return r;
497         },
498
499         /**
500          * Escapes the passed string for use in a regular expression
501          * @param {String} str
502          * @return {String}
503          */
504         escapeRe : function(s) {
505             return s.replace(/([.*+?^${}()|[\]\/\\])/g, "\\$1");
506         },
507
508         // internal
509         callback : function(cb, scope, args, delay){
510             if(typeof cb == "function"){
511                 if(delay){
512                     cb.defer(delay, scope, args || []);
513                 }else{
514                     cb.apply(scope, args || []);
515                 }
516             }
517         },
518
519         /**
520          * Return the dom node for the passed string (id), dom node, or Roo.Element
521          * @param {String/HTMLElement/Roo.Element} el
522          * @return HTMLElement
523          */
524         getDom : function(el){
525             if(!el){
526                 return null;
527             }
528             return el.dom ? el.dom : (typeof el == 'string' ? document.getElementById(el) : el);
529         },
530
531         /**
532         * Shorthand for {@link Roo.ComponentMgr#get}
533         * @param {String} id
534         * @return Roo.Component
535         */
536         getCmp : function(id){
537             return Roo.ComponentMgr.get(id);
538         },
539          
540         num : function(v, defaultValue){
541             if(typeof v != 'number'){
542                 return defaultValue;
543             }
544             return v;
545         },
546
547         destroy : function(){
548             for(var i = 0, a = arguments, len = a.length; i < len; i++) {
549                 var as = a[i];
550                 if(as){
551                     if(as.dom){
552                         as.removeAllListeners();
553                         as.remove();
554                         continue;
555                     }
556                     if(typeof as.purgeListeners == 'function'){
557                         as.purgeListeners();
558                     }
559                     if(typeof as.destroy == 'function'){
560                         as.destroy();
561                     }
562                 }
563             }
564         },
565
566         // inpired by a similar function in mootools library
567         /**
568          * Returns the type of object that is passed in. If the object passed in is null or undefined it
569          * return false otherwise it returns one of the following values:<ul>
570          * <li><b>string</b>: If the object passed is a string</li>
571          * <li><b>number</b>: If the object passed is a number</li>
572          * <li><b>boolean</b>: If the object passed is a boolean value</li>
573          * <li><b>function</b>: If the object passed is a function reference</li>
574          * <li><b>object</b>: If the object passed is an object</li>
575          * <li><b>array</b>: If the object passed is an array</li>
576          * <li><b>regexp</b>: If the object passed is a regular expression</li>
577          * <li><b>element</b>: If the object passed is a DOM Element</li>
578          * <li><b>nodelist</b>: If the object passed is a DOM NodeList</li>
579          * <li><b>textnode</b>: If the object passed is a DOM text node and contains something other than whitespace</li>
580          * <li><b>whitespace</b>: If the object passed is a DOM text node and contains only whitespace</li>
581          * @param {Mixed} object
582          * @return {String}
583          */
584         type : function(o){
585             if(o === undefined || o === null){
586                 return false;
587             }
588             if(o.htmlElement){
589                 return 'element';
590             }
591             var t = typeof o;
592             if(t == 'object' && o.nodeName) {
593                 switch(o.nodeType) {
594                     case 1: return 'element';
595                     case 3: return (/\S/).test(o.nodeValue) ? 'textnode' : 'whitespace';
596                 }
597             }
598             if(t == 'object' || t == 'function') {
599                 switch(o.constructor) {
600                     case Array: return 'array';
601                     case RegExp: return 'regexp';
602                 }
603                 if(typeof o.length == 'number' && typeof o.item == 'function') {
604                     return 'nodelist';
605                 }
606             }
607             return t;
608         },
609
610         /**
611          * Returns true if the passed value is null, undefined or an empty string (optional).
612          * @param {Mixed} value The value to test
613          * @param {Boolean} allowBlank (optional) Pass true if an empty string is not considered empty
614          * @return {Boolean}
615          */
616         isEmpty : function(v, allowBlank){
617             return v === null || v === undefined || (!allowBlank ? v === '' : false);
618         },
619         
620         /** @type Boolean */
621         isOpera : isOpera,
622         /** @type Boolean */
623         isSafari : isSafari,
624         /** @type Boolean */
625         isFirefox : isFirefox,
626         /** @type Boolean */
627         isIE : isIE,
628         /** @type Boolean */
629         isIE7 : isIE7,
630         /** @type Boolean */
631         isIE11 : isIE11,
632         /** @type Boolean */
633         isEdge : isEdge,
634         /** @type Boolean */
635         isGecko : isGecko,
636         /** @type Boolean */
637         isBorderBox : isBorderBox,
638         /** @type Boolean */
639         isWindows : isWindows,
640         /** @type Boolean */
641         isLinux : isLinux,
642         /** @type Boolean */
643         isMac : isMac,
644         /** @type Boolean */
645         isIOS : isIOS,
646         /** @type Boolean */
647         isAndroid : isAndroid,
648         /** @type Boolean */
649         isTouch : isTouch,
650
651         /**
652          * By default, Ext intelligently decides whether floating elements should be shimmed. If you are using flash,
653          * you may want to set this to true.
654          * @type Boolean
655          */
656         useShims : ((isIE && !isIE7) || (isGecko && isMac)),
657         
658         
659                 
660         /**
661          * Selects a single element as a Roo Element
662          * This is about as close as you can get to jQuery's $('do crazy stuff')
663          * @param {String} selector The selector/xpath query
664          * @param {Node} root (optional) The start of the query (defaults to document).
665          * @return {Roo.Element}
666          */
667         selectNode : function(selector, root) 
668         {
669             var node = Roo.DomQuery.selectNode(selector,root);
670             return node ? Roo.get(node) : new Roo.Element(false);
671         },
672                 /**
673                  * Find the current bootstrap width Grid size
674                  * Note xs is the default for smaller.. - this is currently used by grids to render correct columns
675                  * @returns {String} (xs|sm|md|lg|xl)
676                  */
677                 
678                 getGridSize : function()
679                 {
680                         var w = Roo.lib.Dom.getViewWidth();
681                         switch(true) {
682                                 case w > 1200:
683                                         return 'xl';
684                                 case w > 992:
685                                         return 'lg';
686                                 case w > 768:
687                                         return 'md';
688                                 case w > 576:
689                                         return 'sm';
690                                 default:
691                                         return 'xs'
692                         }
693                         
694                 }
695         
696     });
697
698
699 })();
700
701 Roo.namespace("Roo", "Roo.util", "Roo.grid", "Roo.dd", "Roo.tree", "Roo.data",
702                 "Roo.form", "Roo.menu", "Roo.state", "Roo.lib", "Roo.layout",
703                 "Roo.app", "Roo.ux",
704                 "Roo.bootstrap",
705                 "Roo.bootstrap.dash");
706 /*
707  * Based on:
708  * Ext JS Library 1.1.1
709  * Copyright(c) 2006-2007, Ext JS, LLC.
710  *
711  * Originally Released Under LGPL - original licence link has changed is not relivant.
712  *
713  * Fork - LGPL
714  * <script type="text/javascript">
715  */
716
717 (function() {    
718     // wrappedn so fnCleanup is not in global scope...
719     if(Roo.isIE) {
720         function fnCleanUp() {
721             var p = Function.prototype;
722             delete p.createSequence;
723             delete p.defer;
724             delete p.createDelegate;
725             delete p.createCallback;
726             delete p.createInterceptor;
727
728             window.detachEvent("onunload", fnCleanUp);
729         }
730         window.attachEvent("onunload", fnCleanUp);
731     }
732 })();
733
734
735 /**
736  * @class Function
737  * These functions are available on every Function object (any JavaScript function).
738  */
739 Roo.apply(Function.prototype, {
740      /**
741      * Creates a callback that passes arguments[0], arguments[1], arguments[2], ...
742      * Call directly on any function. Example: <code>myFunction.createCallback(myarg, myarg2)</code>
743      * Will create a function that is bound to those 2 args.
744      * @return {Function} The new function
745     */
746     createCallback : function(/*args...*/){
747         // make args available, in function below
748         var args = arguments;
749         var method = this;
750         return function() {
751             return method.apply(window, args);
752         };
753     },
754
755     /**
756      * Creates a delegate (callback) that sets the scope to obj.
757      * Call directly on any function. Example: <code>this.myFunction.createDelegate(this)</code>
758      * Will create a function that is automatically scoped to this.
759      * @param {Object} obj (optional) The object for which the scope is set
760      * @param {Array} args (optional) Overrides arguments for the call. (Defaults to the arguments passed by the caller)
761      * @param {Boolean/Number} appendArgs (optional) if True args are appended to call args instead of overriding,
762      *                                             if a number the args are inserted at the specified position
763      * @return {Function} The new function
764      */
765     createDelegate : function(obj, args, appendArgs){
766         var method = this;
767         return function() {
768             var callArgs = args || arguments;
769             if(appendArgs === true){
770                 callArgs = Array.prototype.slice.call(arguments, 0);
771                 callArgs = callArgs.concat(args);
772             }else if(typeof appendArgs == "number"){
773                 callArgs = Array.prototype.slice.call(arguments, 0); // copy arguments first
774                 var applyArgs = [appendArgs, 0].concat(args); // create method call params
775                 Array.prototype.splice.apply(callArgs, applyArgs); // splice them in
776             }
777             return method.apply(obj || window, callArgs);
778         };
779     },
780
781     /**
782      * Calls this function after the number of millseconds specified.
783      * @param {Number} millis The number of milliseconds for the setTimeout call (if 0 the function is executed immediately)
784      * @param {Object} obj (optional) The object for which the scope is set
785      * @param {Array} args (optional) Overrides arguments for the call. (Defaults to the arguments passed by the caller)
786      * @param {Boolean/Number} appendArgs (optional) if True args are appended to call args instead of overriding,
787      *                                             if a number the args are inserted at the specified position
788      * @return {Number} The timeout id that can be used with clearTimeout
789      */
790     defer : function(millis, obj, args, appendArgs){
791         var fn = this.createDelegate(obj, args, appendArgs);
792         if(millis){
793             return setTimeout(fn, millis);
794         }
795         fn();
796         return 0;
797     },
798     /**
799      * Create a combined function call sequence of the original function + the passed function.
800      * The resulting function returns the results of the original function.
801      * The passed fcn is called with the parameters of the original function
802      * @param {Function} fcn The function to sequence
803      * @param {Object} scope (optional) The scope of the passed fcn (Defaults to scope of original function or window)
804      * @return {Function} The new function
805      */
806     createSequence : function(fcn, scope){
807         if(typeof fcn != "function"){
808             return this;
809         }
810         var method = this;
811         return function() {
812             var retval = method.apply(this || window, arguments);
813             fcn.apply(scope || this || window, arguments);
814             return retval;
815         };
816     },
817
818     /**
819      * Creates an interceptor function. The passed fcn is called before the original one. If it returns false, the original one is not called.
820      * The resulting function returns the results of the original function.
821      * The passed fcn is called with the parameters of the original function.
822      * @addon
823      * @param {Function} fcn The function to call before the original
824      * @param {Object} scope (optional) The scope of the passed fcn (Defaults to scope of original function or window)
825      * @return {Function} The new function
826      */
827     createInterceptor : function(fcn, scope){
828         if(typeof fcn != "function"){
829             return this;
830         }
831         var method = this;
832         return function() {
833             fcn.target = this;
834             fcn.method = method;
835             if(fcn.apply(scope || this || window, arguments) === false){
836                 return;
837             }
838             return method.apply(this || window, arguments);
839         };
840     }
841 });
842 /*
843  * Based on:
844  * Ext JS Library 1.1.1
845  * Copyright(c) 2006-2007, Ext JS, LLC.
846  *
847  * Originally Released Under LGPL - original licence link has changed is not relivant.
848  *
849  * Fork - LGPL
850  * <script type="text/javascript">
851  */
852
853 Roo.applyIf(String, {
854     
855     /** @scope String */
856     
857     /**
858      * Escapes the passed string for ' and \
859      * @param {String} string The string to escape
860      * @return {String} The escaped string
861      * @static
862      */
863     escape : function(string) {
864         return string.replace(/('|\\)/g, "\\$1");
865     },
866
867     /**
868      * Pads the left side of a string with a specified character.  This is especially useful
869      * for normalizing number and date strings.  Example usage:
870      * <pre><code>
871 var s = String.leftPad('123', 5, '0');
872 // s now contains the string: '00123'
873 </code></pre>
874      * @param {String} string The original string
875      * @param {Number} size The total length of the output string
876      * @param {String} char (optional) The character with which to pad the original string (defaults to empty string " ")
877      * @return {String} The padded string
878      * @static
879      */
880     leftPad : function (val, size, ch) {
881         var result = new String(val);
882         if(ch === null || ch === undefined || ch === '') {
883             ch = " ";
884         }
885         while (result.length < size) {
886             result = ch + result;
887         }
888         return result;
889     },
890
891     /**
892      * Allows you to define a tokenized string and pass an arbitrary number of arguments to replace the tokens.  Each
893      * token must be unique, and must increment in the format {0}, {1}, etc.  Example usage:
894      * <pre><code>
895 var cls = 'my-class', text = 'Some text';
896 var s = String.format('<div class="{0}">{1}</div>', cls, text);
897 // s now contains the string: '<div class="my-class">Some text</div>'
898 </code></pre>
899      * @param {String} string The tokenized string to be formatted
900      * @param {String} value1 The value to replace token {0}
901      * @param {String} value2 Etc...
902      * @return {String} The formatted string
903      * @static
904      */
905     format : function(format){
906         var args = Array.prototype.slice.call(arguments, 1);
907         return format.replace(/\{(\d+)\}/g, function(m, i){
908             return Roo.util.Format.htmlEncode(args[i]);
909         });
910     }
911   
912     
913 });
914
915 /**
916  * Utility function that allows you to easily switch a string between two alternating values.  The passed value
917  * is compared to the current string, and if they are equal, the other value that was passed in is returned.  If
918  * they are already different, the first value passed in is returned.  Note that this method returns the new value
919  * but does not change the current string.
920  * <pre><code>
921 // alternate sort directions
922 sort = sort.toggle('ASC', 'DESC');
923
924 // instead of conditional logic:
925 sort = (sort == 'ASC' ? 'DESC' : 'ASC');
926 </code></pre>
927  * @param {String} value The value to compare to the current string
928  * @param {String} other The new value to use if the string already equals the first value passed in
929  * @return {String} The new value
930  */
931  
932 String.prototype.toggle = function(value, other){
933     return this == value ? other : value;
934 };
935
936
937 /**
938   * Remove invalid unicode characters from a string 
939   *
940   * @return {String} The clean string
941   */
942 String.prototype.unicodeClean = function () {
943     return this.replace(/[\s\S]/g,
944         function(character) {
945             if (character.charCodeAt()< 256) {
946               return character;
947            }
948            try {
949                 encodeURIComponent(character);
950            } catch(e) { 
951               return '';
952            }
953            return character;
954         }
955     );
956 };
957   
958 /*
959  * Based on:
960  * Ext JS Library 1.1.1
961  * Copyright(c) 2006-2007, Ext JS, LLC.
962  *
963  * Originally Released Under LGPL - original licence link has changed is not relivant.
964  *
965  * Fork - LGPL
966  * <script type="text/javascript">
967  */
968
969  /**
970  * @class Number
971  */
972 Roo.applyIf(Number.prototype, {
973     /**
974      * Checks whether or not the current number is within a desired range.  If the number is already within the
975      * range it is returned, otherwise the min or max value is returned depending on which side of the range is
976      * exceeded.  Note that this method returns the constrained value but does not change the current number.
977      * @param {Number} min The minimum number in the range
978      * @param {Number} max The maximum number in the range
979      * @return {Number} The constrained value if outside the range, otherwise the current value
980      */
981     constrain : function(min, max){
982         return Math.min(Math.max(this, min), max);
983     }
984 });/*
985  * Based on:
986  * Ext JS Library 1.1.1
987  * Copyright(c) 2006-2007, Ext JS, LLC.
988  *
989  * Originally Released Under LGPL - original licence link has changed is not relivant.
990  *
991  * Fork - LGPL
992  * <script type="text/javascript">
993  */
994  /**
995  * @class Array
996  */
997 Roo.applyIf(Array.prototype, {
998     /**
999      * 
1000      * Checks whether or not the specified object exists in the array.
1001      * @param {Object} o The object to check for
1002      * @return {Number} The index of o in the array (or -1 if it is not found)
1003      */
1004     indexOf : function(o){
1005        for (var i = 0, len = this.length; i < len; i++){
1006               if(this[i] == o) { return i; }
1007        }
1008            return -1;
1009     },
1010
1011     /**
1012      * Removes the specified object from the array.  If the object is not found nothing happens.
1013      * @param {Object} o The object to remove
1014      */
1015     remove : function(o){
1016        var index = this.indexOf(o);
1017        if(index != -1){
1018            this.splice(index, 1);
1019        }
1020     },
1021     /**
1022      * Map (JS 1.6 compatibility)
1023      * @param {Function} function  to call
1024      */
1025     map : function(fun )
1026     {
1027         var len = this.length >>> 0;
1028         if (typeof fun != "function") {
1029             throw new TypeError();
1030         }
1031         var res = new Array(len);
1032         var thisp = arguments[1];
1033         for (var i = 0; i < len; i++)
1034         {
1035             if (i in this) {
1036                 res[i] = fun.call(thisp, this[i], i, this);
1037             }
1038         }
1039
1040         return res;
1041     },
1042     /**
1043      * equals
1044      * @param {Array} o The array to compare to
1045      * @returns {Boolean} true if the same
1046      */
1047     equals : function(b)
1048     {
1049         // https://stackoverflow.com/questions/3115982/how-to-check-if-two-arrays-are-equal-with-javascript
1050         if (this === b) {
1051             return true;
1052          }
1053         if (b == null) {
1054             return false;
1055         }
1056         if (this.length !== b.length) {
1057             return false;
1058         }
1059       
1060         // sort?? a.sort().equals(b.sort());
1061       
1062         for (var i = 0; i < this.length; ++i) {
1063             if (this[i] !== b[i]) {
1064                 return false;
1065             }
1066         }
1067         return true;
1068     }
1069 });
1070
1071
1072  
1073 /*
1074  * Based on:
1075  * Ext JS Library 1.1.1
1076  * Copyright(c) 2006-2007, Ext JS, LLC.
1077  *
1078  * Originally Released Under LGPL - original licence link has changed is not relivant.
1079  *
1080  * Fork - LGPL
1081  * <script type="text/javascript">
1082  */
1083
1084 /**
1085  * @class Date
1086  *
1087  * The date parsing and format syntax is a subset of
1088  * <a href="http://www.php.net/date">PHP's date() function</a>, and the formats that are
1089  * supported will provide results equivalent to their PHP versions.
1090  *
1091  * Following is the list of all currently supported formats:
1092  *<pre>
1093 Sample date:
1094 'Wed Jan 10 2007 15:05:01 GMT-0600 (Central Standard Time)'
1095
1096 Format  Output      Description
1097 ------  ----------  --------------------------------------------------------------
1098   d      10         Day of the month, 2 digits with leading zeros
1099   D      Wed        A textual representation of a day, three letters
1100   j      10         Day of the month without leading zeros
1101   l      Wednesday  A full textual representation of the day of the week
1102   S      th         English ordinal day of month suffix, 2 chars (use with j)
1103   w      3          Numeric representation of the day of the week
1104   z      9          The julian date, or day of the year (0-365)
1105   W      01         ISO-8601 2-digit week number of year, weeks starting on Monday (00-52)
1106   F      January    A full textual representation of the month
1107   m      01         Numeric representation of a month, with leading zeros
1108   M      Jan        Month name abbreviation, three letters
1109   n      1          Numeric representation of a month, without leading zeros
1110   t      31         Number of days in the given month
1111   L      0          Whether it's a leap year (1 if it is a leap year, else 0)
1112   Y      2007       A full numeric representation of a year, 4 digits
1113   y      07         A two digit representation of a year
1114   a      pm         Lowercase Ante meridiem and Post meridiem
1115   A      PM         Uppercase Ante meridiem and Post meridiem
1116   g      3          12-hour format of an hour without leading zeros
1117   G      15         24-hour format of an hour without leading zeros
1118   h      03         12-hour format of an hour with leading zeros
1119   H      15         24-hour format of an hour with leading zeros
1120   i      05         Minutes with leading zeros
1121   s      01         Seconds, with leading zeros
1122   O      -0600      Difference to Greenwich time (GMT) in hours (Allows +08, without minutes)
1123   P      -06:00     Difference to Greenwich time (GMT) with colon between hours and minutes
1124   T      CST        Timezone setting of the machine running the code
1125   Z      -21600     Timezone offset in seconds (negative if west of UTC, positive if east)
1126 </pre>
1127  *
1128  * Example usage (note that you must escape format specifiers with '\\' to render them as character literals):
1129  * <pre><code>
1130 var dt = new Date('1/10/2007 03:05:01 PM GMT-0600');
1131 document.write(dt.format('Y-m-d'));                         //2007-01-10
1132 document.write(dt.format('F j, Y, g:i a'));                 //January 10, 2007, 3:05 pm
1133 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
1134  </code></pre>
1135  *
1136  * Here are some standard date/time patterns that you might find helpful.  They
1137  * are not part of the source of Date.js, but to use them you can simply copy this
1138  * block of code into any script that is included after Date.js and they will also become
1139  * globally available on the Date object.  Feel free to add or remove patterns as needed in your code.
1140  * <pre><code>
1141 Date.patterns = {
1142     ISO8601Long:"Y-m-d H:i:s",
1143     ISO8601Short:"Y-m-d",
1144     ShortDate: "n/j/Y",
1145     LongDate: "l, F d, Y",
1146     FullDateTime: "l, F d, Y g:i:s A",
1147     MonthDay: "F d",
1148     ShortTime: "g:i A",
1149     LongTime: "g:i:s A",
1150     SortableDateTime: "Y-m-d\\TH:i:s",
1151     UniversalSortableDateTime: "Y-m-d H:i:sO",
1152     YearMonth: "F, Y"
1153 };
1154 </code></pre>
1155  *
1156  * Example usage:
1157  * <pre><code>
1158 var dt = new Date();
1159 document.write(dt.format(Date.patterns.ShortDate));
1160  </code></pre>
1161  */
1162
1163 /*
1164  * Most of the date-formatting functions below are the excellent work of Baron Schwartz.
1165  * They generate precompiled functions from date formats instead of parsing and
1166  * processing the pattern every time you format a date.  These functions are available
1167  * on every Date object (any javascript function).
1168  *
1169  * The original article and download are here:
1170  * http://www.xaprb.com/blog/2005/12/12/javascript-closures-for-runtime-efficiency/
1171  *
1172  */
1173  
1174  
1175  // was in core
1176 /**
1177  Returns the number of milliseconds between this date and date
1178  @param {Date} date (optional) Defaults to now
1179  @return {Number} The diff in milliseconds
1180  @member Date getElapsed
1181  */
1182 Date.prototype.getElapsed = function(date) {
1183         return Math.abs((date || new Date()).getTime()-this.getTime());
1184 };
1185 // was in date file..
1186
1187
1188 // private
1189 Date.parseFunctions = {count:0};
1190 // private
1191 Date.parseRegexes = [];
1192 // private
1193 Date.formatFunctions = {count:0};
1194
1195 // private
1196 Date.prototype.dateFormat = function(format) {
1197     if (Date.formatFunctions[format] == null) {
1198         Date.createNewFormat(format);
1199     }
1200     var func = Date.formatFunctions[format];
1201     return this[func]();
1202 };
1203
1204
1205 /**
1206  * Formats a date given the supplied format string
1207  * @param {String} format The format string
1208  * @return {String} The formatted date
1209  * @method
1210  */
1211 Date.prototype.format = Date.prototype.dateFormat;
1212
1213 // private
1214 Date.createNewFormat = function(format) {
1215     var funcName = "format" + Date.formatFunctions.count++;
1216     Date.formatFunctions[format] = funcName;
1217     var code = "Date.prototype." + funcName + " = function(){return ";
1218     var special = false;
1219     var ch = '';
1220     for (var i = 0; i < format.length; ++i) {
1221         ch = format.charAt(i);
1222         if (!special && ch == "\\") {
1223             special = true;
1224         }
1225         else if (special) {
1226             special = false;
1227             code += "'" + String.escape(ch) + "' + ";
1228         }
1229         else {
1230             code += Date.getFormatCode(ch);
1231         }
1232     }
1233     /** eval:var:zzzzzzzzzzzzz */
1234     eval(code.substring(0, code.length - 3) + ";}");
1235 };
1236
1237 // private
1238 Date.getFormatCode = function(character) {
1239     switch (character) {
1240     case "d":
1241         return "String.leftPad(this.getDate(), 2, '0') + ";
1242     case "D":
1243         return "Date.dayNames[this.getDay()].substring(0, 3) + ";
1244     case "j":
1245         return "this.getDate() + ";
1246     case "l":
1247         return "Date.dayNames[this.getDay()] + ";
1248     case "S":
1249         return "this.getSuffix() + ";
1250     case "w":
1251         return "this.getDay() + ";
1252     case "z":
1253         return "this.getDayOfYear() + ";
1254     case "W":
1255         return "this.getWeekOfYear() + ";
1256     case "F":
1257         return "Date.monthNames[this.getMonth()] + ";
1258     case "m":
1259         return "String.leftPad(this.getMonth() + 1, 2, '0') + ";
1260     case "M":
1261         return "Date.monthNames[this.getMonth()].substring(0, 3) + ";
1262     case "n":
1263         return "(this.getMonth() + 1) + ";
1264     case "t":
1265         return "this.getDaysInMonth() + ";
1266     case "L":
1267         return "(this.isLeapYear() ? 1 : 0) + ";
1268     case "Y":
1269         return "this.getFullYear() + ";
1270     case "y":
1271         return "('' + this.getFullYear()).substring(2, 4) + ";
1272     case "a":
1273         return "(this.getHours() < 12 ? 'am' : 'pm') + ";
1274     case "A":
1275         return "(this.getHours() < 12 ? 'AM' : 'PM') + ";
1276     case "g":
1277         return "((this.getHours() % 12) ? this.getHours() % 12 : 12) + ";
1278     case "G":
1279         return "this.getHours() + ";
1280     case "h":
1281         return "String.leftPad((this.getHours() % 12) ? this.getHours() % 12 : 12, 2, '0') + ";
1282     case "H":
1283         return "String.leftPad(this.getHours(), 2, '0') + ";
1284     case "i":
1285         return "String.leftPad(this.getMinutes(), 2, '0') + ";
1286     case "s":
1287         return "String.leftPad(this.getSeconds(), 2, '0') + ";
1288     case "O":
1289         return "this.getGMTOffset() + ";
1290     case "P":
1291         return "this.getGMTColonOffset() + ";
1292     case "T":
1293         return "this.getTimezone() + ";
1294     case "Z":
1295         return "(this.getTimezoneOffset() * -60) + ";
1296     default:
1297         return "'" + String.escape(character) + "' + ";
1298     }
1299 };
1300
1301 /**
1302  * Parses the passed string using the specified format. Note that this function expects dates in normal calendar
1303  * format, meaning that months are 1-based (1 = January) and not zero-based like in JavaScript dates.  Any part of
1304  * the date format that is not specified will default to the current date value for that part.  Time parts can also
1305  * be specified, but default to 0.  Keep in mind that the input date string must precisely match the specified format
1306  * string or the parse operation will fail.
1307  * Example Usage:
1308 <pre><code>
1309 //dt = Fri May 25 2007 (current date)
1310 var dt = new Date();
1311
1312 //dt = Thu May 25 2006 (today's month/day in 2006)
1313 dt = Date.parseDate("2006", "Y");
1314
1315 //dt = Sun Jan 15 2006 (all date parts specified)
1316 dt = Date.parseDate("2006-1-15", "Y-m-d");
1317
1318 //dt = Sun Jan 15 2006 15:20:01 GMT-0600 (CST)
1319 dt = Date.parseDate("2006-1-15 3:20:01 PM", "Y-m-d h:i:s A" );
1320 </code></pre>
1321  * @param {String} input The unparsed date as a string
1322  * @param {String} format The format the date is in
1323  * @return {Date} The parsed date
1324  * @static
1325  */
1326 Date.parseDate = function(input, format) {
1327     if (Date.parseFunctions[format] == null) {
1328         Date.createParser(format);
1329     }
1330     var func = Date.parseFunctions[format];
1331     return Date[func](input);
1332 };
1333 /**
1334  * @private
1335  */
1336
1337 Date.createParser = function(format) {
1338     var funcName = "parse" + Date.parseFunctions.count++;
1339     var regexNum = Date.parseRegexes.length;
1340     var currentGroup = 1;
1341     Date.parseFunctions[format] = funcName;
1342
1343     var code = "Date." + funcName + " = function(input){\n"
1344         + "var y = -1, m = -1, d = -1, h = -1, i = -1, s = -1, o, z, v;\n"
1345         + "var d = new Date();\n"
1346         + "y = d.getFullYear();\n"
1347         + "m = d.getMonth();\n"
1348         + "d = d.getDate();\n"
1349         + "if (typeof(input) !== 'string') { input = input.toString(); }\n"
1350         + "var results = input.match(Date.parseRegexes[" + regexNum + "]);\n"
1351         + "if (results && results.length > 0) {";
1352     var regex = "";
1353
1354     var special = false;
1355     var ch = '';
1356     for (var i = 0; i < format.length; ++i) {
1357         ch = format.charAt(i);
1358         if (!special && ch == "\\") {
1359             special = true;
1360         }
1361         else if (special) {
1362             special = false;
1363             regex += String.escape(ch);
1364         }
1365         else {
1366             var obj = Date.formatCodeToRegex(ch, currentGroup);
1367             currentGroup += obj.g;
1368             regex += obj.s;
1369             if (obj.g && obj.c) {
1370                 code += obj.c;
1371             }
1372         }
1373     }
1374
1375     code += "if (y >= 0 && m >= 0 && d > 0 && h >= 0 && i >= 0 && s >= 0)\n"
1376         + "{v = new Date(y, m, d, h, i, s);}\n"
1377         + "else if (y >= 0 && m >= 0 && d > 0 && h >= 0 && i >= 0)\n"
1378         + "{v = new Date(y, m, d, h, i);}\n"
1379         + "else if (y >= 0 && m >= 0 && d > 0 && h >= 0)\n"
1380         + "{v = new Date(y, m, d, h);}\n"
1381         + "else if (y >= 0 && m >= 0 && d > 0)\n"
1382         + "{v = new Date(y, m, d);}\n"
1383         + "else if (y >= 0 && m >= 0)\n"
1384         + "{v = new Date(y, m);}\n"
1385         + "else if (y >= 0)\n"
1386         + "{v = new Date(y);}\n"
1387         + "}return (v && (z || o))?\n" // favour UTC offset over GMT offset
1388         + "    ((z)? v.add(Date.SECOND, (v.getTimezoneOffset() * 60) + (z*1)) :\n" // reset to UTC, then add offset
1389         + "        v.add(Date.HOUR, (v.getGMTOffset() / 100) + (o / -100))) : v\n" // reset to GMT, then add offset
1390         + ";}";
1391
1392     Date.parseRegexes[regexNum] = new RegExp("^" + regex + "$");
1393     /** eval:var:zzzzzzzzzzzzz */
1394     eval(code);
1395 };
1396
1397 // private
1398 Date.formatCodeToRegex = function(character, currentGroup) {
1399     switch (character) {
1400     case "D":
1401         return {g:0,
1402         c:null,
1403         s:"(?:Sun|Mon|Tue|Wed|Thu|Fri|Sat)"};
1404     case "j":
1405         return {g:1,
1406             c:"d = parseInt(results[" + currentGroup + "], 10);\n",
1407             s:"(\\d{1,2})"}; // day of month without leading zeroes
1408     case "d":
1409         return {g:1,
1410             c:"d = parseInt(results[" + currentGroup + "], 10);\n",
1411             s:"(\\d{2})"}; // day of month with leading zeroes
1412     case "l":
1413         return {g:0,
1414             c:null,
1415             s:"(?:" + Date.dayNames.join("|") + ")"};
1416     case "S":
1417         return {g:0,
1418             c:null,
1419             s:"(?:st|nd|rd|th)"};
1420     case "w":
1421         return {g:0,
1422             c:null,
1423             s:"\\d"};
1424     case "z":
1425         return {g:0,
1426             c:null,
1427             s:"(?:\\d{1,3})"};
1428     case "W":
1429         return {g:0,
1430             c:null,
1431             s:"(?:\\d{2})"};
1432     case "F":
1433         return {g:1,
1434             c:"m = parseInt(Date.monthNumbers[results[" + currentGroup + "].substring(0, 3)], 10);\n",
1435             s:"(" + Date.monthNames.join("|") + ")"};
1436     case "M":
1437         return {g:1,
1438             c:"m = parseInt(Date.monthNumbers[results[" + currentGroup + "]], 10);\n",
1439             s:"(Jan|Feb|Mar|Apr|May|Jun|Jul|Aug|Sep|Oct|Nov|Dec)"};
1440     case "n":
1441         return {g:1,
1442             c:"m = parseInt(results[" + currentGroup + "], 10) - 1;\n",
1443             s:"(\\d{1,2})"}; // Numeric representation of a month, without leading zeros
1444     case "m":
1445         return {g:1,
1446             c:"m = parseInt(results[" + currentGroup + "], 10) - 1;\n",
1447             s:"(\\d{2})"}; // Numeric representation of a month, with leading zeros
1448     case "t":
1449         return {g:0,
1450             c:null,
1451             s:"\\d{1,2}"};
1452     case "L":
1453         return {g:0,
1454             c:null,
1455             s:"(?:1|0)"};
1456     case "Y":
1457         return {g:1,
1458             c:"y = parseInt(results[" + currentGroup + "], 10);\n",
1459             s:"(\\d{4})"};
1460     case "y":
1461         return {g:1,
1462             c:"var ty = parseInt(results[" + currentGroup + "], 10);\n"
1463                 + "y = ty > Date.y2kYear ? 1900 + ty : 2000 + ty;\n",
1464             s:"(\\d{1,2})"};
1465     case "a":
1466         return {g:1,
1467             c:"if (results[" + currentGroup + "] == 'am') {\n"
1468                 + "if (h == 12) { h = 0; }\n"
1469                 + "} else { if (h < 12) { h += 12; }}",
1470             s:"(am|pm)"};
1471     case "A":
1472         return {g:1,
1473             c:"if (results[" + currentGroup + "] == 'AM') {\n"
1474                 + "if (h == 12) { h = 0; }\n"
1475                 + "} else { if (h < 12) { h += 12; }}",
1476             s:"(AM|PM)"};
1477     case "g":
1478     case "G":
1479         return {g:1,
1480             c:"h = parseInt(results[" + currentGroup + "], 10);\n",
1481             s:"(\\d{1,2})"}; // 12/24-hr format  format of an hour without leading zeroes
1482     case "h":
1483     case "H":
1484         return {g:1,
1485             c:"h = parseInt(results[" + currentGroup + "], 10);\n",
1486             s:"(\\d{2})"}; //  12/24-hr format  format of an hour with leading zeroes
1487     case "i":
1488         return {g:1,
1489             c:"i = parseInt(results[" + currentGroup + "], 10);\n",
1490             s:"(\\d{2})"};
1491     case "s":
1492         return {g:1,
1493             c:"s = parseInt(results[" + currentGroup + "], 10);\n",
1494             s:"(\\d{2})"};
1495     case "O":
1496         return {g:1,
1497             c:[
1498                 "o = results[", currentGroup, "];\n",
1499                 "var sn = o.substring(0,1);\n", // get + / - sign
1500                 "var hr = o.substring(1,3)*1 + Math.floor(o.substring(3,5) / 60);\n", // get hours (performs minutes-to-hour conversion also)
1501                 "var mn = o.substring(3,5) % 60;\n", // get minutes
1502                 "o = ((-12 <= (hr*60 + mn)/60) && ((hr*60 + mn)/60 <= 14))?\n", // -12hrs <= GMT offset <= 14hrs
1503                 "    (sn + String.leftPad(hr, 2, 0) + String.leftPad(mn, 2, 0)) : null;\n"
1504             ].join(""),
1505             s:"([+\-]\\d{2,4})"};
1506     
1507     
1508     case "P":
1509         return {g:1,
1510                 c:[
1511                    "o = results[", currentGroup, "];\n",
1512                    "var sn = o.substring(0,1);\n",
1513                    "var hr = o.substring(1,3)*1 + Math.floor(o.substring(4,6) / 60);\n",
1514                    "var mn = o.substring(4,6) % 60;\n",
1515                    "o = ((-12 <= (hr*60 + mn)/60) && ((hr*60 + mn)/60 <= 14))?\n",
1516                         "    (sn + String.leftPad(hr, 2, 0) + String.leftPad(mn, 2, 0)) : null;\n"
1517             ].join(""),
1518             s:"([+\-]\\d{4})"};
1519     case "T":
1520         return {g:0,
1521             c:null,
1522             s:"[A-Z]{1,4}"}; // timezone abbrev. may be between 1 - 4 chars
1523     case "Z":
1524         return {g:1,
1525             c:"z = results[" + currentGroup + "];\n" // -43200 <= UTC offset <= 50400
1526                   + "z = (-43200 <= z*1 && z*1 <= 50400)? z : null;\n",
1527             s:"([+\-]?\\d{1,5})"}; // leading '+' sign is optional for UTC offset
1528     default:
1529         return {g:0,
1530             c:null,
1531             s:String.escape(character)};
1532     }
1533 };
1534
1535 /**
1536  * Get the timezone abbreviation of the current date (equivalent to the format specifier 'T').
1537  * @return {String} The abbreviated timezone name (e.g. 'CST')
1538  */
1539 Date.prototype.getTimezone = function() {
1540     return this.toString().replace(/^.*? ([A-Z]{1,4})[\-+][0-9]{4} .*$/, "$1");
1541 };
1542
1543 /**
1544  * Get the offset from GMT of the current date (equivalent to the format specifier 'O').
1545  * @return {String} The 4-character offset string prefixed with + or - (e.g. '-0600')
1546  */
1547 Date.prototype.getGMTOffset = function() {
1548     return (this.getTimezoneOffset() > 0 ? "-" : "+")
1549         + String.leftPad(Math.abs(Math.floor(this.getTimezoneOffset() / 60)), 2, "0")
1550         + String.leftPad(this.getTimezoneOffset() % 60, 2, "0");
1551 };
1552
1553 /**
1554  * Get the offset from GMT of the current date (equivalent to the format specifier 'P').
1555  * @return {String} 2-characters representing hours and 2-characters representing minutes
1556  * seperated by a colon and prefixed with + or - (e.g. '-06:00')
1557  */
1558 Date.prototype.getGMTColonOffset = function() {
1559         return (this.getTimezoneOffset() > 0 ? "-" : "+")
1560                 + String.leftPad(Math.abs(Math.floor(this.getTimezoneOffset() / 60)), 2, "0")
1561                 + ":"
1562                 + String.leftPad(this.getTimezoneOffset() %60, 2, "0");
1563 }
1564
1565 /**
1566  * Get the numeric day number of the year, adjusted for leap year.
1567  * @return {Number} 0 through 364 (365 in leap years)
1568  */
1569 Date.prototype.getDayOfYear = function() {
1570     var num = 0;
1571     Date.daysInMonth[1] = this.isLeapYear() ? 29 : 28;
1572     for (var i = 0; i < this.getMonth(); ++i) {
1573         num += Date.daysInMonth[i];
1574     }
1575     return num + this.getDate() - 1;
1576 };
1577
1578 /**
1579  * Get the string representation of the numeric week number of the year
1580  * (equivalent to the format specifier 'W').
1581  * @return {String} '00' through '52'
1582  */
1583 Date.prototype.getWeekOfYear = function() {
1584     // Skip to Thursday of this week
1585     var now = this.getDayOfYear() + (4 - this.getDay());
1586     // Find the first Thursday of the year
1587     var jan1 = new Date(this.getFullYear(), 0, 1);
1588     var then = (7 - jan1.getDay() + 4);
1589     return String.leftPad(((now - then) / 7) + 1, 2, "0");
1590 };
1591
1592 /**
1593  * Whether or not the current date is in a leap year.
1594  * @return {Boolean} True if the current date is in a leap year, else false
1595  */
1596 Date.prototype.isLeapYear = function() {
1597     var year = this.getFullYear();
1598     return ((year & 3) == 0 && (year % 100 || (year % 400 == 0 && year)));
1599 };
1600
1601 /**
1602  * Get the first day of the current month, adjusted for leap year.  The returned value
1603  * is the numeric day index within the week (0-6) which can be used in conjunction with
1604  * the {@link #monthNames} array to retrieve the textual day name.
1605  * Example:
1606  *<pre><code>
1607 var dt = new Date('1/10/2007');
1608 document.write(Date.dayNames[dt.getFirstDayOfMonth()]); //output: 'Monday'
1609 </code></pre>
1610  * @return {Number} The day number (0-6)
1611  */
1612 Date.prototype.getFirstDayOfMonth = function() {
1613     var day = (this.getDay() - (this.getDate() - 1)) % 7;
1614     return (day < 0) ? (day + 7) : day;
1615 };
1616
1617 /**
1618  * Get the last day of the current month, adjusted for leap year.  The returned value
1619  * is the numeric day index within the week (0-6) which can be used in conjunction with
1620  * the {@link #monthNames} array to retrieve the textual day name.
1621  * Example:
1622  *<pre><code>
1623 var dt = new Date('1/10/2007');
1624 document.write(Date.dayNames[dt.getLastDayOfMonth()]); //output: 'Wednesday'
1625 </code></pre>
1626  * @return {Number} The day number (0-6)
1627  */
1628 Date.prototype.getLastDayOfMonth = function() {
1629     var day = (this.getDay() + (Date.daysInMonth[this.getMonth()] - this.getDate())) % 7;
1630     return (day < 0) ? (day + 7) : day;
1631 };
1632
1633
1634 /**
1635  * Get the first date of this date's month
1636  * @return {Date}
1637  */
1638 Date.prototype.getFirstDateOfMonth = function() {
1639     return new Date(this.getFullYear(), this.getMonth(), 1);
1640 };
1641
1642 /**
1643  * Get the last date of this date's month
1644  * @return {Date}
1645  */
1646 Date.prototype.getLastDateOfMonth = function() {
1647     return new Date(this.getFullYear(), this.getMonth(), this.getDaysInMonth());
1648 };
1649 /**
1650  * Get the number of days in the current month, adjusted for leap year.
1651  * @return {Number} The number of days in the month
1652  */
1653 Date.prototype.getDaysInMonth = function() {
1654     Date.daysInMonth[1] = this.isLeapYear() ? 29 : 28;
1655     return Date.daysInMonth[this.getMonth()];
1656 };
1657
1658 /**
1659  * Get the English ordinal suffix of the current day (equivalent to the format specifier 'S').
1660  * @return {String} 'st, 'nd', 'rd' or 'th'
1661  */
1662 Date.prototype.getSuffix = function() {
1663     switch (this.getDate()) {
1664         case 1:
1665         case 21:
1666         case 31:
1667             return "st";
1668         case 2:
1669         case 22:
1670             return "nd";
1671         case 3:
1672         case 23:
1673             return "rd";
1674         default:
1675             return "th";
1676     }
1677 };
1678
1679 // private
1680 Date.daysInMonth = [31,28,31,30,31,30,31,31,30,31,30,31];
1681
1682 /**
1683  * An array of textual month names.
1684  * Override these values for international dates, for example...
1685  * Date.monthNames = ['JanInYourLang', 'FebInYourLang', ...];
1686  * @type Array
1687  * @static
1688  */
1689 Date.monthNames =
1690    ["January",
1691     "February",
1692     "March",
1693     "April",
1694     "May",
1695     "June",
1696     "July",
1697     "August",
1698     "September",
1699     "October",
1700     "November",
1701     "December"];
1702
1703 /**
1704  * An array of textual day names.
1705  * Override these values for international dates, for example...
1706  * Date.dayNames = ['SundayInYourLang', 'MondayInYourLang', ...];
1707  * @type Array
1708  * @static
1709  */
1710 Date.dayNames =
1711    ["Sunday",
1712     "Monday",
1713     "Tuesday",
1714     "Wednesday",
1715     "Thursday",
1716     "Friday",
1717     "Saturday"];
1718
1719 // private
1720 Date.y2kYear = 50;
1721 // private
1722 Date.monthNumbers = {
1723     Jan:0,
1724     Feb:1,
1725     Mar:2,
1726     Apr:3,
1727     May:4,
1728     Jun:5,
1729     Jul:6,
1730     Aug:7,
1731     Sep:8,
1732     Oct:9,
1733     Nov:10,
1734     Dec:11};
1735
1736 /**
1737  * Creates and returns a new Date instance with the exact same date value as the called instance.
1738  * Dates are copied and passed by reference, so if a copied date variable is modified later, the original
1739  * variable will also be changed.  When the intention is to create a new variable that will not
1740  * modify the original instance, you should create a clone.
1741  *
1742  * Example of correctly cloning a date:
1743  * <pre><code>
1744 //wrong way:
1745 var orig = new Date('10/1/2006');
1746 var copy = orig;
1747 copy.setDate(5);
1748 document.write(orig);  //returns 'Thu Oct 05 2006'!
1749
1750 //correct way:
1751 var orig = new Date('10/1/2006');
1752 var copy = orig.clone();
1753 copy.setDate(5);
1754 document.write(orig);  //returns 'Thu Oct 01 2006'
1755 </code></pre>
1756  * @return {Date} The new Date instance
1757  */
1758 Date.prototype.clone = function() {
1759         return new Date(this.getTime());
1760 };
1761
1762 /**
1763  * Clears any time information from this date
1764  @param {Boolean} clone true to create a clone of this date, clear the time and return it
1765  @return {Date} this or the clone
1766  */
1767 Date.prototype.clearTime = function(clone){
1768     if(clone){
1769         return this.clone().clearTime();
1770     }
1771     this.setHours(0);
1772     this.setMinutes(0);
1773     this.setSeconds(0);
1774     this.setMilliseconds(0);
1775     return this;
1776 };
1777
1778 // private
1779 // safari setMonth is broken -- check that this is only donw once...
1780 if(Roo.isSafari && typeof(Date.brokenSetMonth) == 'undefined'){
1781     Date.brokenSetMonth = Date.prototype.setMonth;
1782         Date.prototype.setMonth = function(num){
1783                 if(num <= -1){
1784                         var n = Math.ceil(-num);
1785                         var back_year = Math.ceil(n/12);
1786                         var month = (n % 12) ? 12 - n % 12 : 0 ;
1787                         this.setFullYear(this.getFullYear() - back_year);
1788                         return Date.brokenSetMonth.call(this, month);
1789                 } else {
1790                         return Date.brokenSetMonth.apply(this, arguments);
1791                 }
1792         };
1793 }
1794
1795 /** Date interval constant 
1796 * @static 
1797 * @type String */
1798 Date.MILLI = "ms";
1799 /** Date interval constant 
1800 * @static 
1801 * @type String */
1802 Date.SECOND = "s";
1803 /** Date interval constant 
1804 * @static 
1805 * @type String */
1806 Date.MINUTE = "mi";
1807 /** Date interval constant 
1808 * @static 
1809 * @type String */
1810 Date.HOUR = "h";
1811 /** Date interval constant 
1812 * @static 
1813 * @type String */
1814 Date.DAY = "d";
1815 /** Date interval constant 
1816 * @static 
1817 * @type String */
1818 Date.MONTH = "mo";
1819 /** Date interval constant 
1820 * @static 
1821 * @type String */
1822 Date.YEAR = "y";
1823
1824 /**
1825  * Provides a convenient method of performing basic date arithmetic.  This method
1826  * does not modify the Date instance being called - it creates and returns
1827  * a new Date instance containing the resulting date value.
1828  *
1829  * Examples:
1830  * <pre><code>
1831 //Basic usage:
1832 var dt = new Date('10/29/2006').add(Date.DAY, 5);
1833 document.write(dt); //returns 'Fri Oct 06 2006 00:00:00'
1834
1835 //Negative values will subtract correctly:
1836 var dt2 = new Date('10/1/2006').add(Date.DAY, -5);
1837 document.write(dt2); //returns 'Tue Sep 26 2006 00:00:00'
1838
1839 //You can even chain several calls together in one line!
1840 var dt3 = new Date('10/1/2006').add(Date.DAY, 5).add(Date.HOUR, 8).add(Date.MINUTE, -30);
1841 document.write(dt3); //returns 'Fri Oct 06 2006 07:30:00'
1842  </code></pre>
1843  *
1844  * @param {String} interval   A valid date interval enum value
1845  * @param {Number} value      The amount to add to the current date
1846  * @return {Date} The new Date instance
1847  */
1848 Date.prototype.add = function(interval, value){
1849   var d = this.clone();
1850   if (!interval || value === 0) { return d; }
1851   switch(interval.toLowerCase()){
1852     case Date.MILLI:
1853       d.setMilliseconds(this.getMilliseconds() + value);
1854       break;
1855     case Date.SECOND:
1856       d.setSeconds(this.getSeconds() + value);
1857       break;
1858     case Date.MINUTE:
1859       d.setMinutes(this.getMinutes() + value);
1860       break;
1861     case Date.HOUR:
1862       d.setHours(this.getHours() + value);
1863       break;
1864     case Date.DAY:
1865       d.setDate(this.getDate() + value);
1866       break;
1867     case Date.MONTH:
1868       var day = this.getDate();
1869       if(day > 28){
1870           day = Math.min(day, this.getFirstDateOfMonth().add('mo', value).getLastDateOfMonth().getDate());
1871       }
1872       d.setDate(day);
1873       d.setMonth(this.getMonth() + value);
1874       break;
1875     case Date.YEAR:
1876       d.setFullYear(this.getFullYear() + value);
1877       break;
1878   }
1879   return d;
1880 };
1881 /**
1882  * @class Roo.lib.Dom
1883  * @licence LGPL
1884  * @static
1885  * 
1886  * Dom utils (from YIU afaik)
1887  *
1888  * 
1889  **/
1890 Roo.lib.Dom = {
1891     /**
1892      * Get the view width
1893      * @param {Boolean} full True will get the full document, otherwise it's the view width
1894      * @return {Number} The width
1895      */
1896      
1897     getViewWidth : function(full) {
1898         return full ? this.getDocumentWidth() : this.getViewportWidth();
1899     },
1900     /**
1901      * Get the view height
1902      * @param {Boolean} full True will get the full document, otherwise it's the view height
1903      * @return {Number} The height
1904      */
1905     getViewHeight : function(full) {
1906         return full ? this.getDocumentHeight() : this.getViewportHeight();
1907     },
1908     /**
1909      * Get the Full Document height 
1910      * @return {Number} The height
1911      */
1912     getDocumentHeight: function() {
1913         var scrollHeight = (document.compatMode != "CSS1Compat") ? document.body.scrollHeight : document.documentElement.scrollHeight;
1914         return Math.max(scrollHeight, this.getViewportHeight());
1915     },
1916     /**
1917      * Get the Full Document width
1918      * @return {Number} The width
1919      */
1920     getDocumentWidth: function() {
1921         var scrollWidth = (document.compatMode != "CSS1Compat") ? document.body.scrollWidth : document.documentElement.scrollWidth;
1922         return Math.max(scrollWidth, this.getViewportWidth());
1923     },
1924     /**
1925      * Get the Window Viewport height
1926      * @return {Number} The height
1927      */
1928     getViewportHeight: function() {
1929         var height = self.innerHeight;
1930         var mode = document.compatMode;
1931
1932         if ((mode || Roo.isIE) && !Roo.isOpera) {
1933             height = (mode == "CSS1Compat") ?
1934                      document.documentElement.clientHeight :
1935                      document.body.clientHeight;
1936         }
1937
1938         return height;
1939     },
1940     /**
1941      * Get the Window Viewport width
1942      * @return {Number} The width
1943      */
1944     getViewportWidth: function() {
1945         var width = self.innerWidth;
1946         var mode = document.compatMode;
1947
1948         if (mode || Roo.isIE) {
1949             width = (mode == "CSS1Compat") ?
1950                     document.documentElement.clientWidth :
1951                     document.body.clientWidth;
1952         }
1953         return width;
1954     },
1955
1956     isAncestor : function(p, c) {
1957         p = Roo.getDom(p);
1958         c = Roo.getDom(c);
1959         if (!p || !c) {
1960             return false;
1961         }
1962
1963         if (p.contains && !Roo.isSafari) {
1964             return p.contains(c);
1965         } else if (p.compareDocumentPosition) {
1966             return !!(p.compareDocumentPosition(c) & 16);
1967         } else {
1968             var parent = c.parentNode;
1969             while (parent) {
1970                 if (parent == p) {
1971                     return true;
1972                 }
1973                 else if (!parent.tagName || parent.tagName.toUpperCase() == "HTML") {
1974                     return false;
1975                 }
1976                 parent = parent.parentNode;
1977             }
1978             return false;
1979         }
1980     },
1981
1982     getRegion : function(el) {
1983         return Roo.lib.Region.getRegion(el);
1984     },
1985
1986     getY : function(el) {
1987         return this.getXY(el)[1];
1988     },
1989
1990     getX : function(el) {
1991         return this.getXY(el)[0];
1992     },
1993
1994     getXY : function(el) {
1995         var p, pe, b, scroll, bd = document.body;
1996         el = Roo.getDom(el);
1997         var fly = Roo.lib.AnimBase.fly;
1998         if (el.getBoundingClientRect) {
1999             b = el.getBoundingClientRect();
2000             scroll = fly(document).getScroll();
2001             return [b.left + scroll.left, b.top + scroll.top];
2002         }
2003         var x = 0, y = 0;
2004
2005         p = el;
2006
2007         var hasAbsolute = fly(el).getStyle("position") == "absolute";
2008
2009         while (p) {
2010
2011             x += p.offsetLeft;
2012             y += p.offsetTop;
2013
2014             if (!hasAbsolute && fly(p).getStyle("position") == "absolute") {
2015                 hasAbsolute = true;
2016             }
2017
2018             if (Roo.isGecko) {
2019                 pe = fly(p);
2020
2021                 var bt = parseInt(pe.getStyle("borderTopWidth"), 10) || 0;
2022                 var bl = parseInt(pe.getStyle("borderLeftWidth"), 10) || 0;
2023
2024
2025                 x += bl;
2026                 y += bt;
2027
2028
2029                 if (p != el && pe.getStyle('overflow') != 'visible') {
2030                     x += bl;
2031                     y += bt;
2032                 }
2033             }
2034             p = p.offsetParent;
2035         }
2036
2037         if (Roo.isSafari && hasAbsolute) {
2038             x -= bd.offsetLeft;
2039             y -= bd.offsetTop;
2040         }
2041
2042         if (Roo.isGecko && !hasAbsolute) {
2043             var dbd = fly(bd);
2044             x += parseInt(dbd.getStyle("borderLeftWidth"), 10) || 0;
2045             y += parseInt(dbd.getStyle("borderTopWidth"), 10) || 0;
2046         }
2047
2048         p = el.parentNode;
2049         while (p && p != bd) {
2050             if (!Roo.isOpera || (p.tagName != 'TR' && fly(p).getStyle("display") != "inline")) {
2051                 x -= p.scrollLeft;
2052                 y -= p.scrollTop;
2053             }
2054             p = p.parentNode;
2055         }
2056         return [x, y];
2057     },
2058  
2059   
2060
2061
2062     setXY : function(el, xy) {
2063         el = Roo.fly(el, '_setXY');
2064         el.position();
2065         var pts = el.translatePoints(xy);
2066         if (xy[0] !== false) {
2067             el.dom.style.left = pts.left + "px";
2068         }
2069         if (xy[1] !== false) {
2070             el.dom.style.top = pts.top + "px";
2071         }
2072     },
2073
2074     setX : function(el, x) {
2075         this.setXY(el, [x, false]);
2076     },
2077
2078     setY : function(el, y) {
2079         this.setXY(el, [false, y]);
2080     }
2081 };
2082 /*
2083  * Portions of this file are based on pieces of Yahoo User Interface Library
2084  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
2085  * YUI licensed under the BSD License:
2086  * http://developer.yahoo.net/yui/license.txt
2087  * <script type="text/javascript">
2088  *
2089  */
2090
2091 Roo.lib.Event = function() {
2092     var loadComplete = false;
2093     var listeners = [];
2094     var unloadListeners = [];
2095     var retryCount = 0;
2096     var onAvailStack = [];
2097     var counter = 0;
2098     var lastError = null;
2099
2100     return {
2101         POLL_RETRYS: 200,
2102         POLL_INTERVAL: 20,
2103         EL: 0,
2104         TYPE: 1,
2105         FN: 2,
2106         WFN: 3,
2107         OBJ: 3,
2108         ADJ_SCOPE: 4,
2109         _interval: null,
2110
2111         startInterval: function() {
2112             if (!this._interval) {
2113                 var self = this;
2114                 var callback = function() {
2115                     self._tryPreloadAttach();
2116                 };
2117                 this._interval = setInterval(callback, this.POLL_INTERVAL);
2118
2119             }
2120         },
2121
2122         onAvailable: function(p_id, p_fn, p_obj, p_override) {
2123             onAvailStack.push({ id:         p_id,
2124                 fn:         p_fn,
2125                 obj:        p_obj,
2126                 override:   p_override,
2127                 checkReady: false    });
2128
2129             retryCount = this.POLL_RETRYS;
2130             this.startInterval();
2131         },
2132
2133
2134         addListener: function(el, eventName, fn) {
2135             el = Roo.getDom(el);
2136             if (!el || !fn) {
2137                 return false;
2138             }
2139
2140             if ("unload" == eventName) {
2141                 unloadListeners[unloadListeners.length] =
2142                 [el, eventName, fn];
2143                 return true;
2144             }
2145
2146             var wrappedFn = function(e) {
2147                 return fn(Roo.lib.Event.getEvent(e));
2148             };
2149
2150             var li = [el, eventName, fn, wrappedFn];
2151
2152             var index = listeners.length;
2153             listeners[index] = li;
2154
2155             this.doAdd(el, eventName, wrappedFn, false);
2156             return true;
2157
2158         },
2159
2160
2161         removeListener: function(el, eventName, fn) {
2162             var i, len;
2163
2164             el = Roo.getDom(el);
2165
2166             if(!fn) {
2167                 return this.purgeElement(el, false, eventName);
2168             }
2169
2170
2171             if ("unload" == eventName) {
2172
2173                 for (i = 0,len = unloadListeners.length; i < len; i++) {
2174                     var li = unloadListeners[i];
2175                     if (li &&
2176                         li[0] == el &&
2177                         li[1] == eventName &&
2178                         li[2] == fn) {
2179                         unloadListeners.splice(i, 1);
2180                         return true;
2181                     }
2182                 }
2183
2184                 return false;
2185             }
2186
2187             var cacheItem = null;
2188
2189
2190             var index = arguments[3];
2191
2192             if ("undefined" == typeof index) {
2193                 index = this._getCacheIndex(el, eventName, fn);
2194             }
2195
2196             if (index >= 0) {
2197                 cacheItem = listeners[index];
2198             }
2199
2200             if (!el || !cacheItem) {
2201                 return false;
2202             }
2203
2204             this.doRemove(el, eventName, cacheItem[this.WFN], false);
2205
2206             delete listeners[index][this.WFN];
2207             delete listeners[index][this.FN];
2208             listeners.splice(index, 1);
2209
2210             return true;
2211
2212         },
2213
2214
2215         getTarget: function(ev, resolveTextNode) {
2216             ev = ev.browserEvent || ev;
2217             ev = ev.touches ? (ev.touches[0] || ev.changedTouches[0] || ev )  : ev;
2218             var t = ev.target || ev.srcElement;
2219             return this.resolveTextNode(t);
2220         },
2221
2222
2223         resolveTextNode: function(node) {
2224             if (Roo.isSafari && node && 3 == node.nodeType) {
2225                 return node.parentNode;
2226             } else {
2227                 return node;
2228             }
2229         },
2230
2231
2232         getPageX: function(ev) {
2233             ev = ev.browserEvent || ev;
2234             ev = ev.touches ? (ev.touches[0] || ev.changedTouches[0] || ev )  : ev;
2235             var x = ev.pageX;
2236             if (!x && 0 !== x) {
2237                 x = ev.clientX || 0;
2238
2239                 if (Roo.isIE) {
2240                     x += this.getScroll()[1];
2241                 }
2242             }
2243
2244             return x;
2245         },
2246
2247
2248         getPageY: function(ev) {
2249             ev = ev.browserEvent || ev;
2250             ev = ev.touches ? (ev.touches[0] || ev.changedTouches[0] || ev )  : ev;
2251             var y = ev.pageY;
2252             if (!y && 0 !== y) {
2253                 y = ev.clientY || 0;
2254
2255                 if (Roo.isIE) {
2256                     y += this.getScroll()[0];
2257                 }
2258             }
2259
2260
2261             return y;
2262         },
2263
2264
2265         getXY: function(ev) {
2266             ev = ev.browserEvent || ev;
2267             ev = ev.touches ? (ev.touches[0] || ev.changedTouches[0] || ev )  : ev;
2268             return [this.getPageX(ev), this.getPageY(ev)];
2269         },
2270
2271
2272         getRelatedTarget: function(ev) {
2273             ev = ev.browserEvent || ev;
2274             ev = ev.touches ? (ev.touches[0] || ev.changedTouches[0] || ev )  : ev;
2275             var t = ev.relatedTarget;
2276             if (!t) {
2277                 if (ev.type == "mouseout") {
2278                     t = ev.toElement;
2279                 } else if (ev.type == "mouseover") {
2280                     t = ev.fromElement;
2281                 }
2282             }
2283
2284             return this.resolveTextNode(t);
2285         },
2286
2287
2288         getTime: function(ev) {
2289             ev = ev.browserEvent || ev;
2290             ev = ev.touches ? (ev.touches[0] || ev.changedTouches[0] || ev )  : ev;
2291             if (!ev.time) {
2292                 var t = new Date().getTime();
2293                 try {
2294                     ev.time = t;
2295                 } catch(ex) {
2296                     this.lastError = ex;
2297                     return t;
2298                 }
2299             }
2300
2301             return ev.time;
2302         },
2303
2304
2305         stopEvent: function(ev) {
2306             this.stopPropagation(ev);
2307             this.preventDefault(ev);
2308         },
2309
2310
2311         stopPropagation: function(ev) {
2312             ev = ev.browserEvent || ev;
2313             if (ev.stopPropagation) {
2314                 ev.stopPropagation();
2315             } else {
2316                 ev.cancelBubble = true;
2317             }
2318         },
2319
2320
2321         preventDefault: function(ev) {
2322             ev = ev.browserEvent || ev;
2323             if(ev.preventDefault) {
2324                 ev.preventDefault();
2325             } else {
2326                 ev.returnValue = false;
2327             }
2328         },
2329
2330
2331         getEvent: function(e) {
2332             var ev = e || window.event;
2333             if (!ev) {
2334                 var c = this.getEvent.caller;
2335                 while (c) {
2336                     ev = c.arguments[0];
2337                     if (ev && Event == ev.constructor) {
2338                         break;
2339                     }
2340                     c = c.caller;
2341                 }
2342             }
2343             return ev;
2344         },
2345
2346
2347         getCharCode: function(ev) {
2348             ev = ev.browserEvent || ev;
2349             return ev.charCode || ev.keyCode || 0;
2350         },
2351
2352
2353         _getCacheIndex: function(el, eventName, fn) {
2354             for (var i = 0,len = listeners.length; i < len; ++i) {
2355                 var li = listeners[i];
2356                 if (li &&
2357                     li[this.FN] == fn &&
2358                     li[this.EL] == el &&
2359                     li[this.TYPE] == eventName) {
2360                     return i;
2361                 }
2362             }
2363
2364             return -1;
2365         },
2366
2367
2368         elCache: {},
2369
2370
2371         getEl: function(id) {
2372             return document.getElementById(id);
2373         },
2374
2375
2376         clearCache: function() {
2377         },
2378
2379
2380         _load: function(e) {
2381             loadComplete = true;
2382             var EU = Roo.lib.Event;
2383
2384
2385             if (Roo.isIE) {
2386                 EU.doRemove(window, "load", EU._load);
2387             }
2388         },
2389
2390
2391         _tryPreloadAttach: function() {
2392
2393             if (this.locked) {
2394                 return false;
2395             }
2396
2397             this.locked = true;
2398
2399
2400             var tryAgain = !loadComplete;
2401             if (!tryAgain) {
2402                 tryAgain = (retryCount > 0);
2403             }
2404
2405
2406             var notAvail = [];
2407             for (var i = 0,len = onAvailStack.length; i < len; ++i) {
2408                 var item = onAvailStack[i];
2409                 if (item) {
2410                     var el = this.getEl(item.id);
2411
2412                     if (el) {
2413                         if (!item.checkReady ||
2414                             loadComplete ||
2415                             el.nextSibling ||
2416                             (document && document.body)) {
2417
2418                             var scope = el;
2419                             if (item.override) {
2420                                 if (item.override === true) {
2421                                     scope = item.obj;
2422                                 } else {
2423                                     scope = item.override;
2424                                 }
2425                             }
2426                             item.fn.call(scope, item.obj);
2427                             onAvailStack[i] = null;
2428                         }
2429                     } else {
2430                         notAvail.push(item);
2431                     }
2432                 }
2433             }
2434
2435             retryCount = (notAvail.length === 0) ? 0 : retryCount - 1;
2436
2437             if (tryAgain) {
2438
2439                 this.startInterval();
2440             } else {
2441                 clearInterval(this._interval);
2442                 this._interval = null;
2443             }
2444
2445             this.locked = false;
2446
2447             return true;
2448
2449         },
2450
2451
2452         purgeElement: function(el, recurse, eventName) {
2453             var elListeners = this.getListeners(el, eventName);
2454             if (elListeners) {
2455                 for (var i = 0,len = elListeners.length; i < len; ++i) {
2456                     var l = elListeners[i];
2457                     this.removeListener(el, l.type, l.fn);
2458                 }
2459             }
2460
2461             if (recurse && el && el.childNodes) {
2462                 for (i = 0,len = el.childNodes.length; i < len; ++i) {
2463                     this.purgeElement(el.childNodes[i], recurse, eventName);
2464                 }
2465             }
2466         },
2467
2468
2469         getListeners: function(el, eventName) {
2470             var results = [], searchLists;
2471             if (!eventName) {
2472                 searchLists = [listeners, unloadListeners];
2473             } else if (eventName == "unload") {
2474                 searchLists = [unloadListeners];
2475             } else {
2476                 searchLists = [listeners];
2477             }
2478
2479             for (var j = 0; j < searchLists.length; ++j) {
2480                 var searchList = searchLists[j];
2481                 if (searchList && searchList.length > 0) {
2482                     for (var i = 0,len = searchList.length; i < len; ++i) {
2483                         var l = searchList[i];
2484                         if (l && l[this.EL] === el &&
2485                             (!eventName || eventName === l[this.TYPE])) {
2486                             results.push({
2487                                 type:   l[this.TYPE],
2488                                 fn:     l[this.FN],
2489                                 obj:    l[this.OBJ],
2490                                 adjust: l[this.ADJ_SCOPE],
2491                                 index:  i
2492                             });
2493                         }
2494                     }
2495                 }
2496             }
2497
2498             return (results.length) ? results : null;
2499         },
2500
2501
2502         _unload: function(e) {
2503
2504             var EU = Roo.lib.Event, i, j, l, len, index;
2505
2506             for (i = 0,len = unloadListeners.length; i < len; ++i) {
2507                 l = unloadListeners[i];
2508                 if (l) {
2509                     var scope = window;
2510                     if (l[EU.ADJ_SCOPE]) {
2511                         if (l[EU.ADJ_SCOPE] === true) {
2512                             scope = l[EU.OBJ];
2513                         } else {
2514                             scope = l[EU.ADJ_SCOPE];
2515                         }
2516                     }
2517                     l[EU.FN].call(scope, EU.getEvent(e), l[EU.OBJ]);
2518                     unloadListeners[i] = null;
2519                     l = null;
2520                     scope = null;
2521                 }
2522             }
2523
2524             unloadListeners = null;
2525
2526             if (listeners && listeners.length > 0) {
2527                 j = listeners.length;
2528                 while (j) {
2529                     index = j - 1;
2530                     l = listeners[index];
2531                     if (l) {
2532                         EU.removeListener(l[EU.EL], l[EU.TYPE],
2533                                 l[EU.FN], index);
2534                     }
2535                     j = j - 1;
2536                 }
2537                 l = null;
2538
2539                 EU.clearCache();
2540             }
2541
2542             EU.doRemove(window, "unload", EU._unload);
2543
2544         },
2545
2546
2547         getScroll: function() {
2548             var dd = document.documentElement, db = document.body;
2549             if (dd && (dd.scrollTop || dd.scrollLeft)) {
2550                 return [dd.scrollTop, dd.scrollLeft];
2551             } else if (db) {
2552                 return [db.scrollTop, db.scrollLeft];
2553             } else {
2554                 return [0, 0];
2555             }
2556         },
2557
2558
2559         doAdd: function () {
2560             if (window.addEventListener) {
2561                 return function(el, eventName, fn, capture) {
2562                     el.addEventListener(eventName, fn, (capture));
2563                 };
2564             } else if (window.attachEvent) {
2565                 return function(el, eventName, fn, capture) {
2566                     el.attachEvent("on" + eventName, fn);
2567                 };
2568             } else {
2569                 return function() {
2570                 };
2571             }
2572         }(),
2573
2574
2575         doRemove: function() {
2576             if (window.removeEventListener) {
2577                 return function (el, eventName, fn, capture) {
2578                     el.removeEventListener(eventName, fn, (capture));
2579                 };
2580             } else if (window.detachEvent) {
2581                 return function (el, eventName, fn) {
2582                     el.detachEvent("on" + eventName, fn);
2583                 };
2584             } else {
2585                 return function() {
2586                 };
2587             }
2588         }()
2589     };
2590     
2591 }();
2592 (function() {     
2593    
2594     var E = Roo.lib.Event;
2595     E.on = E.addListener;
2596     E.un = E.removeListener;
2597
2598     if (document && document.body) {
2599         E._load();
2600     } else {
2601         E.doAdd(window, "load", E._load);
2602     }
2603     E.doAdd(window, "unload", E._unload);
2604     E._tryPreloadAttach();
2605 })();
2606
2607 /*
2608  * Portions of this file are based on pieces of Yahoo User Interface Library
2609  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
2610  * YUI licensed under the BSD License:
2611  * http://developer.yahoo.net/yui/license.txt
2612  * <script type="text/javascript">
2613  *
2614  */
2615
2616 (function() {
2617     /**
2618      * @class Roo.lib.Ajax
2619      *
2620      */
2621     Roo.lib.Ajax = {
2622         /**
2623          * @static 
2624          */
2625         request : function(method, uri, cb, data, options) {
2626             if(options){
2627                 var hs = options.headers;
2628                 if(hs){
2629                     for(var h in hs){
2630                         if(hs.hasOwnProperty(h)){
2631                             this.initHeader(h, hs[h], false);
2632                         }
2633                     }
2634                 }
2635                 if(options.xmlData){
2636                     this.initHeader('Content-Type', 'text/xml', false);
2637                     method = 'POST';
2638                     data = options.xmlData;
2639                 }
2640             }
2641
2642             return this.asyncRequest(method, uri, cb, data);
2643         },
2644
2645         serializeForm : function(form) {
2646             if(typeof form == 'string') {
2647                 form = (document.getElementById(form) || document.forms[form]);
2648             }
2649
2650             var el, name, val, disabled, data = '', hasSubmit = false;
2651             for (var i = 0; i < form.elements.length; i++) {
2652                 el = form.elements[i];
2653                 disabled = form.elements[i].disabled;
2654                 name = form.elements[i].name;
2655                 val = form.elements[i].value;
2656
2657                 if (!disabled && name){
2658                     switch (el.type)
2659                             {
2660                         case 'select-one':
2661                         case 'select-multiple':
2662                             for (var j = 0; j < el.options.length; j++) {
2663                                 if (el.options[j].selected) {
2664                                     if (Roo.isIE) {
2665                                         data += Roo.encodeURIComponent(name) + '=' + Roo.encodeURIComponent(el.options[j].attributes['value'].specified ? el.options[j].value : el.options[j].text) + '&';
2666                                     }
2667                                     else {
2668                                         data += Roo.encodeURIComponent(name) + '=' + Roo.encodeURIComponent(el.options[j].hasAttribute('value') ? el.options[j].value : el.options[j].text) + '&';
2669                                     }
2670                                 }
2671                             }
2672                             break;
2673                         case 'radio':
2674                         case 'checkbox':
2675                             if (el.checked) {
2676                                 data += Roo.encodeURIComponent(name) + '=' + Roo.encodeURIComponent(val) + '&';
2677                             }
2678                             break;
2679                         case 'file':
2680
2681                         case undefined:
2682
2683                         case 'reset':
2684
2685                         case 'button':
2686
2687                             break;
2688                         case 'submit':
2689                             if(hasSubmit == false) {
2690                                 data += Roo.encodeURIComponent(name) + '=' + Roo.encodeURIComponent(val) + '&';
2691                                 hasSubmit = true;
2692                             }
2693                             break;
2694                         default:
2695                             data += Roo.encodeURIComponent(name) + '=' + Roo.encodeURIComponent(val) + '&';
2696                             break;
2697                     }
2698                 }
2699             }
2700             data = data.substr(0, data.length - 1);
2701             return data;
2702         },
2703
2704         headers:{},
2705
2706         hasHeaders:false,
2707
2708         useDefaultHeader:true,
2709
2710         defaultPostHeader:'application/x-www-form-urlencoded',
2711
2712         useDefaultXhrHeader:true,
2713
2714         defaultXhrHeader:'XMLHttpRequest',
2715
2716         hasDefaultHeaders:true,
2717
2718         defaultHeaders:{},
2719
2720         poll:{},
2721
2722         timeout:{},
2723
2724         pollInterval:50,
2725
2726         transactionId:0,
2727
2728         setProgId:function(id)
2729         {
2730             this.activeX.unshift(id);
2731         },
2732
2733         setDefaultPostHeader:function(b)
2734         {
2735             this.useDefaultHeader = b;
2736         },
2737
2738         setDefaultXhrHeader:function(b)
2739         {
2740             this.useDefaultXhrHeader = b;
2741         },
2742
2743         setPollingInterval:function(i)
2744         {
2745             if (typeof i == 'number' && isFinite(i)) {
2746                 this.pollInterval = i;
2747             }
2748         },
2749
2750         createXhrObject:function(transactionId)
2751         {
2752             var obj,http;
2753             try
2754             {
2755
2756                 http = new XMLHttpRequest();
2757
2758                 obj = { conn:http, tId:transactionId };
2759             }
2760             catch(e)
2761             {
2762                 for (var i = 0; i < this.activeX.length; ++i) {
2763                     try
2764                     {
2765
2766                         http = new ActiveXObject(this.activeX[i]);
2767
2768                         obj = { conn:http, tId:transactionId };
2769                         break;
2770                     }
2771                     catch(e) {
2772                     }
2773                 }
2774             }
2775             finally
2776             {
2777                 return obj;
2778             }
2779         },
2780
2781         getConnectionObject:function()
2782         {
2783             var o;
2784             var tId = this.transactionId;
2785
2786             try
2787             {
2788                 o = this.createXhrObject(tId);
2789                 if (o) {
2790                     this.transactionId++;
2791                 }
2792             }
2793             catch(e) {
2794             }
2795             finally
2796             {
2797                 return o;
2798             }
2799         },
2800
2801         asyncRequest:function(method, uri, callback, postData)
2802         {
2803             var o = this.getConnectionObject();
2804
2805             if (!o) {
2806                 return null;
2807             }
2808             else {
2809                 o.conn.open(method, uri, true);
2810
2811                 if (this.useDefaultXhrHeader) {
2812                     if (!this.defaultHeaders['X-Requested-With']) {
2813                         this.initHeader('X-Requested-With', this.defaultXhrHeader, true);
2814                     }
2815                 }
2816
2817                 if(postData && this.useDefaultHeader){
2818                     this.initHeader('Content-Type', this.defaultPostHeader);
2819                 }
2820
2821                  if (this.hasDefaultHeaders || this.hasHeaders) {
2822                     this.setHeader(o);
2823                 }
2824
2825                 this.handleReadyState(o, callback);
2826                 o.conn.send(postData || null);
2827
2828                 return o;
2829             }
2830         },
2831
2832         handleReadyState:function(o, callback)
2833         {
2834             var oConn = this;
2835
2836             if (callback && callback.timeout) {
2837                 
2838                 this.timeout[o.tId] = window.setTimeout(function() {
2839                     oConn.abort(o, callback, true);
2840                 }, callback.timeout);
2841             }
2842
2843             this.poll[o.tId] = window.setInterval(
2844                     function() {
2845                         if (o.conn && o.conn.readyState == 4) {
2846                             window.clearInterval(oConn.poll[o.tId]);
2847                             delete oConn.poll[o.tId];
2848
2849                             if(callback && callback.timeout) {
2850                                 window.clearTimeout(oConn.timeout[o.tId]);
2851                                 delete oConn.timeout[o.tId];
2852                             }
2853
2854                             oConn.handleTransactionResponse(o, callback);
2855                         }
2856                     }
2857                     , this.pollInterval);
2858         },
2859
2860         handleTransactionResponse:function(o, callback, isAbort)
2861         {
2862
2863             if (!callback) {
2864                 this.releaseObject(o);
2865                 return;
2866             }
2867
2868             var httpStatus, responseObject;
2869
2870             try
2871             {
2872                 if (o.conn.status !== undefined && o.conn.status != 0) {
2873                     httpStatus = o.conn.status;
2874                 }
2875                 else {
2876                     httpStatus = 13030;
2877                 }
2878             }
2879             catch(e) {
2880
2881
2882                 httpStatus = 13030;
2883             }
2884
2885             if (httpStatus >= 200 && httpStatus < 300) {
2886                 responseObject = this.createResponseObject(o, callback.argument);
2887                 if (callback.success) {
2888                     if (!callback.scope) {
2889                         callback.success(responseObject);
2890                     }
2891                     else {
2892
2893
2894                         callback.success.apply(callback.scope, [responseObject]);
2895                     }
2896                 }
2897             }
2898             else {
2899                 switch (httpStatus) {
2900
2901                     case 12002:
2902                     case 12029:
2903                     case 12030:
2904                     case 12031:
2905                     case 12152:
2906                     case 13030:
2907                         responseObject = this.createExceptionObject(o.tId, callback.argument, (isAbort ? isAbort : false));
2908                         if (callback.failure) {
2909                             if (!callback.scope) {
2910                                 callback.failure(responseObject);
2911                             }
2912                             else {
2913                                 callback.failure.apply(callback.scope, [responseObject]);
2914                             }
2915                         }
2916                         break;
2917                     default:
2918                         responseObject = this.createResponseObject(o, callback.argument);
2919                         if (callback.failure) {
2920                             if (!callback.scope) {
2921                                 callback.failure(responseObject);
2922                             }
2923                             else {
2924                                 callback.failure.apply(callback.scope, [responseObject]);
2925                             }
2926                         }
2927                 }
2928             }
2929
2930             this.releaseObject(o);
2931             responseObject = null;
2932         },
2933
2934         createResponseObject:function(o, callbackArg)
2935         {
2936             var obj = {};
2937             var headerObj = {};
2938
2939             try
2940             {
2941                 var headerStr = o.conn.getAllResponseHeaders();
2942                 var header = headerStr.split('\n');
2943                 for (var i = 0; i < header.length; i++) {
2944                     var delimitPos = header[i].indexOf(':');
2945                     if (delimitPos != -1) {
2946                         headerObj[header[i].substring(0, delimitPos)] = header[i].substring(delimitPos + 2);
2947                     }
2948                 }
2949             }
2950             catch(e) {
2951             }
2952
2953             obj.tId = o.tId;
2954             obj.status = o.conn.status;
2955             obj.statusText = o.conn.statusText;
2956             obj.getResponseHeader = headerObj;
2957             obj.getAllResponseHeaders = headerStr;
2958             obj.responseText = o.conn.responseText;
2959             obj.responseXML = o.conn.responseXML;
2960
2961             if (typeof callbackArg !== undefined) {
2962                 obj.argument = callbackArg;
2963             }
2964
2965             return obj;
2966         },
2967
2968         createExceptionObject:function(tId, callbackArg, isAbort)
2969         {
2970             var COMM_CODE = 0;
2971             var COMM_ERROR = 'communication failure';
2972             var ABORT_CODE = -1;
2973             var ABORT_ERROR = 'transaction aborted';
2974
2975             var obj = {};
2976
2977             obj.tId = tId;
2978             if (isAbort) {
2979                 obj.status = ABORT_CODE;
2980                 obj.statusText = ABORT_ERROR;
2981             }
2982             else {
2983                 obj.status = COMM_CODE;
2984                 obj.statusText = COMM_ERROR;
2985             }
2986
2987             if (callbackArg) {
2988                 obj.argument = callbackArg;
2989             }
2990
2991             return obj;
2992         },
2993
2994         initHeader:function(label, value, isDefault)
2995         {
2996             var headerObj = (isDefault) ? this.defaultHeaders : this.headers;
2997
2998             if (headerObj[label] === undefined) {
2999                 headerObj[label] = value;
3000             }
3001             else {
3002
3003
3004                 headerObj[label] = value + "," + headerObj[label];
3005             }
3006
3007             if (isDefault) {
3008                 this.hasDefaultHeaders = true;
3009             }
3010             else {
3011                 this.hasHeaders = true;
3012             }
3013         },
3014
3015
3016         setHeader:function(o)
3017         {
3018             if (this.hasDefaultHeaders) {
3019                 for (var prop in this.defaultHeaders) {
3020                     if (this.defaultHeaders.hasOwnProperty(prop)) {
3021                         o.conn.setRequestHeader(prop, this.defaultHeaders[prop]);
3022                     }
3023                 }
3024             }
3025
3026             if (this.hasHeaders) {
3027                 for (var prop in this.headers) {
3028                     if (this.headers.hasOwnProperty(prop)) {
3029                         o.conn.setRequestHeader(prop, this.headers[prop]);
3030                     }
3031                 }
3032                 this.headers = {};
3033                 this.hasHeaders = false;
3034             }
3035         },
3036
3037         resetDefaultHeaders:function() {
3038             delete this.defaultHeaders;
3039             this.defaultHeaders = {};
3040             this.hasDefaultHeaders = false;
3041         },
3042
3043         abort:function(o, callback, isTimeout)
3044         {
3045             if(this.isCallInProgress(o)) {
3046                 o.conn.abort();
3047                 window.clearInterval(this.poll[o.tId]);
3048                 delete this.poll[o.tId];
3049                 if (isTimeout) {
3050                     delete this.timeout[o.tId];
3051                 }
3052
3053                 this.handleTransactionResponse(o, callback, true);
3054
3055                 return true;
3056             }
3057             else {
3058                 return false;
3059             }
3060         },
3061
3062
3063         isCallInProgress:function(o)
3064         {
3065             if (o && o.conn) {
3066                 return o.conn.readyState != 4 && o.conn.readyState != 0;
3067             }
3068             else {
3069
3070                 return false;
3071             }
3072         },
3073
3074
3075         releaseObject:function(o)
3076         {
3077
3078             o.conn = null;
3079
3080             o = null;
3081         },
3082
3083         activeX:[
3084         'MSXML2.XMLHTTP.3.0',
3085         'MSXML2.XMLHTTP',
3086         'Microsoft.XMLHTTP'
3087         ]
3088
3089
3090     };
3091 })();/*
3092  * Portions of this file are based on pieces of Yahoo User Interface Library
3093  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3094  * YUI licensed under the BSD License:
3095  * http://developer.yahoo.net/yui/license.txt
3096  * <script type="text/javascript">
3097  *
3098  */
3099
3100 Roo.lib.Region = function(t, r, b, l) {
3101     this.top = t;
3102     this[1] = t;
3103     this.right = r;
3104     this.bottom = b;
3105     this.left = l;
3106     this[0] = l;
3107 };
3108
3109
3110 Roo.lib.Region.prototype = {
3111     contains : function(region) {
3112         return ( region.left >= this.left &&
3113                  region.right <= this.right &&
3114                  region.top >= this.top &&
3115                  region.bottom <= this.bottom    );
3116
3117     },
3118
3119     getArea : function() {
3120         return ( (this.bottom - this.top) * (this.right - this.left) );
3121     },
3122
3123     intersect : function(region) {
3124         var t = Math.max(this.top, region.top);
3125         var r = Math.min(this.right, region.right);
3126         var b = Math.min(this.bottom, region.bottom);
3127         var l = Math.max(this.left, region.left);
3128
3129         if (b >= t && r >= l) {
3130             return new Roo.lib.Region(t, r, b, l);
3131         } else {
3132             return null;
3133         }
3134     },
3135     union : function(region) {
3136         var t = Math.min(this.top, region.top);
3137         var r = Math.max(this.right, region.right);
3138         var b = Math.max(this.bottom, region.bottom);
3139         var l = Math.min(this.left, region.left);
3140
3141         return new Roo.lib.Region(t, r, b, l);
3142     },
3143
3144     adjust : function(t, l, b, r) {
3145         this.top += t;
3146         this.left += l;
3147         this.right += r;
3148         this.bottom += b;
3149         return this;
3150     }
3151 };
3152
3153 Roo.lib.Region.getRegion = function(el) {
3154     var p = Roo.lib.Dom.getXY(el);
3155
3156     var t = p[1];
3157     var r = p[0] + el.offsetWidth;
3158     var b = p[1] + el.offsetHeight;
3159     var l = p[0];
3160
3161     return new Roo.lib.Region(t, r, b, l);
3162 };
3163 /*
3164  * Portions of this file are based on pieces of Yahoo User Interface Library
3165  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3166  * YUI licensed under the BSD License:
3167  * http://developer.yahoo.net/yui/license.txt
3168  * <script type="text/javascript">
3169  *
3170  */
3171 //@@dep Roo.lib.Region
3172
3173
3174 Roo.lib.Point = function(x, y) {
3175     if (x instanceof Array) {
3176         y = x[1];
3177         x = x[0];
3178     }
3179     this.x = this.right = this.left = this[0] = x;
3180     this.y = this.top = this.bottom = this[1] = y;
3181 };
3182
3183 Roo.lib.Point.prototype = new Roo.lib.Region();
3184 /*
3185  * Portions of this file are based on pieces of Yahoo User Interface Library
3186  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3187  * YUI licensed under the BSD License:
3188  * http://developer.yahoo.net/yui/license.txt
3189  * <script type="text/javascript">
3190  *
3191  */
3192  
3193 (function() {   
3194
3195     Roo.lib.Anim = {
3196         scroll : function(el, args, duration, easing, cb, scope) {
3197             this.run(el, args, duration, easing, cb, scope, Roo.lib.Scroll);
3198         },
3199
3200         motion : function(el, args, duration, easing, cb, scope) {
3201             this.run(el, args, duration, easing, cb, scope, Roo.lib.Motion);
3202         },
3203
3204         color : function(el, args, duration, easing, cb, scope) {
3205             this.run(el, args, duration, easing, cb, scope, Roo.lib.ColorAnim);
3206         },
3207
3208         run : function(el, args, duration, easing, cb, scope, type) {
3209             type = type || Roo.lib.AnimBase;
3210             if (typeof easing == "string") {
3211                 easing = Roo.lib.Easing[easing];
3212             }
3213             var anim = new type(el, args, duration, easing);
3214             anim.animateX(function() {
3215                 Roo.callback(cb, scope);
3216             });
3217             return anim;
3218         }
3219     };
3220 })();/*
3221  * Portions of this file are based on pieces of Yahoo User Interface Library
3222  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3223  * YUI licensed under the BSD License:
3224  * http://developer.yahoo.net/yui/license.txt
3225  * <script type="text/javascript">
3226  *
3227  */
3228
3229 (function() {    
3230     var libFlyweight;
3231     
3232     function fly(el) {
3233         if (!libFlyweight) {
3234             libFlyweight = new Roo.Element.Flyweight();
3235         }
3236         libFlyweight.dom = el;
3237         return libFlyweight;
3238     }
3239
3240     // since this uses fly! - it cant be in DOM (which does not have fly yet..)
3241     
3242    
3243     
3244     Roo.lib.AnimBase = function(el, attributes, duration, method) {
3245         if (el) {
3246             this.init(el, attributes, duration, method);
3247         }
3248     };
3249
3250     Roo.lib.AnimBase.fly = fly;
3251     
3252     
3253     
3254     Roo.lib.AnimBase.prototype = {
3255
3256         toString: function() {
3257             var el = this.getEl();
3258             var id = el.id || el.tagName;
3259             return ("Anim " + id);
3260         },
3261
3262         patterns: {
3263             noNegatives:        /width|height|opacity|padding/i,
3264             offsetAttribute:  /^((width|height)|(top|left))$/,
3265             defaultUnit:        /width|height|top$|bottom$|left$|right$/i,
3266             offsetUnit:         /\d+(em|%|en|ex|pt|in|cm|mm|pc)$/i
3267         },
3268
3269
3270         doMethod: function(attr, start, end) {
3271             return this.method(this.currentFrame, start, end - start, this.totalFrames);
3272         },
3273
3274
3275         setAttribute: function(attr, val, unit) {
3276             if (this.patterns.noNegatives.test(attr)) {
3277                 val = (val > 0) ? val : 0;
3278             }
3279
3280             Roo.fly(this.getEl(), '_anim').setStyle(attr, val + unit);
3281         },
3282
3283
3284         getAttribute: function(attr) {
3285             var el = this.getEl();
3286             var val = fly(el).getStyle(attr);
3287
3288             if (val !== 'auto' && !this.patterns.offsetUnit.test(val)) {
3289                 return parseFloat(val);
3290             }
3291
3292             var a = this.patterns.offsetAttribute.exec(attr) || [];
3293             var pos = !!( a[3] );
3294             var box = !!( a[2] );
3295
3296
3297             if (box || (fly(el).getStyle('position') == 'absolute' && pos)) {
3298                 val = el['offset' + a[0].charAt(0).toUpperCase() + a[0].substr(1)];
3299             } else {
3300                 val = 0;
3301             }
3302
3303             return val;
3304         },
3305
3306
3307         getDefaultUnit: function(attr) {
3308             if (this.patterns.defaultUnit.test(attr)) {
3309                 return 'px';
3310             }
3311
3312             return '';
3313         },
3314
3315         animateX : function(callback, scope) {
3316             var f = function() {
3317                 this.onComplete.removeListener(f);
3318                 if (typeof callback == "function") {
3319                     callback.call(scope || this, this);
3320                 }
3321             };
3322             this.onComplete.addListener(f, this);
3323             this.animate();
3324         },
3325
3326
3327         setRuntimeAttribute: function(attr) {
3328             var start;
3329             var end;
3330             var attributes = this.attributes;
3331
3332             this.runtimeAttributes[attr] = {};
3333
3334             var isset = function(prop) {
3335                 return (typeof prop !== 'undefined');
3336             };
3337
3338             if (!isset(attributes[attr]['to']) && !isset(attributes[attr]['by'])) {
3339                 return false;
3340             }
3341
3342             start = ( isset(attributes[attr]['from']) ) ? attributes[attr]['from'] : this.getAttribute(attr);
3343
3344
3345             if (isset(attributes[attr]['to'])) {
3346                 end = attributes[attr]['to'];
3347             } else if (isset(attributes[attr]['by'])) {
3348                 if (start.constructor == Array) {
3349                     end = [];
3350                     for (var i = 0, len = start.length; i < len; ++i) {
3351                         end[i] = start[i] + attributes[attr]['by'][i];
3352                     }
3353                 } else {
3354                     end = start + attributes[attr]['by'];
3355                 }
3356             }
3357
3358             this.runtimeAttributes[attr].start = start;
3359             this.runtimeAttributes[attr].end = end;
3360
3361
3362             this.runtimeAttributes[attr].unit = ( isset(attributes[attr].unit) ) ? attributes[attr]['unit'] : this.getDefaultUnit(attr);
3363         },
3364
3365
3366         init: function(el, attributes, duration, method) {
3367
3368             var isAnimated = false;
3369
3370
3371             var startTime = null;
3372
3373
3374             var actualFrames = 0;
3375
3376
3377             el = Roo.getDom(el);
3378
3379
3380             this.attributes = attributes || {};
3381
3382
3383             this.duration = duration || 1;
3384
3385
3386             this.method = method || Roo.lib.Easing.easeNone;
3387
3388
3389             this.useSeconds = true;
3390
3391
3392             this.currentFrame = 0;
3393
3394
3395             this.totalFrames = Roo.lib.AnimMgr.fps;
3396
3397
3398             this.getEl = function() {
3399                 return el;
3400             };
3401
3402
3403             this.isAnimated = function() {
3404                 return isAnimated;
3405             };
3406
3407
3408             this.getStartTime = function() {
3409                 return startTime;
3410             };
3411
3412             this.runtimeAttributes = {};
3413
3414
3415             this.animate = function() {
3416                 if (this.isAnimated()) {
3417                     return false;
3418                 }
3419
3420                 this.currentFrame = 0;
3421
3422                 this.totalFrames = ( this.useSeconds ) ? Math.ceil(Roo.lib.AnimMgr.fps * this.duration) : this.duration;
3423
3424                 Roo.lib.AnimMgr.registerElement(this);
3425             };
3426
3427
3428             this.stop = function(finish) {
3429                 if (finish) {
3430                     this.currentFrame = this.totalFrames;
3431                     this._onTween.fire();
3432                 }
3433                 Roo.lib.AnimMgr.stop(this);
3434             };
3435
3436             var onStart = function() {
3437                 this.onStart.fire();
3438
3439                 this.runtimeAttributes = {};
3440                 for (var attr in this.attributes) {
3441                     this.setRuntimeAttribute(attr);
3442                 }
3443
3444                 isAnimated = true;
3445                 actualFrames = 0;
3446                 startTime = new Date();
3447             };
3448
3449
3450             var onTween = function() {
3451                 var data = {
3452                     duration: new Date() - this.getStartTime(),
3453                     currentFrame: this.currentFrame
3454                 };
3455
3456                 data.toString = function() {
3457                     return (
3458                             'duration: ' + data.duration +
3459                             ', currentFrame: ' + data.currentFrame
3460                             );
3461                 };
3462
3463                 this.onTween.fire(data);
3464
3465                 var runtimeAttributes = this.runtimeAttributes;
3466
3467                 for (var attr in runtimeAttributes) {
3468                     this.setAttribute(attr, this.doMethod(attr, runtimeAttributes[attr].start, runtimeAttributes[attr].end), runtimeAttributes[attr].unit);
3469                 }
3470
3471                 actualFrames += 1;
3472             };
3473
3474             var onComplete = function() {
3475                 var actual_duration = (new Date() - startTime) / 1000 ;
3476
3477                 var data = {
3478                     duration: actual_duration,
3479                     frames: actualFrames,
3480                     fps: actualFrames / actual_duration
3481                 };
3482
3483                 data.toString = function() {
3484                     return (
3485                             'duration: ' + data.duration +
3486                             ', frames: ' + data.frames +
3487                             ', fps: ' + data.fps
3488                             );
3489                 };
3490
3491                 isAnimated = false;
3492                 actualFrames = 0;
3493                 this.onComplete.fire(data);
3494             };
3495
3496
3497             this._onStart = new Roo.util.Event(this);
3498             this.onStart = new Roo.util.Event(this);
3499             this.onTween = new Roo.util.Event(this);
3500             this._onTween = new Roo.util.Event(this);
3501             this.onComplete = new Roo.util.Event(this);
3502             this._onComplete = new Roo.util.Event(this);
3503             this._onStart.addListener(onStart);
3504             this._onTween.addListener(onTween);
3505             this._onComplete.addListener(onComplete);
3506         }
3507     };
3508 })();
3509 /*
3510  * Portions of this file are based on pieces of Yahoo User Interface Library
3511  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3512  * YUI licensed under the BSD License:
3513  * http://developer.yahoo.net/yui/license.txt
3514  * <script type="text/javascript">
3515  *
3516  */
3517
3518 Roo.lib.AnimMgr = new function() {
3519
3520     var thread = null;
3521
3522
3523     var queue = [];
3524
3525
3526     var tweenCount = 0;
3527
3528
3529     this.fps = 1000;
3530
3531
3532     this.delay = 1;
3533
3534
3535     this.registerElement = function(tween) {
3536         queue[queue.length] = tween;
3537         tweenCount += 1;
3538         tween._onStart.fire();
3539         this.start();
3540     };
3541
3542
3543     this.unRegister = function(tween, index) {
3544         tween._onComplete.fire();
3545         index = index || getIndex(tween);
3546         if (index != -1) {
3547             queue.splice(index, 1);
3548         }
3549
3550         tweenCount -= 1;
3551         if (tweenCount <= 0) {
3552             this.stop();
3553         }
3554     };
3555
3556
3557     this.start = function() {
3558         if (thread === null) {
3559             thread = setInterval(this.run, this.delay);
3560         }
3561     };
3562
3563
3564     this.stop = function(tween) {
3565         if (!tween) {
3566             clearInterval(thread);
3567
3568             for (var i = 0, len = queue.length; i < len; ++i) {
3569                 if (queue[0].isAnimated()) {
3570                     this.unRegister(queue[0], 0);
3571                 }
3572             }
3573
3574             queue = [];
3575             thread = null;
3576             tweenCount = 0;
3577         }
3578         else {
3579             this.unRegister(tween);
3580         }
3581     };
3582
3583
3584     this.run = function() {
3585         for (var i = 0, len = queue.length; i < len; ++i) {
3586             var tween = queue[i];
3587             if (!tween || !tween.isAnimated()) {
3588                 continue;
3589             }
3590
3591             if (tween.currentFrame < tween.totalFrames || tween.totalFrames === null)
3592             {
3593                 tween.currentFrame += 1;
3594
3595                 if (tween.useSeconds) {
3596                     correctFrame(tween);
3597                 }
3598                 tween._onTween.fire();
3599             }
3600             else {
3601                 Roo.lib.AnimMgr.stop(tween, i);
3602             }
3603         }
3604     };
3605
3606     var getIndex = function(anim) {
3607         for (var i = 0, len = queue.length; i < len; ++i) {
3608             if (queue[i] == anim) {
3609                 return i;
3610             }
3611         }
3612         return -1;
3613     };
3614
3615
3616     var correctFrame = function(tween) {
3617         var frames = tween.totalFrames;
3618         var frame = tween.currentFrame;
3619         var expected = (tween.currentFrame * tween.duration * 1000 / tween.totalFrames);
3620         var elapsed = (new Date() - tween.getStartTime());
3621         var tweak = 0;
3622
3623         if (elapsed < tween.duration * 1000) {
3624             tweak = Math.round((elapsed / expected - 1) * tween.currentFrame);
3625         } else {
3626             tweak = frames - (frame + 1);
3627         }
3628         if (tweak > 0 && isFinite(tweak)) {
3629             if (tween.currentFrame + tweak >= frames) {
3630                 tweak = frames - (frame + 1);
3631             }
3632
3633             tween.currentFrame += tweak;
3634         }
3635     };
3636 };
3637
3638     /*
3639  * Portions of this file are based on pieces of Yahoo User Interface Library
3640  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3641  * YUI licensed under the BSD License:
3642  * http://developer.yahoo.net/yui/license.txt
3643  * <script type="text/javascript">
3644  *
3645  */
3646 Roo.lib.Bezier = new function() {
3647
3648         this.getPosition = function(points, t) {
3649             var n = points.length;
3650             var tmp = [];
3651
3652             for (var i = 0; i < n; ++i) {
3653                 tmp[i] = [points[i][0], points[i][1]];
3654             }
3655
3656             for (var j = 1; j < n; ++j) {
3657                 for (i = 0; i < n - j; ++i) {
3658                     tmp[i][0] = (1 - t) * tmp[i][0] + t * tmp[parseInt(i + 1, 10)][0];
3659                     tmp[i][1] = (1 - t) * tmp[i][1] + t * tmp[parseInt(i + 1, 10)][1];
3660                 }
3661             }
3662
3663             return [ tmp[0][0], tmp[0][1] ];
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 (function() {
3675
3676     Roo.lib.ColorAnim = function(el, attributes, duration, method) {
3677         Roo.lib.ColorAnim.superclass.constructor.call(this, el, attributes, duration, method);
3678     };
3679
3680     Roo.extend(Roo.lib.ColorAnim, Roo.lib.AnimBase);
3681
3682     var fly = Roo.lib.AnimBase.fly;
3683     var Y = Roo.lib;
3684     var superclass = Y.ColorAnim.superclass;
3685     var proto = Y.ColorAnim.prototype;
3686
3687     proto.toString = function() {
3688         var el = this.getEl();
3689         var id = el.id || el.tagName;
3690         return ("ColorAnim " + id);
3691     };
3692
3693     proto.patterns.color = /color$/i;
3694     proto.patterns.rgb = /^rgb\(([0-9]+)\s*,\s*([0-9]+)\s*,\s*([0-9]+)\)$/i;
3695     proto.patterns.hex = /^#?([0-9A-F]{2})([0-9A-F]{2})([0-9A-F]{2})$/i;
3696     proto.patterns.hex3 = /^#?([0-9A-F]{1})([0-9A-F]{1})([0-9A-F]{1})$/i;
3697     proto.patterns.transparent = /^transparent|rgba\(0, 0, 0, 0\)$/;
3698
3699
3700     proto.parseColor = function(s) {
3701         if (s.length == 3) {
3702             return s;
3703         }
3704
3705         var c = this.patterns.hex.exec(s);
3706         if (c && c.length == 4) {
3707             return [ parseInt(c[1], 16), parseInt(c[2], 16), parseInt(c[3], 16) ];
3708         }
3709
3710         c = this.patterns.rgb.exec(s);
3711         if (c && c.length == 4) {
3712             return [ parseInt(c[1], 10), parseInt(c[2], 10), parseInt(c[3], 10) ];
3713         }
3714
3715         c = this.patterns.hex3.exec(s);
3716         if (c && c.length == 4) {
3717             return [ parseInt(c[1] + c[1], 16), parseInt(c[2] + c[2], 16), parseInt(c[3] + c[3], 16) ];
3718         }
3719
3720         return null;
3721     };
3722     // since this uses fly! - it cant be in ColorAnim (which does not have fly yet..)
3723     proto.getAttribute = function(attr) {
3724         var el = this.getEl();
3725         if (this.patterns.color.test(attr)) {
3726             var val = fly(el).getStyle(attr);
3727
3728             if (this.patterns.transparent.test(val)) {
3729                 var parent = el.parentNode;
3730                 val = fly(parent).getStyle(attr);
3731
3732                 while (parent && this.patterns.transparent.test(val)) {
3733                     parent = parent.parentNode;
3734                     val = fly(parent).getStyle(attr);
3735                     if (parent.tagName.toUpperCase() == 'HTML') {
3736                         val = '#fff';
3737                     }
3738                 }
3739             }
3740         } else {
3741             val = superclass.getAttribute.call(this, attr);
3742         }
3743
3744         return val;
3745     };
3746     proto.getAttribute = function(attr) {
3747         var el = this.getEl();
3748         if (this.patterns.color.test(attr)) {
3749             var val = fly(el).getStyle(attr);
3750
3751             if (this.patterns.transparent.test(val)) {
3752                 var parent = el.parentNode;
3753                 val = fly(parent).getStyle(attr);
3754
3755                 while (parent && this.patterns.transparent.test(val)) {
3756                     parent = parent.parentNode;
3757                     val = fly(parent).getStyle(attr);
3758                     if (parent.tagName.toUpperCase() == 'HTML') {
3759                         val = '#fff';
3760                     }
3761                 }
3762             }
3763         } else {
3764             val = superclass.getAttribute.call(this, attr);
3765         }
3766
3767         return val;
3768     };
3769
3770     proto.doMethod = function(attr, start, end) {
3771         var val;
3772
3773         if (this.patterns.color.test(attr)) {
3774             val = [];
3775             for (var i = 0, len = start.length; i < len; ++i) {
3776                 val[i] = superclass.doMethod.call(this, attr, start[i], end[i]);
3777             }
3778
3779             val = 'rgb(' + Math.floor(val[0]) + ',' + Math.floor(val[1]) + ',' + Math.floor(val[2]) + ')';
3780         }
3781         else {
3782             val = superclass.doMethod.call(this, attr, start, end);
3783         }
3784
3785         return val;
3786     };
3787
3788     proto.setRuntimeAttribute = function(attr) {
3789         superclass.setRuntimeAttribute.call(this, attr);
3790
3791         if (this.patterns.color.test(attr)) {
3792             var attributes = this.attributes;
3793             var start = this.parseColor(this.runtimeAttributes[attr].start);
3794             var end = this.parseColor(this.runtimeAttributes[attr].end);
3795
3796             if (typeof attributes[attr]['to'] === 'undefined' && typeof attributes[attr]['by'] !== 'undefined') {
3797                 end = this.parseColor(attributes[attr].by);
3798
3799                 for (var i = 0, len = start.length; i < len; ++i) {
3800                     end[i] = start[i] + end[i];
3801                 }
3802             }
3803
3804             this.runtimeAttributes[attr].start = start;
3805             this.runtimeAttributes[attr].end = end;
3806         }
3807     };
3808 })();
3809
3810 /*
3811  * Portions of this file are based on pieces of Yahoo User Interface Library
3812  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3813  * YUI licensed under the BSD License:
3814  * http://developer.yahoo.net/yui/license.txt
3815  * <script type="text/javascript">
3816  *
3817  */
3818 Roo.lib.Easing = {
3819
3820
3821     easeNone: function (t, b, c, d) {
3822         return c * t / d + b;
3823     },
3824
3825
3826     easeIn: function (t, b, c, d) {
3827         return c * (t /= d) * t + b;
3828     },
3829
3830
3831     easeOut: function (t, b, c, d) {
3832         return -c * (t /= d) * (t - 2) + b;
3833     },
3834
3835
3836     easeBoth: function (t, b, c, d) {
3837         if ((t /= d / 2) < 1) {
3838             return c / 2 * t * t + b;
3839         }
3840
3841         return -c / 2 * ((--t) * (t - 2) - 1) + b;
3842     },
3843
3844
3845     easeInStrong: function (t, b, c, d) {
3846         return c * (t /= d) * t * t * t + b;
3847     },
3848
3849
3850     easeOutStrong: function (t, b, c, d) {
3851         return -c * ((t = t / d - 1) * t * t * t - 1) + b;
3852     },
3853
3854
3855     easeBothStrong: function (t, b, c, d) {
3856         if ((t /= d / 2) < 1) {
3857             return c / 2 * t * t * t * t + b;
3858         }
3859
3860         return -c / 2 * ((t -= 2) * t * t * t - 2) + b;
3861     },
3862
3863
3864
3865     elasticIn: function (t, b, c, d, a, p) {
3866         if (t == 0) {
3867             return b;
3868         }
3869         if ((t /= d) == 1) {
3870             return b + c;
3871         }
3872         if (!p) {
3873             p = d * .3;
3874         }
3875
3876         if (!a || a < Math.abs(c)) {
3877             a = c;
3878             var s = p / 4;
3879         }
3880         else {
3881             var s = p / (2 * Math.PI) * Math.asin(c / a);
3882         }
3883
3884         return -(a * Math.pow(2, 10 * (t -= 1)) * Math.sin((t * d - s) * (2 * Math.PI) / p)) + b;
3885     },
3886
3887
3888     elasticOut: function (t, b, c, d, a, p) {
3889         if (t == 0) {
3890             return b;
3891         }
3892         if ((t /= d) == 1) {
3893             return b + c;
3894         }
3895         if (!p) {
3896             p = d * .3;
3897         }
3898
3899         if (!a || a < Math.abs(c)) {
3900             a = c;
3901             var s = p / 4;
3902         }
3903         else {
3904             var s = p / (2 * Math.PI) * Math.asin(c / a);
3905         }
3906
3907         return a * Math.pow(2, -10 * t) * Math.sin((t * d - s) * (2 * Math.PI) / p) + c + b;
3908     },
3909
3910
3911     elasticBoth: function (t, b, c, d, a, p) {
3912         if (t == 0) {
3913             return b;
3914         }
3915
3916         if ((t /= d / 2) == 2) {
3917             return b + c;
3918         }
3919
3920         if (!p) {
3921             p = d * (.3 * 1.5);
3922         }
3923
3924         if (!a || a < Math.abs(c)) {
3925             a = c;
3926             var s = p / 4;
3927         }
3928         else {
3929             var s = p / (2 * Math.PI) * Math.asin(c / a);
3930         }
3931
3932         if (t < 1) {
3933             return -.5 * (a * Math.pow(2, 10 * (t -= 1)) *
3934                           Math.sin((t * d - s) * (2 * Math.PI) / p)) + b;
3935         }
3936         return a * Math.pow(2, -10 * (t -= 1)) *
3937                Math.sin((t * d - s) * (2 * Math.PI) / p) * .5 + c + b;
3938     },
3939
3940
3941
3942     backIn: function (t, b, c, d, s) {
3943         if (typeof s == 'undefined') {
3944             s = 1.70158;
3945         }
3946         return c * (t /= d) * t * ((s + 1) * t - s) + b;
3947     },
3948
3949
3950     backOut: function (t, b, c, d, s) {
3951         if (typeof s == 'undefined') {
3952             s = 1.70158;
3953         }
3954         return c * ((t = t / d - 1) * t * ((s + 1) * t + s) + 1) + b;
3955     },
3956
3957
3958     backBoth: function (t, b, c, d, s) {
3959         if (typeof s == 'undefined') {
3960             s = 1.70158;
3961         }
3962
3963         if ((t /= d / 2 ) < 1) {
3964             return c / 2 * (t * t * (((s *= (1.525)) + 1) * t - s)) + b;
3965         }
3966         return c / 2 * ((t -= 2) * t * (((s *= (1.525)) + 1) * t + s) + 2) + b;
3967     },
3968
3969
3970     bounceIn: function (t, b, c, d) {
3971         return c - Roo.lib.Easing.bounceOut(d - t, 0, c, d) + b;
3972     },
3973
3974
3975     bounceOut: function (t, b, c, d) {
3976         if ((t /= d) < (1 / 2.75)) {
3977             return c * (7.5625 * t * t) + b;
3978         } else if (t < (2 / 2.75)) {
3979             return c * (7.5625 * (t -= (1.5 / 2.75)) * t + .75) + b;
3980         } else if (t < (2.5 / 2.75)) {
3981             return c * (7.5625 * (t -= (2.25 / 2.75)) * t + .9375) + b;
3982         }
3983         return c * (7.5625 * (t -= (2.625 / 2.75)) * t + .984375) + b;
3984     },
3985
3986
3987     bounceBoth: function (t, b, c, d) {
3988         if (t < d / 2) {
3989             return Roo.lib.Easing.bounceIn(t * 2, 0, c, d) * .5 + b;
3990         }
3991         return Roo.lib.Easing.bounceOut(t * 2 - d, 0, c, d) * .5 + c * .5 + b;
3992     }
3993 };/*
3994  * Portions of this file are based on pieces of Yahoo User Interface Library
3995  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3996  * YUI licensed under the BSD License:
3997  * http://developer.yahoo.net/yui/license.txt
3998  * <script type="text/javascript">
3999  *
4000  */
4001     (function() {
4002         Roo.lib.Motion = function(el, attributes, duration, method) {
4003             if (el) {
4004                 Roo.lib.Motion.superclass.constructor.call(this, el, attributes, duration, method);
4005             }
4006         };
4007
4008         Roo.extend(Roo.lib.Motion, Roo.lib.ColorAnim);
4009
4010
4011         var Y = Roo.lib;
4012         var superclass = Y.Motion.superclass;
4013         var proto = Y.Motion.prototype;
4014
4015         proto.toString = function() {
4016             var el = this.getEl();
4017             var id = el.id || el.tagName;
4018             return ("Motion " + id);
4019         };
4020
4021         proto.patterns.points = /^points$/i;
4022
4023         proto.setAttribute = function(attr, val, unit) {
4024             if (this.patterns.points.test(attr)) {
4025                 unit = unit || 'px';
4026                 superclass.setAttribute.call(this, 'left', val[0], unit);
4027                 superclass.setAttribute.call(this, 'top', val[1], unit);
4028             } else {
4029                 superclass.setAttribute.call(this, attr, val, unit);
4030             }
4031         };
4032
4033         proto.getAttribute = function(attr) {
4034             if (this.patterns.points.test(attr)) {
4035                 var val = [
4036                         superclass.getAttribute.call(this, 'left'),
4037                         superclass.getAttribute.call(this, 'top')
4038                         ];
4039             } else {
4040                 val = superclass.getAttribute.call(this, attr);
4041             }
4042
4043             return val;
4044         };
4045
4046         proto.doMethod = function(attr, start, end) {
4047             var val = null;
4048
4049             if (this.patterns.points.test(attr)) {
4050                 var t = this.method(this.currentFrame, 0, 100, this.totalFrames) / 100;
4051                 val = Y.Bezier.getPosition(this.runtimeAttributes[attr], t);
4052             } else {
4053                 val = superclass.doMethod.call(this, attr, start, end);
4054             }
4055             return val;
4056         };
4057
4058         proto.setRuntimeAttribute = function(attr) {
4059             if (this.patterns.points.test(attr)) {
4060                 var el = this.getEl();
4061                 var attributes = this.attributes;
4062                 var start;
4063                 var control = attributes['points']['control'] || [];
4064                 var end;
4065                 var i, len;
4066
4067                 if (control.length > 0 && !(control[0] instanceof Array)) {
4068                     control = [control];
4069                 } else {
4070                     var tmp = [];
4071                     for (i = 0,len = control.length; i < len; ++i) {
4072                         tmp[i] = control[i];
4073                     }
4074                     control = tmp;
4075                 }
4076
4077                 Roo.fly(el).position();
4078
4079                 if (isset(attributes['points']['from'])) {
4080                     Roo.lib.Dom.setXY(el, attributes['points']['from']);
4081                 }
4082                 else {
4083                     Roo.lib.Dom.setXY(el, Roo.lib.Dom.getXY(el));
4084                 }
4085
4086                 start = this.getAttribute('points');
4087
4088
4089                 if (isset(attributes['points']['to'])) {
4090                     end = translateValues.call(this, attributes['points']['to'], start);
4091
4092                     var pageXY = Roo.lib.Dom.getXY(this.getEl());
4093                     for (i = 0,len = control.length; i < len; ++i) {
4094                         control[i] = translateValues.call(this, control[i], start);
4095                     }
4096
4097
4098                 } else if (isset(attributes['points']['by'])) {
4099                     end = [ start[0] + attributes['points']['by'][0], start[1] + attributes['points']['by'][1] ];
4100
4101                     for (i = 0,len = control.length; i < len; ++i) {
4102                         control[i] = [ start[0] + control[i][0], start[1] + control[i][1] ];
4103                     }
4104                 }
4105
4106                 this.runtimeAttributes[attr] = [start];
4107
4108                 if (control.length > 0) {
4109                     this.runtimeAttributes[attr] = this.runtimeAttributes[attr].concat(control);
4110                 }
4111
4112                 this.runtimeAttributes[attr][this.runtimeAttributes[attr].length] = end;
4113             }
4114             else {
4115                 superclass.setRuntimeAttribute.call(this, attr);
4116             }
4117         };
4118
4119         var translateValues = function(val, start) {
4120             var pageXY = Roo.lib.Dom.getXY(this.getEl());
4121             val = [ val[0] - pageXY[0] + start[0], val[1] - pageXY[1] + start[1] ];
4122
4123             return val;
4124         };
4125
4126         var isset = function(prop) {
4127             return (typeof prop !== 'undefined');
4128         };
4129     })();
4130 /*
4131  * Portions of this file are based on pieces of Yahoo User Interface Library
4132  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
4133  * YUI licensed under the BSD License:
4134  * http://developer.yahoo.net/yui/license.txt
4135  * <script type="text/javascript">
4136  *
4137  */
4138     (function() {
4139         Roo.lib.Scroll = function(el, attributes, duration, method) {
4140             if (el) {
4141                 Roo.lib.Scroll.superclass.constructor.call(this, el, attributes, duration, method);
4142             }
4143         };
4144
4145         Roo.extend(Roo.lib.Scroll, Roo.lib.ColorAnim);
4146
4147
4148         var Y = Roo.lib;
4149         var superclass = Y.Scroll.superclass;
4150         var proto = Y.Scroll.prototype;
4151
4152         proto.toString = function() {
4153             var el = this.getEl();
4154             var id = el.id || el.tagName;
4155             return ("Scroll " + id);
4156         };
4157
4158         proto.doMethod = function(attr, start, end) {
4159             var val = null;
4160
4161             if (attr == 'scroll') {
4162                 val = [
4163                         this.method(this.currentFrame, start[0], end[0] - start[0], this.totalFrames),
4164                         this.method(this.currentFrame, start[1], end[1] - start[1], this.totalFrames)
4165                         ];
4166
4167             } else {
4168                 val = superclass.doMethod.call(this, attr, start, end);
4169             }
4170             return val;
4171         };
4172
4173         proto.getAttribute = function(attr) {
4174             var val = null;
4175             var el = this.getEl();
4176
4177             if (attr == 'scroll') {
4178                 val = [ el.scrollLeft, el.scrollTop ];
4179             } else {
4180                 val = superclass.getAttribute.call(this, attr);
4181             }
4182
4183             return val;
4184         };
4185
4186         proto.setAttribute = function(attr, val, unit) {
4187             var el = this.getEl();
4188
4189             if (attr == 'scroll') {
4190                 el.scrollLeft = val[0];
4191                 el.scrollTop = val[1];
4192             } else {
4193                 superclass.setAttribute.call(this, attr, val, unit);
4194             }
4195         };
4196     })();
4197 /*
4198  * Based on:
4199  * Ext JS Library 1.1.1
4200  * Copyright(c) 2006-2007, Ext JS, LLC.
4201  *
4202  * Originally Released Under LGPL - original licence link has changed is not relivant.
4203  *
4204  * Fork - LGPL
4205  * <script type="text/javascript">
4206  */
4207
4208
4209 // nasty IE9 hack - what a pile of crap that is..
4210
4211  if (typeof Range != "undefined" && typeof Range.prototype.createContextualFragment == "undefined") {
4212     Range.prototype.createContextualFragment = function (html) {
4213         var doc = window.document;
4214         var container = doc.createElement("div");
4215         container.innerHTML = html;
4216         var frag = doc.createDocumentFragment(), n;
4217         while ((n = container.firstChild)) {
4218             frag.appendChild(n);
4219         }
4220         return frag;
4221     };
4222 }
4223
4224 /**
4225  * @class Roo.DomHelper
4226  * Utility class for working with DOM and/or Templates. It transparently supports using HTML fragments or DOM.
4227  * 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>.
4228  * @singleton
4229  */
4230 Roo.DomHelper = function(){
4231     var tempTableEl = null;
4232     var emptyTags = /^(?:br|frame|hr|img|input|link|meta|range|spacer|wbr|area|param|col)$/i;
4233     var tableRe = /^table|tbody|tr|td$/i;
4234     var xmlns = {};
4235     // build as innerHTML where available
4236     /** @ignore */
4237     var createHtml = function(o){
4238         if(typeof o == 'string'){
4239             return o;
4240         }
4241         var b = "";
4242         if(!o.tag){
4243             o.tag = "div";
4244         }
4245         b += "<" + o.tag;
4246         for(var attr in o){
4247             if(attr == "tag" || attr == "children" || attr == "cn" || attr == "html" || typeof o[attr] == "function") { continue; }
4248             if(attr == "style"){
4249                 var s = o["style"];
4250                 if(typeof s == "function"){
4251                     s = s.call();
4252                 }
4253                 if(typeof s == "string"){
4254                     b += ' style="' + s + '"';
4255                 }else if(typeof s == "object"){
4256                     b += ' style="';
4257                     for(var key in s){
4258                         if(typeof s[key] != "function"){
4259                             b += key + ":" + s[key] + ";";
4260                         }
4261                     }
4262                     b += '"';
4263                 }
4264             }else{
4265                 if(attr == "cls"){
4266                     b += ' class="' + o["cls"] + '"';
4267                 }else if(attr == "htmlFor"){
4268                     b += ' for="' + o["htmlFor"] + '"';
4269                 }else{
4270                     b += " " + attr + '="' + o[attr] + '"';
4271                 }
4272             }
4273         }
4274         if(emptyTags.test(o.tag)){
4275             b += "/>";
4276         }else{
4277             b += ">";
4278             var cn = o.children || o.cn;
4279             if(cn){
4280                 //http://bugs.kde.org/show_bug.cgi?id=71506
4281                 if((cn instanceof Array) || (Roo.isSafari && typeof(cn.join) == "function")){
4282                     for(var i = 0, len = cn.length; i < len; i++) {
4283                         b += createHtml(cn[i], b);
4284                     }
4285                 }else{
4286                     b += createHtml(cn, b);
4287                 }
4288             }
4289             if(o.html){
4290                 b += o.html;
4291             }
4292             b += "</" + o.tag + ">";
4293         }
4294         return b;
4295     };
4296
4297     // build as dom
4298     /** @ignore */
4299     var createDom = function(o, parentNode){
4300          
4301         // defininition craeted..
4302         var ns = false;
4303         if (o.ns && o.ns != 'html') {
4304                
4305             if (o.xmlns && typeof(xmlns[o.ns]) == 'undefined') {
4306                 xmlns[o.ns] = o.xmlns;
4307                 ns = o.xmlns;
4308             }
4309             if (typeof(xmlns[o.ns]) == 'undefined') {
4310                 console.log("Trying to create namespace element " + o.ns + ", however no xmlns was sent to builder previously");
4311             }
4312             ns = xmlns[o.ns];
4313         }
4314         
4315         
4316         if (typeof(o) == 'string') {
4317             return parentNode.appendChild(document.createTextNode(o));
4318         }
4319         o.tag = o.tag || div;
4320         if (o.ns && Roo.isIE) {
4321             ns = false;
4322             o.tag = o.ns + ':' + o.tag;
4323             
4324         }
4325         var el = ns ? document.createElementNS( ns, o.tag||'div') :  document.createElement(o.tag||'div');
4326         var useSet = el.setAttribute ? true : false; // In IE some elements don't have setAttribute
4327         for(var attr in o){
4328             
4329             if(attr == "tag" || attr == "ns" ||attr == "xmlns" ||attr == "children" || attr == "cn" || attr == "html" || 
4330                     attr == "style" || typeof o[attr] == "function") { continue; }
4331                     
4332             if(attr=="cls" && Roo.isIE){
4333                 el.className = o["cls"];
4334             }else{
4335                 if(useSet) { el.setAttribute(attr=="cls" ? 'class' : attr, o[attr]);}
4336                 else { 
4337                     el[attr] = o[attr];
4338                 }
4339             }
4340         }
4341         Roo.DomHelper.applyStyles(el, o.style);
4342         var cn = o.children || o.cn;
4343         if(cn){
4344             //http://bugs.kde.org/show_bug.cgi?id=71506
4345              if((cn instanceof Array) || (Roo.isSafari && typeof(cn.join) == "function")){
4346                 for(var i = 0, len = cn.length; i < len; i++) {
4347                     createDom(cn[i], el);
4348                 }
4349             }else{
4350                 createDom(cn, el);
4351             }
4352         }
4353         if(o.html){
4354             el.innerHTML = o.html;
4355         }
4356         if(parentNode){
4357            parentNode.appendChild(el);
4358         }
4359         return el;
4360     };
4361
4362     var ieTable = function(depth, s, h, e){
4363         tempTableEl.innerHTML = [s, h, e].join('');
4364         var i = -1, el = tempTableEl;
4365         while(++i < depth && el.firstChild){
4366             el = el.firstChild;
4367         }
4368         return el;
4369     };
4370
4371     // kill repeat to save bytes
4372     var ts = '<table>',
4373         te = '</table>',
4374         tbs = ts+'<tbody>',
4375         tbe = '</tbody>'+te,
4376         trs = tbs + '<tr>',
4377         tre = '</tr>'+tbe;
4378
4379     /**
4380      * @ignore
4381      * Nasty code for IE's broken table implementation
4382      */
4383     var insertIntoTable = function(tag, where, el, html){
4384         if(!tempTableEl){
4385             tempTableEl = document.createElement('div');
4386         }
4387         var node;
4388         var before = null;
4389         if(tag == 'td'){
4390             if(where == 'afterbegin' || where == 'beforeend'){ // INTO a TD
4391                 return;
4392             }
4393             if(where == 'beforebegin'){
4394                 before = el;
4395                 el = el.parentNode;
4396             } else{
4397                 before = el.nextSibling;
4398                 el = el.parentNode;
4399             }
4400             node = ieTable(4, trs, html, tre);
4401         }
4402         else if(tag == 'tr'){
4403             if(where == 'beforebegin'){
4404                 before = el;
4405                 el = el.parentNode;
4406                 node = ieTable(3, tbs, html, tbe);
4407             } else if(where == 'afterend'){
4408                 before = el.nextSibling;
4409                 el = el.parentNode;
4410                 node = ieTable(3, tbs, html, tbe);
4411             } else{ // INTO a TR
4412                 if(where == 'afterbegin'){
4413                     before = el.firstChild;
4414                 }
4415                 node = ieTable(4, trs, html, tre);
4416             }
4417         } else if(tag == 'tbody'){
4418             if(where == 'beforebegin'){
4419                 before = el;
4420                 el = el.parentNode;
4421                 node = ieTable(2, ts, html, te);
4422             } else if(where == 'afterend'){
4423                 before = el.nextSibling;
4424                 el = el.parentNode;
4425                 node = ieTable(2, ts, html, te);
4426             } else{
4427                 if(where == 'afterbegin'){
4428                     before = el.firstChild;
4429                 }
4430                 node = ieTable(3, tbs, html, tbe);
4431             }
4432         } else{ // TABLE
4433             if(where == 'beforebegin' || where == 'afterend'){ // OUTSIDE the table
4434                 return;
4435             }
4436             if(where == 'afterbegin'){
4437                 before = el.firstChild;
4438             }
4439             node = ieTable(2, ts, html, te);
4440         }
4441         el.insertBefore(node, before);
4442         return node;
4443     };
4444
4445     return {
4446     /** True to force the use of DOM instead of html fragments @type Boolean */
4447     useDom : false,
4448
4449     /**
4450      * Returns the markup for the passed Element(s) config
4451      * @param {Object} o The Dom object spec (and children)
4452      * @return {String}
4453      */
4454     markup : function(o){
4455         return createHtml(o);
4456     },
4457
4458     /**
4459      * Applies a style specification to an element
4460      * @param {String/HTMLElement} el The element to apply styles to
4461      * @param {String/Object/Function} styles A style specification string eg "width:100px", or object in the form {width:"100px"}, or
4462      * a function which returns such a specification.
4463      */
4464     applyStyles : function(el, styles){
4465         if(styles){
4466            el = Roo.fly(el);
4467            if(typeof styles == "string"){
4468                var re = /\s?([a-z\-]*)\:\s?([^;]*);?/gi;
4469                var matches;
4470                while ((matches = re.exec(styles)) != null){
4471                    el.setStyle(matches[1], matches[2]);
4472                }
4473            }else if (typeof styles == "object"){
4474                for (var style in styles){
4475                   el.setStyle(style, styles[style]);
4476                }
4477            }else if (typeof styles == "function"){
4478                 Roo.DomHelper.applyStyles(el, styles.call());
4479            }
4480         }
4481     },
4482
4483     /**
4484      * Inserts an HTML fragment into the Dom
4485      * @param {String} where Where to insert the html in relation to el - beforeBegin, afterBegin, beforeEnd, afterEnd.
4486      * @param {HTMLElement} el The context element
4487      * @param {String} html The HTML fragmenet
4488      * @return {HTMLElement} The new node
4489      */
4490     insertHtml : function(where, el, html){
4491         where = where.toLowerCase();
4492         if(el.insertAdjacentHTML){
4493             if(tableRe.test(el.tagName)){
4494                 var rs;
4495                 if(rs = insertIntoTable(el.tagName.toLowerCase(), where, el, html)){
4496                     return rs;
4497                 }
4498             }
4499             switch(where){
4500                 case "beforebegin":
4501                     el.insertAdjacentHTML('BeforeBegin', html);
4502                     return el.previousSibling;
4503                 case "afterbegin":
4504                     el.insertAdjacentHTML('AfterBegin', html);
4505                     return el.firstChild;
4506                 case "beforeend":
4507                     el.insertAdjacentHTML('BeforeEnd', html);
4508                     return el.lastChild;
4509                 case "afterend":
4510                     el.insertAdjacentHTML('AfterEnd', html);
4511                     return el.nextSibling;
4512             }
4513             throw 'Illegal insertion point -> "' + where + '"';
4514         }
4515         var range = el.ownerDocument.createRange();
4516         var frag;
4517         switch(where){
4518              case "beforebegin":
4519                 range.setStartBefore(el);
4520                 frag = range.createContextualFragment(html);
4521                 el.parentNode.insertBefore(frag, el);
4522                 return el.previousSibling;
4523              case "afterbegin":
4524                 if(el.firstChild){
4525                     range.setStartBefore(el.firstChild);
4526                     frag = range.createContextualFragment(html);
4527                     el.insertBefore(frag, el.firstChild);
4528                     return el.firstChild;
4529                 }else{
4530                     el.innerHTML = html;
4531                     return el.firstChild;
4532                 }
4533             case "beforeend":
4534                 if(el.lastChild){
4535                     range.setStartAfter(el.lastChild);
4536                     frag = range.createContextualFragment(html);
4537                     el.appendChild(frag);
4538                     return el.lastChild;
4539                 }else{
4540                     el.innerHTML = html;
4541                     return el.lastChild;
4542                 }
4543             case "afterend":
4544                 range.setStartAfter(el);
4545                 frag = range.createContextualFragment(html);
4546                 el.parentNode.insertBefore(frag, el.nextSibling);
4547                 return el.nextSibling;
4548             }
4549             throw 'Illegal insertion point -> "' + where + '"';
4550     },
4551
4552     /**
4553      * Creates new Dom element(s) and inserts them before el
4554      * @param {String/HTMLElement/Element} el The context element
4555      * @param {Object/String} o The Dom object spec (and children) or raw HTML blob
4556      * @param {Boolean} returnElement (optional) true to return a Roo.Element
4557      * @return {HTMLElement/Roo.Element} The new node
4558      */
4559     insertBefore : function(el, o, returnElement){
4560         return this.doInsert(el, o, returnElement, "beforeBegin");
4561     },
4562
4563     /**
4564      * Creates new Dom element(s) and inserts them after el
4565      * @param {String/HTMLElement/Element} el The context element
4566      * @param {Object} o The Dom object spec (and children)
4567      * @param {Boolean} returnElement (optional) true to return a Roo.Element
4568      * @return {HTMLElement/Roo.Element} The new node
4569      */
4570     insertAfter : function(el, o, returnElement){
4571         return this.doInsert(el, o, returnElement, "afterEnd", "nextSibling");
4572     },
4573
4574     /**
4575      * Creates new Dom element(s) and inserts them as the first child of el
4576      * @param {String/HTMLElement/Element} el The context element
4577      * @param {Object/String} o The Dom object spec (and children) or raw HTML blob
4578      * @param {Boolean} returnElement (optional) true to return a Roo.Element
4579      * @return {HTMLElement/Roo.Element} The new node
4580      */
4581     insertFirst : function(el, o, returnElement){
4582         return this.doInsert(el, o, returnElement, "afterBegin");
4583     },
4584
4585     // private
4586     doInsert : function(el, o, returnElement, pos, sibling){
4587         el = Roo.getDom(el);
4588         var newNode;
4589         if(this.useDom || o.ns){
4590             newNode = createDom(o, null);
4591             el.parentNode.insertBefore(newNode, sibling ? el[sibling] : el);
4592         }else{
4593             var html = createHtml(o);
4594             newNode = this.insertHtml(pos, el, html);
4595         }
4596         return returnElement ? Roo.get(newNode, true) : newNode;
4597     },
4598
4599     /**
4600      * Creates new Dom element(s) and appends them to el
4601      * @param {String/HTMLElement/Element} el The context element
4602      * @param {Object/String} o The Dom object spec (and children) or raw HTML blob
4603      * @param {Boolean} returnElement (optional) true to return a Roo.Element
4604      * @return {HTMLElement/Roo.Element} The new node
4605      */
4606     append : function(el, o, returnElement){
4607         el = Roo.getDom(el);
4608         var newNode;
4609         if(this.useDom || o.ns){
4610             newNode = createDom(o, null);
4611             el.appendChild(newNode);
4612         }else{
4613             var html = createHtml(o);
4614             newNode = this.insertHtml("beforeEnd", el, html);
4615         }
4616         return returnElement ? Roo.get(newNode, true) : newNode;
4617     },
4618
4619     /**
4620      * Creates new Dom element(s) and overwrites the contents of el with them
4621      * @param {String/HTMLElement/Element} el The context element
4622      * @param {Object/String} o The Dom object spec (and children) or raw HTML blob
4623      * @param {Boolean} returnElement (optional) true to return a Roo.Element
4624      * @return {HTMLElement/Roo.Element} The new node
4625      */
4626     overwrite : function(el, o, returnElement){
4627         el = Roo.getDom(el);
4628         if (o.ns) {
4629           
4630             while (el.childNodes.length) {
4631                 el.removeChild(el.firstChild);
4632             }
4633             createDom(o, el);
4634         } else {
4635             el.innerHTML = createHtml(o);   
4636         }
4637         
4638         return returnElement ? Roo.get(el.firstChild, true) : el.firstChild;
4639     },
4640
4641     /**
4642      * Creates a new Roo.DomHelper.Template from the Dom object spec
4643      * @param {Object} o The Dom object spec (and children)
4644      * @return {Roo.DomHelper.Template} The new template
4645      */
4646     createTemplate : function(o){
4647         var html = createHtml(o);
4648         return new Roo.Template(html);
4649     }
4650     };
4651 }();
4652 /*
4653  * Based on:
4654  * Ext JS Library 1.1.1
4655  * Copyright(c) 2006-2007, Ext JS, LLC.
4656  *
4657  * Originally Released Under LGPL - original licence link has changed is not relivant.
4658  *
4659  * Fork - LGPL
4660  * <script type="text/javascript">
4661  */
4662  
4663 /**
4664 * @class Roo.Template
4665 * Represents an HTML fragment template. Templates can be precompiled for greater performance.
4666 * For a list of available format functions, see {@link Roo.util.Format}.<br />
4667 * Usage:
4668 <pre><code>
4669 var t = new Roo.Template({
4670     html :  '&lt;div name="{id}"&gt;' + 
4671         '&lt;span class="{cls}"&gt;{name:trim} {someval:this.myformat}{value:ellipsis(10)}&lt;/span&gt;' +
4672         '&lt;/div&gt;',
4673     myformat: function (value, allValues) {
4674         return 'XX' + value;
4675     }
4676 });
4677 t.append('some-element', {id: 'myid', cls: 'myclass', name: 'foo', value: 'bar'});
4678 </code></pre>
4679 * For more information see this blog post with examples:
4680 *  <a href="http://www.cnitblog.com/seeyeah/archive/2011/12/30/38728.html/">DomHelper
4681      - Create Elements using DOM, HTML fragments and Templates</a>. 
4682 * @constructor
4683 * @param {Object} cfg - Configuration object.
4684 */
4685 Roo.Template = function(cfg){
4686     // BC!
4687     if(cfg instanceof Array){
4688         cfg = cfg.join("");
4689     }else if(arguments.length > 1){
4690         cfg = Array.prototype.join.call(arguments, "");
4691     }
4692     
4693     
4694     if (typeof(cfg) == 'object') {
4695         Roo.apply(this,cfg)
4696     } else {
4697         // bc
4698         this.html = cfg;
4699     }
4700     if (this.url) {
4701         this.load();
4702     }
4703     
4704 };
4705 Roo.Template.prototype = {
4706     
4707     /**
4708      * @cfg {Function} onLoad Called after the template has been loaded and complied (usually from a remove source)
4709      */
4710     onLoad : false,
4711     
4712     
4713     /**
4714      * @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..
4715      *                    it should be fixed so that template is observable...
4716      */
4717     url : false,
4718     /**
4719      * @cfg {String} html  The HTML fragment or an array of fragments to join("") or multiple arguments to join("")
4720      */
4721     html : '',
4722     
4723     
4724     compiled : false,
4725     loaded : false,
4726     /**
4727      * Returns an HTML fragment of this template with the specified values applied.
4728      * @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'})
4729      * @return {String} The HTML fragment
4730      */
4731     
4732    
4733     
4734     applyTemplate : function(values){
4735         //Roo.log(["applyTemplate", values]);
4736         try {
4737            
4738             if(this.compiled){
4739                 return this.compiled(values);
4740             }
4741             var useF = this.disableFormats !== true;
4742             var fm = Roo.util.Format, tpl = this;
4743             var fn = function(m, name, format, args){
4744                 if(format && useF){
4745                     if(format.substr(0, 5) == "this."){
4746                         return tpl.call(format.substr(5), values[name], values);
4747                     }else{
4748                         if(args){
4749                             // quoted values are required for strings in compiled templates, 
4750                             // but for non compiled we need to strip them
4751                             // quoted reversed for jsmin
4752                             var re = /^\s*['"](.*)["']\s*$/;
4753                             args = args.split(',');
4754                             for(var i = 0, len = args.length; i < len; i++){
4755                                 args[i] = args[i].replace(re, "$1");
4756                             }
4757                             args = [values[name]].concat(args);
4758                         }else{
4759                             args = [values[name]];
4760                         }
4761                         return fm[format].apply(fm, args);
4762                     }
4763                 }else{
4764                     return values[name] !== undefined ? values[name] : "";
4765                 }
4766             };
4767             return this.html.replace(this.re, fn);
4768         } catch (e) {
4769             Roo.log(e);
4770             throw e;
4771         }
4772          
4773     },
4774     
4775     loading : false,
4776       
4777     load : function ()
4778     {
4779          
4780         if (this.loading) {
4781             return;
4782         }
4783         var _t = this;
4784         
4785         this.loading = true;
4786         this.compiled = false;
4787         
4788         var cx = new Roo.data.Connection();
4789         cx.request({
4790             url : this.url,
4791             method : 'GET',
4792             success : function (response) {
4793                 _t.loading = false;
4794                 _t.url = false;
4795                 
4796                 _t.set(response.responseText,true);
4797                 _t.loaded = true;
4798                 if (_t.onLoad) {
4799                     _t.onLoad();
4800                 }
4801              },
4802             failure : function(response) {
4803                 Roo.log("Template failed to load from " + _t.url);
4804                 _t.loading = false;
4805             }
4806         });
4807     },
4808
4809     /**
4810      * Sets the HTML used as the template and optionally compiles it.
4811      * @param {String} html
4812      * @param {Boolean} compile (optional) True to compile the template (defaults to undefined)
4813      * @return {Roo.Template} this
4814      */
4815     set : function(html, compile){
4816         this.html = html;
4817         this.compiled = false;
4818         if(compile){
4819             this.compile();
4820         }
4821         return this;
4822     },
4823     
4824     /**
4825      * True to disable format functions (defaults to false)
4826      * @type Boolean
4827      */
4828     disableFormats : false,
4829     
4830     /**
4831     * The regular expression used to match template variables 
4832     * @type RegExp
4833     * @property 
4834     */
4835     re : /\{([\w-]+)(?:\:([\w\.]*)(?:\((.*?)?\))?)?\}/g,
4836     
4837     /**
4838      * Compiles the template into an internal function, eliminating the RegEx overhead.
4839      * @return {Roo.Template} this
4840      */
4841     compile : function(){
4842         var fm = Roo.util.Format;
4843         var useF = this.disableFormats !== true;
4844         var sep = Roo.isGecko ? "+" : ",";
4845         var fn = function(m, name, format, args){
4846             if(format && useF){
4847                 args = args ? ',' + args : "";
4848                 if(format.substr(0, 5) != "this."){
4849                     format = "fm." + format + '(';
4850                 }else{
4851                     format = 'this.call("'+ format.substr(5) + '", ';
4852                     args = ", values";
4853                 }
4854             }else{
4855                 args= ''; format = "(values['" + name + "'] == undefined ? '' : ";
4856             }
4857             return "'"+ sep + format + "values['" + name + "']" + args + ")"+sep+"'";
4858         };
4859         var body;
4860         // branched to use + in gecko and [].join() in others
4861         if(Roo.isGecko){
4862             body = "this.compiled = function(values){ return '" +
4863                    this.html.replace(/\\/g, '\\\\').replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn) +
4864                     "';};";
4865         }else{
4866             body = ["this.compiled = function(values){ return ['"];
4867             body.push(this.html.replace(/\\/g, '\\\\').replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn));
4868             body.push("'].join('');};");
4869             body = body.join('');
4870         }
4871         /**
4872          * eval:var:values
4873          * eval:var:fm
4874          */
4875         eval(body);
4876         return this;
4877     },
4878     
4879     // private function used to call members
4880     call : function(fnName, value, allValues){
4881         return this[fnName](value, allValues);
4882     },
4883     
4884     /**
4885      * Applies the supplied values to the template and inserts the new node(s) as the first child of el.
4886      * @param {String/HTMLElement/Roo.Element} el The context element
4887      * @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'})
4888      * @param {Boolean} returnElement (optional) true to return a Roo.Element (defaults to undefined)
4889      * @return {HTMLElement/Roo.Element} The new node or Element
4890      */
4891     insertFirst: function(el, values, returnElement){
4892         return this.doInsert('afterBegin', el, values, returnElement);
4893     },
4894
4895     /**
4896      * Applies the supplied values to the template and inserts the new node(s) before el.
4897      * @param {String/HTMLElement/Roo.Element} el The context element
4898      * @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'})
4899      * @param {Boolean} returnElement (optional) true to return a Roo.Element (defaults to undefined)
4900      * @return {HTMLElement/Roo.Element} The new node or Element
4901      */
4902     insertBefore: function(el, values, returnElement){
4903         return this.doInsert('beforeBegin', el, values, returnElement);
4904     },
4905
4906     /**
4907      * Applies the supplied values to the template and inserts the new node(s) after el.
4908      * @param {String/HTMLElement/Roo.Element} el The context element
4909      * @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'})
4910      * @param {Boolean} returnElement (optional) true to return a Roo.Element (defaults to undefined)
4911      * @return {HTMLElement/Roo.Element} The new node or Element
4912      */
4913     insertAfter : function(el, values, returnElement){
4914         return this.doInsert('afterEnd', el, values, returnElement);
4915     },
4916     
4917     /**
4918      * Applies the supplied values to the template and appends the new node(s) to el.
4919      * @param {String/HTMLElement/Roo.Element} el The context element
4920      * @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'})
4921      * @param {Boolean} returnElement (optional) true to return a Roo.Element (defaults to undefined)
4922      * @return {HTMLElement/Roo.Element} The new node or Element
4923      */
4924     append : function(el, values, returnElement){
4925         return this.doInsert('beforeEnd', el, values, returnElement);
4926     },
4927
4928     doInsert : function(where, el, values, returnEl){
4929         el = Roo.getDom(el);
4930         var newNode = Roo.DomHelper.insertHtml(where, el, this.applyTemplate(values));
4931         return returnEl ? Roo.get(newNode, true) : newNode;
4932     },
4933
4934     /**
4935      * Applies the supplied values to the template and overwrites the content of el with the new node(s).
4936      * @param {String/HTMLElement/Roo.Element} el The context element
4937      * @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'})
4938      * @param {Boolean} returnElement (optional) true to return a Roo.Element (defaults to undefined)
4939      * @return {HTMLElement/Roo.Element} The new node or Element
4940      */
4941     overwrite : function(el, values, returnElement){
4942         el = Roo.getDom(el);
4943         el.innerHTML = this.applyTemplate(values);
4944         return returnElement ? Roo.get(el.firstChild, true) : el.firstChild;
4945     }
4946 };
4947 /**
4948  * Alias for {@link #applyTemplate}
4949  * @method
4950  */
4951 Roo.Template.prototype.apply = Roo.Template.prototype.applyTemplate;
4952
4953 // backwards compat
4954 Roo.DomHelper.Template = Roo.Template;
4955
4956 /**
4957  * Creates a template from the passed element's value (<i>display:none</i> textarea, preferred) or innerHTML.
4958  * @param {String/HTMLElement} el A DOM element or its id
4959  * @returns {Roo.Template} The created template
4960  * @static
4961  */
4962 Roo.Template.from = function(el){
4963     el = Roo.getDom(el);
4964     return new Roo.Template(el.value || el.innerHTML);
4965 };/*
4966  * Based on:
4967  * Ext JS Library 1.1.1
4968  * Copyright(c) 2006-2007, Ext JS, LLC.
4969  *
4970  * Originally Released Under LGPL - original licence link has changed is not relivant.
4971  *
4972  * Fork - LGPL
4973  * <script type="text/javascript">
4974  */
4975  
4976
4977 /*
4978  * This is code is also distributed under MIT license for use
4979  * with jQuery and prototype JavaScript libraries.
4980  */
4981 /**
4982  * @class Roo.DomQuery
4983 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).
4984 <p>
4985 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>
4986
4987 <p>
4988 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.
4989 </p>
4990 <h4>Element Selectors:</h4>
4991 <ul class="list">
4992     <li> <b>*</b> any element</li>
4993     <li> <b>E</b> an element with the tag E</li>
4994     <li> <b>E F</b> All descendent elements of E that have the tag F</li>
4995     <li> <b>E > F</b> or <b>E/F</b> all direct children elements of E that have the tag F</li>
4996     <li> <b>E + F</b> all elements with the tag F that are immediately preceded by an element with the tag E</li>
4997     <li> <b>E ~ F</b> all elements with the tag F that are preceded by a sibling element with the tag E</li>
4998 </ul>
4999 <h4>Attribute Selectors:</h4>
5000 <p>The use of @ and quotes are optional. For example, div[@foo='bar'] is also a valid attribute selector.</p>
5001 <ul class="list">
5002     <li> <b>E[foo]</b> has an attribute "foo"</li>
5003     <li> <b>E[foo=bar]</b> has an attribute "foo" that equals "bar"</li>
5004     <li> <b>E[foo^=bar]</b> has an attribute "foo" that starts with "bar"</li>
5005     <li> <b>E[foo$=bar]</b> has an attribute "foo" that ends with "bar"</li>
5006     <li> <b>E[foo*=bar]</b> has an attribute "foo" that contains the substring "bar"</li>
5007     <li> <b>E[foo%=2]</b> has an attribute "foo" that is evenly divisible by 2</li>
5008     <li> <b>E[foo!=bar]</b> has an attribute "foo" that does not equal "bar"</li>
5009 </ul>
5010 <h4>Pseudo Classes:</h4>
5011 <ul class="list">
5012     <li> <b>E:first-child</b> E is the first child of its parent</li>
5013     <li> <b>E:last-child</b> E is the last child of its parent</li>
5014     <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>
5015     <li> <b>E:nth-child(odd)</b> E is an odd child of its parent</li>
5016     <li> <b>E:nth-child(even)</b> E is an even child of its parent</li>
5017     <li> <b>E:only-child</b> E is the only child of its parent</li>
5018     <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>
5019     <li> <b>E:first</b> the first E in the resultset</li>
5020     <li> <b>E:last</b> the last E in the resultset</li>
5021     <li> <b>E:nth(<i>n</i>)</b> the <i>n</i>th E in the resultset (1 based)</li>
5022     <li> <b>E:odd</b> shortcut for :nth-child(odd)</li>
5023     <li> <b>E:even</b> shortcut for :nth-child(even)</li>
5024     <li> <b>E:contains(foo)</b> E's innerHTML contains the substring "foo"</li>
5025     <li> <b>E:nodeValue(foo)</b> E contains a textNode with a nodeValue that equals "foo"</li>
5026     <li> <b>E:not(S)</b> an E element that does not match simple selector S</li>
5027     <li> <b>E:has(S)</b> an E element that has a descendent that matches simple selector S</li>
5028     <li> <b>E:next(S)</b> an E element whose next sibling matches simple selector S</li>
5029     <li> <b>E:prev(S)</b> an E element whose previous sibling matches simple selector S</li>
5030 </ul>
5031 <h4>CSS Value Selectors:</h4>
5032 <ul class="list">
5033     <li> <b>E{display=none}</b> css value "display" that equals "none"</li>
5034     <li> <b>E{display^=none}</b> css value "display" that starts with "none"</li>
5035     <li> <b>E{display$=none}</b> css value "display" that ends with "none"</li>
5036     <li> <b>E{display*=none}</b> css value "display" that contains the substring "none"</li>
5037     <li> <b>E{display%=2}</b> css value "display" that is evenly divisible by 2</li>
5038     <li> <b>E{display!=none}</b> css value "display" that does not equal "none"</li>
5039 </ul>
5040  * @singleton
5041  */
5042 Roo.DomQuery = function(){
5043     var cache = {}, simpleCache = {}, valueCache = {};
5044     var nonSpace = /\S/;
5045     var trimRe = /^\s+|\s+$/g;
5046     var tplRe = /\{(\d+)\}/g;
5047     var modeRe = /^(\s?[\/>+~]\s?|\s|$)/;
5048     var tagTokenRe = /^(#)?([\w-\*]+)/;
5049     var nthRe = /(\d*)n\+?(\d*)/, nthRe2 = /\D/;
5050
5051     function child(p, index){
5052         var i = 0;
5053         var n = p.firstChild;
5054         while(n){
5055             if(n.nodeType == 1){
5056                if(++i == index){
5057                    return n;
5058                }
5059             }
5060             n = n.nextSibling;
5061         }
5062         return null;
5063     };
5064
5065     function next(n){
5066         while((n = n.nextSibling) && n.nodeType != 1);
5067         return n;
5068     };
5069
5070     function prev(n){
5071         while((n = n.previousSibling) && n.nodeType != 1);
5072         return n;
5073     };
5074
5075     function children(d){
5076         var n = d.firstChild, ni = -1;
5077             while(n){
5078                 var nx = n.nextSibling;
5079                 if(n.nodeType == 3 && !nonSpace.test(n.nodeValue)){
5080                     d.removeChild(n);
5081                 }else{
5082                     n.nodeIndex = ++ni;
5083                 }
5084                 n = nx;
5085             }
5086             return this;
5087         };
5088
5089     function byClassName(c, a, v){
5090         if(!v){
5091             return c;
5092         }
5093         var r = [], ri = -1, cn;
5094         for(var i = 0, ci; ci = c[i]; i++){
5095             
5096             
5097             if((' '+
5098                 ( (ci instanceof SVGElement) ? ci.className.baseVal : ci.className)
5099                  +' ').indexOf(v) != -1){
5100                 r[++ri] = ci;
5101             }
5102         }
5103         return r;
5104     };
5105
5106     function attrValue(n, attr){
5107         if(!n.tagName && typeof n.length != "undefined"){
5108             n = n[0];
5109         }
5110         if(!n){
5111             return null;
5112         }
5113         if(attr == "for"){
5114             return n.htmlFor;
5115         }
5116         if(attr == "class" || attr == "className"){
5117             return (n instanceof SVGElement) ? n.className.baseVal : n.className;
5118         }
5119         return n.getAttribute(attr) || n[attr];
5120
5121     };
5122
5123     function getNodes(ns, mode, tagName){
5124         var result = [], ri = -1, cs;
5125         if(!ns){
5126             return result;
5127         }
5128         tagName = tagName || "*";
5129         if(typeof ns.getElementsByTagName != "undefined"){
5130             ns = [ns];
5131         }
5132         if(!mode){
5133             for(var i = 0, ni; ni = ns[i]; i++){
5134                 cs = ni.getElementsByTagName(tagName);
5135                 for(var j = 0, ci; ci = cs[j]; j++){
5136                     result[++ri] = ci;
5137                 }
5138             }
5139         }else if(mode == "/" || mode == ">"){
5140             var utag = tagName.toUpperCase();
5141             for(var i = 0, ni, cn; ni = ns[i]; i++){
5142                 cn = ni.children || ni.childNodes;
5143                 for(var j = 0, cj; cj = cn[j]; j++){
5144                     if(cj.nodeName == utag || cj.nodeName == tagName  || tagName == '*'){
5145                         result[++ri] = cj;
5146                     }
5147                 }
5148             }
5149         }else if(mode == "+"){
5150             var utag = tagName.toUpperCase();
5151             for(var i = 0, n; n = ns[i]; i++){
5152                 while((n = n.nextSibling) && n.nodeType != 1);
5153                 if(n && (n.nodeName == utag || n.nodeName == tagName || tagName == '*')){
5154                     result[++ri] = n;
5155                 }
5156             }
5157         }else if(mode == "~"){
5158             for(var i = 0, n; n = ns[i]; i++){
5159                 while((n = n.nextSibling) && (n.nodeType != 1 || (tagName == '*' || n.tagName.toLowerCase()!=tagName)));
5160                 if(n){
5161                     result[++ri] = n;
5162                 }
5163             }
5164         }
5165         return result;
5166     };
5167
5168     function concat(a, b){
5169         if(b.slice){
5170             return a.concat(b);
5171         }
5172         for(var i = 0, l = b.length; i < l; i++){
5173             a[a.length] = b[i];
5174         }
5175         return a;
5176     }
5177
5178     function byTag(cs, tagName){
5179         if(cs.tagName || cs == document){
5180             cs = [cs];
5181         }
5182         if(!tagName){
5183             return cs;
5184         }
5185         var r = [], ri = -1;
5186         tagName = tagName.toLowerCase();
5187         for(var i = 0, ci; ci = cs[i]; i++){
5188             if(ci.nodeType == 1 && ci.tagName.toLowerCase()==tagName){
5189                 r[++ri] = ci;
5190             }
5191         }
5192         return r;
5193     };
5194
5195     function byId(cs, attr, id){
5196         if(cs.tagName || cs == document){
5197             cs = [cs];
5198         }
5199         if(!id){
5200             return cs;
5201         }
5202         var r = [], ri = -1;
5203         for(var i = 0,ci; ci = cs[i]; i++){
5204             if(ci && ci.id == id){
5205                 r[++ri] = ci;
5206                 return r;
5207             }
5208         }
5209         return r;
5210     };
5211
5212     function byAttribute(cs, attr, value, op, custom){
5213         var r = [], ri = -1, st = custom=="{";
5214         var f = Roo.DomQuery.operators[op];
5215         for(var i = 0, ci; ci = cs[i]; i++){
5216             var a;
5217             if(st){
5218                 a = Roo.DomQuery.getStyle(ci, attr);
5219             }
5220             else if(attr == "class" || attr == "className"){
5221                 a = (ci instanceof SVGElement) ? ci.className.baseVal : ci.className;
5222             }else if(attr == "for"){
5223                 a = ci.htmlFor;
5224             }else if(attr == "href"){
5225                 a = ci.getAttribute("href", 2);
5226             }else{
5227                 a = ci.getAttribute(attr);
5228             }
5229             if((f && f(a, value)) || (!f && a)){
5230                 r[++ri] = ci;
5231             }
5232         }
5233         return r;
5234     };
5235
5236     function byPseudo(cs, name, value){
5237         return Roo.DomQuery.pseudos[name](cs, value);
5238     };
5239
5240     // This is for IE MSXML which does not support expandos.
5241     // IE runs the same speed using setAttribute, however FF slows way down
5242     // and Safari completely fails so they need to continue to use expandos.
5243     var isIE = window.ActiveXObject ? true : false;
5244
5245     // this eval is stop the compressor from
5246     // renaming the variable to something shorter
5247     
5248     /** eval:var:batch */
5249     var batch = 30803; 
5250
5251     var key = 30803;
5252
5253     function nodupIEXml(cs){
5254         var d = ++key;
5255         cs[0].setAttribute("_nodup", d);
5256         var r = [cs[0]];
5257         for(var i = 1, len = cs.length; i < len; i++){
5258             var c = cs[i];
5259             if(!c.getAttribute("_nodup") != d){
5260                 c.setAttribute("_nodup", d);
5261                 r[r.length] = c;
5262             }
5263         }
5264         for(var i = 0, len = cs.length; i < len; i++){
5265             cs[i].removeAttribute("_nodup");
5266         }
5267         return r;
5268     }
5269
5270     function nodup(cs){
5271         if(!cs){
5272             return [];
5273         }
5274         var len = cs.length, c, i, r = cs, cj, ri = -1;
5275         if(!len || typeof cs.nodeType != "undefined" || len == 1){
5276             return cs;
5277         }
5278         if(isIE && typeof cs[0].selectSingleNode != "undefined"){
5279             return nodupIEXml(cs);
5280         }
5281         var d = ++key;
5282         cs[0]._nodup = d;
5283         for(i = 1; c = cs[i]; i++){
5284             if(c._nodup != d){
5285                 c._nodup = d;
5286             }else{
5287                 r = [];
5288                 for(var j = 0; j < i; j++){
5289                     r[++ri] = cs[j];
5290                 }
5291                 for(j = i+1; cj = cs[j]; j++){
5292                     if(cj._nodup != d){
5293                         cj._nodup = d;
5294                         r[++ri] = cj;
5295                     }
5296                 }
5297                 return r;
5298             }
5299         }
5300         return r;
5301     }
5302
5303     function quickDiffIEXml(c1, c2){
5304         var d = ++key;
5305         for(var i = 0, len = c1.length; i < len; i++){
5306             c1[i].setAttribute("_qdiff", d);
5307         }
5308         var r = [];
5309         for(var i = 0, len = c2.length; i < len; i++){
5310             if(c2[i].getAttribute("_qdiff") != d){
5311                 r[r.length] = c2[i];
5312             }
5313         }
5314         for(var i = 0, len = c1.length; i < len; i++){
5315            c1[i].removeAttribute("_qdiff");
5316         }
5317         return r;
5318     }
5319
5320     function quickDiff(c1, c2){
5321         var len1 = c1.length;
5322         if(!len1){
5323             return c2;
5324         }
5325         if(isIE && c1[0].selectSingleNode){
5326             return quickDiffIEXml(c1, c2);
5327         }
5328         var d = ++key;
5329         for(var i = 0; i < len1; i++){
5330             c1[i]._qdiff = d;
5331         }
5332         var r = [];
5333         for(var i = 0, len = c2.length; i < len; i++){
5334             if(c2[i]._qdiff != d){
5335                 r[r.length] = c2[i];
5336             }
5337         }
5338         return r;
5339     }
5340
5341     function quickId(ns, mode, root, id){
5342         if(ns == root){
5343            var d = root.ownerDocument || root;
5344            return d.getElementById(id);
5345         }
5346         ns = getNodes(ns, mode, "*");
5347         return byId(ns, null, id);
5348     }
5349
5350     return {
5351         getStyle : function(el, name){
5352             return Roo.fly(el).getStyle(name);
5353         },
5354         /**
5355          * Compiles a selector/xpath query into a reusable function. The returned function
5356          * takes one parameter "root" (optional), which is the context node from where the query should start.
5357          * @param {String} selector The selector/xpath query
5358          * @param {String} type (optional) Either "select" (the default) or "simple" for a simple selector match
5359          * @return {Function}
5360          */
5361         compile : function(path, type){
5362             type = type || "select";
5363             
5364             var fn = ["var f = function(root){\n var mode; ++batch; var n = root || document;\n"];
5365             var q = path, mode, lq;
5366             var tk = Roo.DomQuery.matchers;
5367             var tklen = tk.length;
5368             var mm;
5369
5370             // accept leading mode switch
5371             var lmode = q.match(modeRe);
5372             if(lmode && lmode[1]){
5373                 fn[fn.length] = 'mode="'+lmode[1].replace(trimRe, "")+'";';
5374                 q = q.replace(lmode[1], "");
5375             }
5376             // strip leading slashes
5377             while(path.substr(0, 1)=="/"){
5378                 path = path.substr(1);
5379             }
5380
5381             while(q && lq != q){
5382                 lq = q;
5383                 var tm = q.match(tagTokenRe);
5384                 if(type == "select"){
5385                     if(tm){
5386                         if(tm[1] == "#"){
5387                             fn[fn.length] = 'n = quickId(n, mode, root, "'+tm[2]+'");';
5388                         }else{
5389                             fn[fn.length] = 'n = getNodes(n, mode, "'+tm[2]+'");';
5390                         }
5391                         q = q.replace(tm[0], "");
5392                     }else if(q.substr(0, 1) != '@'){
5393                         fn[fn.length] = 'n = getNodes(n, mode, "*");';
5394                     }
5395                 }else{
5396                     if(tm){
5397                         if(tm[1] == "#"){
5398                             fn[fn.length] = 'n = byId(n, null, "'+tm[2]+'");';
5399                         }else{
5400                             fn[fn.length] = 'n = byTag(n, "'+tm[2]+'");';
5401                         }
5402                         q = q.replace(tm[0], "");
5403                     }
5404                 }
5405                 while(!(mm = q.match(modeRe))){
5406                     var matched = false;
5407                     for(var j = 0; j < tklen; j++){
5408                         var t = tk[j];
5409                         var m = q.match(t.re);
5410                         if(m){
5411                             fn[fn.length] = t.select.replace(tplRe, function(x, i){
5412                                                     return m[i];
5413                                                 });
5414                             q = q.replace(m[0], "");
5415                             matched = true;
5416                             break;
5417                         }
5418                     }
5419                     // prevent infinite loop on bad selector
5420                     if(!matched){
5421                         throw 'Error parsing selector, parsing failed at "' + q + '"';
5422                     }
5423                 }
5424                 if(mm[1]){
5425                     fn[fn.length] = 'mode="'+mm[1].replace(trimRe, "")+'";';
5426                     q = q.replace(mm[1], "");
5427                 }
5428             }
5429             fn[fn.length] = "return nodup(n);\n}";
5430             
5431              /** 
5432               * list of variables that need from compression as they are used by eval.
5433              *  eval:var:batch 
5434              *  eval:var:nodup
5435              *  eval:var:byTag
5436              *  eval:var:ById
5437              *  eval:var:getNodes
5438              *  eval:var:quickId
5439              *  eval:var:mode
5440              *  eval:var:root
5441              *  eval:var:n
5442              *  eval:var:byClassName
5443              *  eval:var:byPseudo
5444              *  eval:var:byAttribute
5445              *  eval:var:attrValue
5446              * 
5447              **/ 
5448             eval(fn.join(""));
5449             return f;
5450         },
5451
5452         /**
5453          * Selects a group of elements.
5454          * @param {String} selector The selector/xpath query (can be a comma separated list of selectors)
5455          * @param {Node} root (optional) The start of the query (defaults to document).
5456          * @return {Array}
5457          */
5458         select : function(path, root, type){
5459             if(!root || root == document){
5460                 root = document;
5461             }
5462             if(typeof root == "string"){
5463                 root = document.getElementById(root);
5464             }
5465             var paths = path.split(",");
5466             var results = [];
5467             for(var i = 0, len = paths.length; i < len; i++){
5468                 var p = paths[i].replace(trimRe, "");
5469                 if(!cache[p]){
5470                     cache[p] = Roo.DomQuery.compile(p);
5471                     if(!cache[p]){
5472                         throw p + " is not a valid selector";
5473                     }
5474                 }
5475                 var result = cache[p](root);
5476                 if(result && result != document){
5477                     results = results.concat(result);
5478                 }
5479             }
5480             if(paths.length > 1){
5481                 return nodup(results);
5482             }
5483             return results;
5484         },
5485
5486         /**
5487          * Selects a single element.
5488          * @param {String} selector The selector/xpath query
5489          * @param {Node} root (optional) The start of the query (defaults to document).
5490          * @return {Element}
5491          */
5492         selectNode : function(path, root){
5493             return Roo.DomQuery.select(path, root)[0];
5494         },
5495
5496         /**
5497          * Selects the value of a node, optionally replacing null with the defaultValue.
5498          * @param {String} selector The selector/xpath query
5499          * @param {Node} root (optional) The start of the query (defaults to document).
5500          * @param {String} defaultValue
5501          */
5502         selectValue : function(path, root, defaultValue){
5503             path = path.replace(trimRe, "");
5504             if(!valueCache[path]){
5505                 valueCache[path] = Roo.DomQuery.compile(path, "select");
5506             }
5507             var n = valueCache[path](root);
5508             n = n[0] ? n[0] : n;
5509             var v = (n && n.firstChild ? n.firstChild.nodeValue : null);
5510             return ((v === null||v === undefined||v==='') ? defaultValue : v);
5511         },
5512
5513         /**
5514          * Selects the value of a node, parsing integers and floats.
5515          * @param {String} selector The selector/xpath query
5516          * @param {Node} root (optional) The start of the query (defaults to document).
5517          * @param {Number} defaultValue
5518          * @return {Number}
5519          */
5520         selectNumber : function(path, root, defaultValue){
5521             var v = Roo.DomQuery.selectValue(path, root, defaultValue || 0);
5522             return parseFloat(v);
5523         },
5524
5525         /**
5526          * Returns true if the passed element(s) match the passed simple selector (e.g. div.some-class or span:first-child)
5527          * @param {String/HTMLElement/Array} el An element id, element or array of elements
5528          * @param {String} selector The simple selector to test
5529          * @return {Boolean}
5530          */
5531         is : function(el, ss){
5532             if(typeof el == "string"){
5533                 el = document.getElementById(el);
5534             }
5535             var isArray = (el instanceof Array);
5536             var result = Roo.DomQuery.filter(isArray ? el : [el], ss);
5537             return isArray ? (result.length == el.length) : (result.length > 0);
5538         },
5539
5540         /**
5541          * Filters an array of elements to only include matches of a simple selector (e.g. div.some-class or span:first-child)
5542          * @param {Array} el An array of elements to filter
5543          * @param {String} selector The simple selector to test
5544          * @param {Boolean} nonMatches If true, it returns the elements that DON'T match
5545          * the selector instead of the ones that match
5546          * @return {Array}
5547          */
5548         filter : function(els, ss, nonMatches){
5549             ss = ss.replace(trimRe, "");
5550             if(!simpleCache[ss]){
5551                 simpleCache[ss] = Roo.DomQuery.compile(ss, "simple");
5552             }
5553             var result = simpleCache[ss](els);
5554             return nonMatches ? quickDiff(result, els) : result;
5555         },
5556
5557         /**
5558          * Collection of matching regular expressions and code snippets.
5559          */
5560         matchers : [{
5561                 re: /^\.([\w-]+)/,
5562                 select: 'n = byClassName(n, null, " {1} ");'
5563             }, {
5564                 re: /^\:([\w-]+)(?:\(((?:[^\s>\/]*|.*?))\))?/,
5565                 select: 'n = byPseudo(n, "{1}", "{2}");'
5566             },{
5567                 re: /^(?:([\[\{])(?:@)?([\w-]+)\s?(?:(=|.=)\s?['"]?(.*?)["']?)?[\]\}])/,
5568                 select: 'n = byAttribute(n, "{2}", "{4}", "{3}", "{1}");'
5569             }, {
5570                 re: /^#([\w-]+)/,
5571                 select: 'n = byId(n, null, "{1}");'
5572             },{
5573                 re: /^@([\w-]+)/,
5574                 select: 'return {firstChild:{nodeValue:attrValue(n, "{1}")}};'
5575             }
5576         ],
5577
5578         /**
5579          * Collection of operator comparison functions. The default operators are =, !=, ^=, $=, *=, %=, |= and ~=.
5580          * 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;.
5581          */
5582         operators : {
5583             "=" : function(a, v){
5584                 return a == v;
5585             },
5586             "!=" : function(a, v){
5587                 return a != v;
5588             },
5589             "^=" : function(a, v){
5590                 return a && a.substr(0, v.length) == v;
5591             },
5592             "$=" : function(a, v){
5593                 return a && a.substr(a.length-v.length) == v;
5594             },
5595             "*=" : function(a, v){
5596                 return a && a.indexOf(v) !== -1;
5597             },
5598             "%=" : function(a, v){
5599                 return (a % v) == 0;
5600             },
5601             "|=" : function(a, v){
5602                 return a && (a == v || a.substr(0, v.length+1) == v+'-');
5603             },
5604             "~=" : function(a, v){
5605                 return a && (' '+a+' ').indexOf(' '+v+' ') != -1;
5606             }
5607         },
5608
5609         /**
5610          * Collection of "pseudo class" processors. Each processor is passed the current nodeset (array)
5611          * and the argument (if any) supplied in the selector.
5612          */
5613         pseudos : {
5614             "first-child" : function(c){
5615                 var r = [], ri = -1, n;
5616                 for(var i = 0, ci; ci = n = c[i]; i++){
5617                     while((n = n.previousSibling) && n.nodeType != 1);
5618                     if(!n){
5619                         r[++ri] = ci;
5620                     }
5621                 }
5622                 return r;
5623             },
5624
5625             "last-child" : function(c){
5626                 var r = [], ri = -1, n;
5627                 for(var i = 0, ci; ci = n = c[i]; i++){
5628                     while((n = n.nextSibling) && n.nodeType != 1);
5629                     if(!n){
5630                         r[++ri] = ci;
5631                     }
5632                 }
5633                 return r;
5634             },
5635
5636             "nth-child" : function(c, a) {
5637                 var r = [], ri = -1;
5638                 var m = nthRe.exec(a == "even" && "2n" || a == "odd" && "2n+1" || !nthRe2.test(a) && "n+" + a || a);
5639                 var f = (m[1] || 1) - 0, l = m[2] - 0;
5640                 for(var i = 0, n; n = c[i]; i++){
5641                     var pn = n.parentNode;
5642                     if (batch != pn._batch) {
5643                         var j = 0;
5644                         for(var cn = pn.firstChild; cn; cn = cn.nextSibling){
5645                             if(cn.nodeType == 1){
5646                                cn.nodeIndex = ++j;
5647                             }
5648                         }
5649                         pn._batch = batch;
5650                     }
5651                     if (f == 1) {
5652                         if (l == 0 || n.nodeIndex == l){
5653                             r[++ri] = n;
5654                         }
5655                     } else if ((n.nodeIndex + l) % f == 0){
5656                         r[++ri] = n;
5657                     }
5658                 }
5659
5660                 return r;
5661             },
5662
5663             "only-child" : function(c){
5664                 var r = [], ri = -1;;
5665                 for(var i = 0, ci; ci = c[i]; i++){
5666                     if(!prev(ci) && !next(ci)){
5667                         r[++ri] = ci;
5668                     }
5669                 }
5670                 return r;
5671             },
5672
5673             "empty" : function(c){
5674                 var r = [], ri = -1;
5675                 for(var i = 0, ci; ci = c[i]; i++){
5676                     var cns = ci.childNodes, j = 0, cn, empty = true;
5677                     while(cn = cns[j]){
5678                         ++j;
5679                         if(cn.nodeType == 1 || cn.nodeType == 3){
5680                             empty = false;
5681                             break;
5682                         }
5683                     }
5684                     if(empty){
5685                         r[++ri] = ci;
5686                     }
5687                 }
5688                 return r;
5689             },
5690
5691             "contains" : function(c, v){
5692                 var r = [], ri = -1;
5693                 for(var i = 0, ci; ci = c[i]; i++){
5694                     if((ci.textContent||ci.innerText||'').indexOf(v) != -1){
5695                         r[++ri] = ci;
5696                     }
5697                 }
5698                 return r;
5699             },
5700
5701             "nodeValue" : function(c, v){
5702                 var r = [], ri = -1;
5703                 for(var i = 0, ci; ci = c[i]; i++){
5704                     if(ci.firstChild && ci.firstChild.nodeValue == v){
5705                         r[++ri] = ci;
5706                     }
5707                 }
5708                 return r;
5709             },
5710
5711             "checked" : function(c){
5712                 var r = [], ri = -1;
5713                 for(var i = 0, ci; ci = c[i]; i++){
5714                     if(ci.checked == true){
5715                         r[++ri] = ci;
5716                     }
5717                 }
5718                 return r;
5719             },
5720
5721             "not" : function(c, ss){
5722                 return Roo.DomQuery.filter(c, ss, true);
5723             },
5724
5725             "odd" : function(c){
5726                 return this["nth-child"](c, "odd");
5727             },
5728
5729             "even" : function(c){
5730                 return this["nth-child"](c, "even");
5731             },
5732
5733             "nth" : function(c, a){
5734                 return c[a-1] || [];
5735             },
5736
5737             "first" : function(c){
5738                 return c[0] || [];
5739             },
5740
5741             "last" : function(c){
5742                 return c[c.length-1] || [];
5743             },
5744
5745             "has" : function(c, ss){
5746                 var s = Roo.DomQuery.select;
5747                 var r = [], ri = -1;
5748                 for(var i = 0, ci; ci = c[i]; i++){
5749                     if(s(ss, ci).length > 0){
5750                         r[++ri] = ci;
5751                     }
5752                 }
5753                 return r;
5754             },
5755
5756             "next" : function(c, ss){
5757                 var is = Roo.DomQuery.is;
5758                 var r = [], ri = -1;
5759                 for(var i = 0, ci; ci = c[i]; i++){
5760                     var n = next(ci);
5761                     if(n && is(n, ss)){
5762                         r[++ri] = ci;
5763                     }
5764                 }
5765                 return r;
5766             },
5767
5768             "prev" : function(c, ss){
5769                 var is = Roo.DomQuery.is;
5770                 var r = [], ri = -1;
5771                 for(var i = 0, ci; ci = c[i]; i++){
5772                     var n = prev(ci);
5773                     if(n && is(n, ss)){
5774                         r[++ri] = ci;
5775                     }
5776                 }
5777                 return r;
5778             }
5779         }
5780     };
5781 }();
5782
5783 /**
5784  * Selects an array of DOM nodes by CSS/XPath selector. Shorthand of {@link Roo.DomQuery#select}
5785  * @param {String} path The selector/xpath query
5786  * @param {Node} root (optional) The start of the query (defaults to document).
5787  * @return {Array}
5788  * @member Roo
5789  * @method query
5790  */
5791 Roo.query = Roo.DomQuery.select;
5792 /*
5793  * Based on:
5794  * Ext JS Library 1.1.1
5795  * Copyright(c) 2006-2007, Ext JS, LLC.
5796  *
5797  * Originally Released Under LGPL - original licence link has changed is not relivant.
5798  *
5799  * Fork - LGPL
5800  * <script type="text/javascript">
5801  */
5802
5803 /**
5804  * @class Roo.util.Observable
5805  * Base class that provides a common interface for publishing events. Subclasses are expected to
5806  * to have a property "events" with all the events defined.<br>
5807  * For example:
5808  * <pre><code>
5809  Employee = function(name){
5810     this.name = name;
5811     this.addEvents({
5812         "fired" : true,
5813         "quit" : true
5814     });
5815  }
5816  Roo.extend(Employee, Roo.util.Observable);
5817 </code></pre>
5818  * @param {Object} config properties to use (incuding events / listeners)
5819  */
5820
5821 Roo.util.Observable = function(cfg){
5822     
5823     cfg = cfg|| {};
5824     this.addEvents(cfg.events || {});
5825     if (cfg.events) {
5826         delete cfg.events; // make sure
5827     }
5828      
5829     Roo.apply(this, cfg);
5830     
5831     if(this.listeners){
5832         this.on(this.listeners);
5833         delete this.listeners;
5834     }
5835 };
5836 Roo.util.Observable.prototype = {
5837     /** 
5838  * @cfg {Object} listeners  list of events and functions to call for this object, 
5839  * For example :
5840  * <pre><code>
5841     listeners :  { 
5842        'click' : function(e) {
5843            ..... 
5844         } ,
5845         .... 
5846     } 
5847   </code></pre>
5848  */
5849     
5850     
5851     /**
5852      * Fires the specified event with the passed parameters (minus the event name).
5853      * @param {String} eventName
5854      * @param {Object...} args Variable number of parameters are passed to handlers
5855      * @return {Boolean} returns false if any of the handlers return false otherwise it returns true
5856      */
5857     fireEvent : function(){
5858         var ce = this.events[arguments[0].toLowerCase()];
5859         if(typeof ce == "object"){
5860             return ce.fire.apply(ce, Array.prototype.slice.call(arguments, 1));
5861         }else{
5862             return true;
5863         }
5864     },
5865
5866     // private
5867     filterOptRe : /^(?:scope|delay|buffer|single)$/,
5868
5869     /**
5870      * Appends an event handler to this component
5871      * @param {String}   eventName The type of event to listen for
5872      * @param {Function} handler The method the event invokes
5873      * @param {Object}   scope (optional) The scope in which to execute the handler
5874      * function. The handler function's "this" context.
5875      * @param {Object}   options (optional) An object containing handler configuration
5876      * properties. This may contain any of the following properties:<ul>
5877      * <li>scope {Object} The scope in which to execute the handler function. The handler function's "this" context.</li>
5878      * <li>delay {Number} The number of milliseconds to delay the invocation of the handler after te event fires.</li>
5879      * <li>single {Boolean} True to add a handler to handle just the next firing of the event, and then remove itself.</li>
5880      * <li>buffer {Number} Causes the handler to be scheduled to run in an {@link Roo.util.DelayedTask} delayed
5881      * by the specified number of milliseconds. If the event fires again within that time, the original
5882      * handler is <em>not</em> invoked, but the new handler is scheduled in its place.</li>
5883      * </ul><br>
5884      * <p>
5885      * <b>Combining Options</b><br>
5886      * Using the options argument, it is possible to combine different types of listeners:<br>
5887      * <br>
5888      * A normalized, delayed, one-time listener that auto stops the event and passes a custom argument (forumId)
5889                 <pre><code>
5890                 el.on('click', this.onClick, this, {
5891                         single: true,
5892                 delay: 100,
5893                 forumId: 4
5894                 });
5895                 </code></pre>
5896      * <p>
5897      * <b>Attaching multiple handlers in 1 call</b><br>
5898      * The method also allows for a single argument to be passed which is a config object containing properties
5899      * which specify multiple handlers.
5900      * <pre><code>
5901                 el.on({
5902                         'click': {
5903                         fn: this.onClick,
5904                         scope: this,
5905                         delay: 100
5906                 }, 
5907                 'mouseover': {
5908                         fn: this.onMouseOver,
5909                         scope: this
5910                 },
5911                 'mouseout': {
5912                         fn: this.onMouseOut,
5913                         scope: this
5914                 }
5915                 });
5916                 </code></pre>
5917      * <p>
5918      * Or a shorthand syntax which passes the same scope object to all handlers:
5919         <pre><code>
5920                 el.on({
5921                         'click': this.onClick,
5922                 'mouseover': this.onMouseOver,
5923                 'mouseout': this.onMouseOut,
5924                 scope: this
5925                 });
5926                 </code></pre>
5927      */
5928     addListener : function(eventName, fn, scope, o){
5929         if(typeof eventName == "object"){
5930             o = eventName;
5931             for(var e in o){
5932                 if(this.filterOptRe.test(e)){
5933                     continue;
5934                 }
5935                 if(typeof o[e] == "function"){
5936                     // shared options
5937                     this.addListener(e, o[e], o.scope,  o);
5938                 }else{
5939                     // individual options
5940                     this.addListener(e, o[e].fn, o[e].scope, o[e]);
5941                 }
5942             }
5943             return;
5944         }
5945         o = (!o || typeof o == "boolean") ? {} : o;
5946         eventName = eventName.toLowerCase();
5947         var ce = this.events[eventName] || true;
5948         if(typeof ce == "boolean"){
5949             ce = new Roo.util.Event(this, eventName);
5950             this.events[eventName] = ce;
5951         }
5952         ce.addListener(fn, scope, o);
5953     },
5954
5955     /**
5956      * Removes a listener
5957      * @param {String}   eventName     The type of event to listen for
5958      * @param {Function} handler        The handler to remove
5959      * @param {Object}   scope  (optional) The scope (this object) for the handler
5960      */
5961     removeListener : function(eventName, fn, scope){
5962         var ce = this.events[eventName.toLowerCase()];
5963         if(typeof ce == "object"){
5964             ce.removeListener(fn, scope);
5965         }
5966     },
5967
5968     /**
5969      * Removes all listeners for this object
5970      */
5971     purgeListeners : function(){
5972         for(var evt in this.events){
5973             if(typeof this.events[evt] == "object"){
5974                  this.events[evt].clearListeners();
5975             }
5976         }
5977     },
5978
5979     relayEvents : function(o, events){
5980         var createHandler = function(ename){
5981             return function(){
5982                  
5983                 return this.fireEvent.apply(this, Roo.combine(ename, Array.prototype.slice.call(arguments, 0)));
5984             };
5985         };
5986         for(var i = 0, len = events.length; i < len; i++){
5987             var ename = events[i];
5988             if(!this.events[ename]){
5989                 this.events[ename] = true;
5990             };
5991             o.on(ename, createHandler(ename), this);
5992         }
5993     },
5994
5995     /**
5996      * Used to define events on this Observable
5997      * @param {Object} object The object with the events defined
5998      */
5999     addEvents : function(o){
6000         if(!this.events){
6001             this.events = {};
6002         }
6003         Roo.applyIf(this.events, o);
6004     },
6005
6006     /**
6007      * Checks to see if this object has any listeners for a specified event
6008      * @param {String} eventName The name of the event to check for
6009      * @return {Boolean} True if the event is being listened for, else false
6010      */
6011     hasListener : function(eventName){
6012         var e = this.events[eventName];
6013         return typeof e == "object" && e.listeners.length > 0;
6014     }
6015 };
6016 /**
6017  * Appends an event handler to this element (shorthand for addListener)
6018  * @param {String}   eventName     The type of event to listen for
6019  * @param {Function} handler        The method the event invokes
6020  * @param {Object}   scope (optional) The scope in which to execute the handler
6021  * function. The handler function's "this" context.
6022  * @param {Object}   options  (optional)
6023  * @method
6024  */
6025 Roo.util.Observable.prototype.on = Roo.util.Observable.prototype.addListener;
6026 /**
6027  * Removes a listener (shorthand for removeListener)
6028  * @param {String}   eventName     The type of event to listen for
6029  * @param {Function} handler        The handler to remove
6030  * @param {Object}   scope  (optional) The scope (this object) for the handler
6031  * @method
6032  */
6033 Roo.util.Observable.prototype.un = Roo.util.Observable.prototype.removeListener;
6034
6035 /**
6036  * Starts capture on the specified Observable. All events will be passed
6037  * to the supplied function with the event name + standard signature of the event
6038  * <b>before</b> the event is fired. If the supplied function returns false,
6039  * the event will not fire.
6040  * @param {Observable} o The Observable to capture
6041  * @param {Function} fn The function to call
6042  * @param {Object} scope (optional) The scope (this object) for the fn
6043  * @static
6044  */
6045 Roo.util.Observable.capture = function(o, fn, scope){
6046     o.fireEvent = o.fireEvent.createInterceptor(fn, scope);
6047 };
6048
6049 /**
6050  * Removes <b>all</b> added captures from the Observable.
6051  * @param {Observable} o The Observable to release
6052  * @static
6053  */
6054 Roo.util.Observable.releaseCapture = function(o){
6055     o.fireEvent = Roo.util.Observable.prototype.fireEvent;
6056 };
6057
6058 (function(){
6059
6060     var createBuffered = function(h, o, scope){
6061         var task = new Roo.util.DelayedTask();
6062         return function(){
6063             task.delay(o.buffer, h, scope, Array.prototype.slice.call(arguments, 0));
6064         };
6065     };
6066
6067     var createSingle = function(h, e, fn, scope){
6068         return function(){
6069             e.removeListener(fn, scope);
6070             return h.apply(scope, arguments);
6071         };
6072     };
6073
6074     var createDelayed = function(h, o, scope){
6075         return function(){
6076             var args = Array.prototype.slice.call(arguments, 0);
6077             setTimeout(function(){
6078                 h.apply(scope, args);
6079             }, o.delay || 10);
6080         };
6081     };
6082
6083     Roo.util.Event = function(obj, name){
6084         this.name = name;
6085         this.obj = obj;
6086         this.listeners = [];
6087     };
6088
6089     Roo.util.Event.prototype = {
6090         addListener : function(fn, scope, options){
6091             var o = options || {};
6092             scope = scope || this.obj;
6093             if(!this.isListening(fn, scope)){
6094                 var l = {fn: fn, scope: scope, options: o};
6095                 var h = fn;
6096                 if(o.delay){
6097                     h = createDelayed(h, o, scope);
6098                 }
6099                 if(o.single){
6100                     h = createSingle(h, this, fn, scope);
6101                 }
6102                 if(o.buffer){
6103                     h = createBuffered(h, o, scope);
6104                 }
6105                 l.fireFn = h;
6106                 if(!this.firing){ // if we are currently firing this event, don't disturb the listener loop
6107                     this.listeners.push(l);
6108                 }else{
6109                     this.listeners = this.listeners.slice(0);
6110                     this.listeners.push(l);
6111                 }
6112             }
6113         },
6114
6115         findListener : function(fn, scope){
6116             scope = scope || this.obj;
6117             var ls = this.listeners;
6118             for(var i = 0, len = ls.length; i < len; i++){
6119                 var l = ls[i];
6120                 if(l.fn == fn && l.scope == scope){
6121                     return i;
6122                 }
6123             }
6124             return -1;
6125         },
6126
6127         isListening : function(fn, scope){
6128             return this.findListener(fn, scope) != -1;
6129         },
6130
6131         removeListener : function(fn, scope){
6132             var index;
6133             if((index = this.findListener(fn, scope)) != -1){
6134                 if(!this.firing){
6135                     this.listeners.splice(index, 1);
6136                 }else{
6137                     this.listeners = this.listeners.slice(0);
6138                     this.listeners.splice(index, 1);
6139                 }
6140                 return true;
6141             }
6142             return false;
6143         },
6144
6145         clearListeners : function(){
6146             this.listeners = [];
6147         },
6148
6149         fire : function(){
6150             var ls = this.listeners, scope, len = ls.length;
6151             if(len > 0){
6152                 this.firing = true;
6153                 var args = Array.prototype.slice.call(arguments, 0);                
6154                 for(var i = 0; i < len; i++){
6155                     var l = ls[i];
6156                     if(l.fireFn.apply(l.scope||this.obj||window, args) === false){
6157                         this.firing = false;
6158                         return false;
6159                     }
6160                 }
6161                 this.firing = false;
6162             }
6163             return true;
6164         }
6165     };
6166 })();/*
6167  * RooJS Library 
6168  * Copyright(c) 2007-2017, Roo J Solutions Ltd
6169  *
6170  * Licence LGPL 
6171  *
6172  */
6173  
6174 /**
6175  * @class Roo.Document
6176  * @extends Roo.util.Observable
6177  * This is a convience class to wrap up the main document loading code.. , rather than adding Roo.onReady(......)
6178  * 
6179  * @param {Object} config the methods and properties of the 'base' class for the application.
6180  * 
6181  *  Generic Page handler - implement this to start your app..
6182  * 
6183  * eg.
6184  *  MyProject = new Roo.Document({
6185         events : {
6186             'load' : true // your events..
6187         },
6188         listeners : {
6189             'ready' : function() {
6190                 // fired on Roo.onReady()
6191             }
6192         }
6193  * 
6194  */
6195 Roo.Document = function(cfg) {
6196      
6197     this.addEvents({ 
6198         'ready' : true
6199     });
6200     Roo.util.Observable.call(this,cfg);
6201     
6202     var _this = this;
6203     
6204     Roo.onReady(function() {
6205         _this.fireEvent('ready');
6206     },null,false);
6207     
6208     
6209 }
6210
6211 Roo.extend(Roo.Document, Roo.util.Observable, {});/*
6212  * Based on:
6213  * Ext JS Library 1.1.1
6214  * Copyright(c) 2006-2007, Ext JS, LLC.
6215  *
6216  * Originally Released Under LGPL - original licence link has changed is not relivant.
6217  *
6218  * Fork - LGPL
6219  * <script type="text/javascript">
6220  */
6221
6222 /**
6223  * @class Roo.EventManager
6224  * Registers event handlers that want to receive a normalized EventObject instead of the standard browser event and provides 
6225  * several useful events directly.
6226  * See {@link Roo.EventObject} for more details on normalized event objects.
6227  * @singleton
6228  */
6229 Roo.EventManager = function(){
6230     var docReadyEvent, docReadyProcId, docReadyState = false;
6231     var resizeEvent, resizeTask, textEvent, textSize;
6232     var E = Roo.lib.Event;
6233     var D = Roo.lib.Dom;
6234
6235     
6236     
6237
6238     var fireDocReady = function(){
6239         if(!docReadyState){
6240             docReadyState = true;
6241             Roo.isReady = true;
6242             if(docReadyProcId){
6243                 clearInterval(docReadyProcId);
6244             }
6245             if(Roo.isGecko || Roo.isOpera) {
6246                 document.removeEventListener("DOMContentLoaded", fireDocReady, false);
6247             }
6248             if(Roo.isIE){
6249                 var defer = document.getElementById("ie-deferred-loader");
6250                 if(defer){
6251                     defer.onreadystatechange = null;
6252                     defer.parentNode.removeChild(defer);
6253                 }
6254             }
6255             if(docReadyEvent){
6256                 docReadyEvent.fire();
6257                 docReadyEvent.clearListeners();
6258             }
6259         }
6260     };
6261     
6262     var initDocReady = function(){
6263         docReadyEvent = new Roo.util.Event();
6264         if(Roo.isGecko || Roo.isOpera) {
6265             document.addEventListener("DOMContentLoaded", fireDocReady, false);
6266         }else if(Roo.isIE){
6267             document.write("<s"+'cript id="ie-deferred-loader" defer="defer" src="/'+'/:"></s'+"cript>");
6268             var defer = document.getElementById("ie-deferred-loader");
6269             defer.onreadystatechange = function(){
6270                 if(this.readyState == "complete"){
6271                     fireDocReady();
6272                 }
6273             };
6274         }else if(Roo.isSafari){ 
6275             docReadyProcId = setInterval(function(){
6276                 var rs = document.readyState;
6277                 if(rs == "complete") {
6278                     fireDocReady();     
6279                  }
6280             }, 10);
6281         }
6282         // no matter what, make sure it fires on load
6283         E.on(window, "load", fireDocReady);
6284     };
6285
6286     var createBuffered = function(h, o){
6287         var task = new Roo.util.DelayedTask(h);
6288         return function(e){
6289             // create new event object impl so new events don't wipe out properties
6290             e = new Roo.EventObjectImpl(e);
6291             task.delay(o.buffer, h, null, [e]);
6292         };
6293     };
6294
6295     var createSingle = function(h, el, ename, fn){
6296         return function(e){
6297             Roo.EventManager.removeListener(el, ename, fn);
6298             h(e);
6299         };
6300     };
6301
6302     var createDelayed = function(h, o){
6303         return function(e){
6304             // create new event object impl so new events don't wipe out properties
6305             e = new Roo.EventObjectImpl(e);
6306             setTimeout(function(){
6307                 h(e);
6308             }, o.delay || 10);
6309         };
6310     };
6311     var transitionEndVal = false;
6312     
6313     var transitionEnd = function()
6314     {
6315         if (transitionEndVal) {
6316             return transitionEndVal;
6317         }
6318         var el = document.createElement('div');
6319
6320         var transEndEventNames = {
6321             WebkitTransition : 'webkitTransitionEnd',
6322             MozTransition    : 'transitionend',
6323             OTransition      : 'oTransitionEnd otransitionend',
6324             transition       : 'transitionend'
6325         };
6326     
6327         for (var name in transEndEventNames) {
6328             if (el.style[name] !== undefined) {
6329                 transitionEndVal = transEndEventNames[name];
6330                 return  transitionEndVal ;
6331             }
6332         }
6333     }
6334     
6335   
6336
6337     var listen = function(element, ename, opt, fn, scope)
6338     {
6339         var o = (!opt || typeof opt == "boolean") ? {} : opt;
6340         fn = fn || o.fn; scope = scope || o.scope;
6341         var el = Roo.getDom(element);
6342         
6343         
6344         if(!el){
6345             throw "Error listening for \"" + ename + '\". Element "' + element + '" doesn\'t exist.';
6346         }
6347         
6348         if (ename == 'transitionend') {
6349             ename = transitionEnd();
6350         }
6351         var h = function(e){
6352             e = Roo.EventObject.setEvent(e);
6353             var t;
6354             if(o.delegate){
6355                 t = e.getTarget(o.delegate, el);
6356                 if(!t){
6357                     return;
6358                 }
6359             }else{
6360                 t = e.target;
6361             }
6362             if(o.stopEvent === true){
6363                 e.stopEvent();
6364             }
6365             if(o.preventDefault === true){
6366                e.preventDefault();
6367             }
6368             if(o.stopPropagation === true){
6369                 e.stopPropagation();
6370             }
6371
6372             if(o.normalized === false){
6373                 e = e.browserEvent;
6374             }
6375
6376             fn.call(scope || el, e, t, o);
6377         };
6378         if(o.delay){
6379             h = createDelayed(h, o);
6380         }
6381         if(o.single){
6382             h = createSingle(h, el, ename, fn);
6383         }
6384         if(o.buffer){
6385             h = createBuffered(h, o);
6386         }
6387         
6388         fn._handlers = fn._handlers || [];
6389         
6390         
6391         fn._handlers.push([Roo.id(el), ename, h]);
6392         
6393         
6394          
6395         E.on(el, ename, h); // this adds the actuall listener to the object..
6396         
6397         
6398         if(ename == "mousewheel" && el.addEventListener){ // workaround for jQuery
6399             el.addEventListener("DOMMouseScroll", h, false);
6400             E.on(window, 'unload', function(){
6401                 el.removeEventListener("DOMMouseScroll", h, false);
6402             });
6403         }
6404         if(ename == "mousedown" && el == document){ // fix stopped mousedowns on the document
6405             Roo.EventManager.stoppedMouseDownEvent.addListener(h);
6406         }
6407         return h;
6408     };
6409
6410     var stopListening = function(el, ename, fn){
6411         var id = Roo.id(el), hds = fn._handlers, hd = fn;
6412         if(hds){
6413             for(var i = 0, len = hds.length; i < len; i++){
6414                 var h = hds[i];
6415                 if(h[0] == id && h[1] == ename){
6416                     hd = h[2];
6417                     hds.splice(i, 1);
6418                     break;
6419                 }
6420             }
6421         }
6422         E.un(el, ename, hd);
6423         el = Roo.getDom(el);
6424         if(ename == "mousewheel" && el.addEventListener){
6425             el.removeEventListener("DOMMouseScroll", hd, false);
6426         }
6427         if(ename == "mousedown" && el == document){ // fix stopped mousedowns on the document
6428             Roo.EventManager.stoppedMouseDownEvent.removeListener(hd);
6429         }
6430     };
6431
6432     var propRe = /^(?:scope|delay|buffer|single|stopEvent|preventDefault|stopPropagation|normalized|args|delegate)$/;
6433     
6434     var pub = {
6435         
6436         
6437         /** 
6438          * Fix for doc tools
6439          * @scope Roo.EventManager
6440          */
6441         
6442         
6443         /** 
6444          * This is no longer needed and is deprecated. Places a simple wrapper around an event handler to override the browser event
6445          * object with a Roo.EventObject
6446          * @param {Function} fn        The method the event invokes
6447          * @param {Object}   scope    An object that becomes the scope of the handler
6448          * @param {boolean}  override If true, the obj passed in becomes
6449          *                             the execution scope of the listener
6450          * @return {Function} The wrapped function
6451          * @deprecated
6452          */
6453         wrap : function(fn, scope, override){
6454             return function(e){
6455                 Roo.EventObject.setEvent(e);
6456                 fn.call(override ? scope || window : window, Roo.EventObject, scope);
6457             };
6458         },
6459         
6460         /**
6461      * Appends an event handler to an element (shorthand for addListener)
6462      * @param {String/HTMLElement}   element        The html element or id to assign the
6463      * @param {String}   eventName The type of event to listen for
6464      * @param {Function} handler The method the event invokes
6465      * @param {Object}   scope (optional) The scope in which to execute the handler
6466      * function. The handler function's "this" context.
6467      * @param {Object}   options (optional) An object containing handler configuration
6468      * properties. This may contain any of the following properties:<ul>
6469      * <li>scope {Object} The scope in which to execute the handler function. The handler function's "this" context.</li>
6470      * <li>delegate {String} A simple selector to filter the target or look for a descendant of the target</li>
6471      * <li>stopEvent {Boolean} True to stop the event. That is stop propagation, and prevent the default action.</li>
6472      * <li>preventDefault {Boolean} True to prevent the default action</li>
6473      * <li>stopPropagation {Boolean} True to prevent event propagation</li>
6474      * <li>normalized {Boolean} False to pass a browser event to the handler function instead of an Roo.EventObject</li>
6475      * <li>delay {Number} The number of milliseconds to delay the invocation of the handler after te event fires.</li>
6476      * <li>single {Boolean} True to add a handler to handle just the next firing of the event, and then remove itself.</li>
6477      * <li>buffer {Number} Causes the handler to be scheduled to run in an {@link Roo.util.DelayedTask} delayed
6478      * by the specified number of milliseconds. If the event fires again within that time, the original
6479      * handler is <em>not</em> invoked, but the new handler is scheduled in its place.</li>
6480      * </ul><br>
6481      * <p>
6482      * <b>Combining Options</b><br>
6483      * Using the options argument, it is possible to combine different types of listeners:<br>
6484      * <br>
6485      * A normalized, delayed, one-time listener that auto stops the event and passes a custom argument (forumId)<div style="margin: 5px 20px 20px;">
6486      * Code:<pre><code>
6487 el.on('click', this.onClick, this, {
6488     single: true,
6489     delay: 100,
6490     stopEvent : true,
6491     forumId: 4
6492 });</code></pre>
6493      * <p>
6494      * <b>Attaching multiple handlers in 1 call</b><br>
6495       * The method also allows for a single argument to be passed which is a config object containing properties
6496      * which specify multiple handlers.
6497      * <p>
6498      * Code:<pre><code>
6499 el.on({
6500     'click' : {
6501         fn: this.onClick
6502         scope: this,
6503         delay: 100
6504     },
6505     'mouseover' : {
6506         fn: this.onMouseOver
6507         scope: this
6508     },
6509     'mouseout' : {
6510         fn: this.onMouseOut
6511         scope: this
6512     }
6513 });</code></pre>
6514      * <p>
6515      * Or a shorthand syntax:<br>
6516      * Code:<pre><code>
6517 el.on({
6518     'click' : this.onClick,
6519     'mouseover' : this.onMouseOver,
6520     'mouseout' : this.onMouseOut
6521     scope: this
6522 });</code></pre>
6523      */
6524         addListener : function(element, eventName, fn, scope, options){
6525             if(typeof eventName == "object"){
6526                 var o = eventName;
6527                 for(var e in o){
6528                     if(propRe.test(e)){
6529                         continue;
6530                     }
6531                     if(typeof o[e] == "function"){
6532                         // shared options
6533                         listen(element, e, o, o[e], o.scope);
6534                     }else{
6535                         // individual options
6536                         listen(element, e, o[e]);
6537                     }
6538                 }
6539                 return;
6540             }
6541             return listen(element, eventName, options, fn, scope);
6542         },
6543         
6544         /**
6545          * Removes an event handler
6546          *
6547          * @param {String/HTMLElement}   element        The id or html element to remove the 
6548          *                             event from
6549          * @param {String}   eventName     The type of event
6550          * @param {Function} fn
6551          * @return {Boolean} True if a listener was actually removed
6552          */
6553         removeListener : function(element, eventName, fn){
6554             return stopListening(element, eventName, fn);
6555         },
6556         
6557         /**
6558          * Fires when the document is ready (before onload and before images are loaded). Can be 
6559          * accessed shorthanded Roo.onReady().
6560          * @param {Function} fn        The method the event invokes
6561          * @param {Object}   scope    An  object that becomes the scope of the handler
6562          * @param {boolean}  options
6563          */
6564         onDocumentReady : function(fn, scope, options){
6565             if(docReadyState){ // if it already fired
6566                 docReadyEvent.addListener(fn, scope, options);
6567                 docReadyEvent.fire();
6568                 docReadyEvent.clearListeners();
6569                 return;
6570             }
6571             if(!docReadyEvent){
6572                 initDocReady();
6573             }
6574             docReadyEvent.addListener(fn, scope, options);
6575         },
6576         
6577         /**
6578          * Fires when the window is resized and provides resize event buffering (50 milliseconds), passes new viewport width and height to handlers.
6579          * @param {Function} fn        The method the event invokes
6580          * @param {Object}   scope    An object that becomes the scope of the handler
6581          * @param {boolean}  options
6582          */
6583         onWindowResize : function(fn, scope, options){
6584             if(!resizeEvent){
6585                 resizeEvent = new Roo.util.Event();
6586                 resizeTask = new Roo.util.DelayedTask(function(){
6587                     resizeEvent.fire(D.getViewWidth(), D.getViewHeight());
6588                 });
6589                 E.on(window, "resize", function(){
6590                     if(Roo.isIE){
6591                         resizeTask.delay(50);
6592                     }else{
6593                         resizeEvent.fire(D.getViewWidth(), D.getViewHeight());
6594                     }
6595                 });
6596             }
6597             resizeEvent.addListener(fn, scope, options);
6598         },
6599
6600         /**
6601          * Fires when the user changes the active text size. Handler gets called with 2 params, the old size and the new size.
6602          * @param {Function} fn        The method the event invokes
6603          * @param {Object}   scope    An object that becomes the scope of the handler
6604          * @param {boolean}  options
6605          */
6606         onTextResize : function(fn, scope, options){
6607             if(!textEvent){
6608                 textEvent = new Roo.util.Event();
6609                 var textEl = new Roo.Element(document.createElement('div'));
6610                 textEl.dom.className = 'x-text-resize';
6611                 textEl.dom.innerHTML = 'X';
6612                 textEl.appendTo(document.body);
6613                 textSize = textEl.dom.offsetHeight;
6614                 setInterval(function(){
6615                     if(textEl.dom.offsetHeight != textSize){
6616                         textEvent.fire(textSize, textSize = textEl.dom.offsetHeight);
6617                     }
6618                 }, this.textResizeInterval);
6619             }
6620             textEvent.addListener(fn, scope, options);
6621         },
6622
6623         /**
6624          * Removes the passed window resize listener.
6625          * @param {Function} fn        The method the event invokes
6626          * @param {Object}   scope    The scope of handler
6627          */
6628         removeResizeListener : function(fn, scope){
6629             if(resizeEvent){
6630                 resizeEvent.removeListener(fn, scope);
6631             }
6632         },
6633
6634         // private
6635         fireResize : function(){
6636             if(resizeEvent){
6637                 resizeEvent.fire(D.getViewWidth(), D.getViewHeight());
6638             }   
6639         },
6640         /**
6641          * Url used for onDocumentReady with using SSL (defaults to Roo.SSL_SECURE_URL)
6642          */
6643         ieDeferSrc : false,
6644         /**
6645          * The frequency, in milliseconds, to check for text resize events (defaults to 50)
6646          */
6647         textResizeInterval : 50
6648     };
6649     
6650     /**
6651      * Fix for doc tools
6652      * @scopeAlias pub=Roo.EventManager
6653      */
6654     
6655      /**
6656      * Appends an event handler to an element (shorthand for addListener)
6657      * @param {String/HTMLElement}   element        The html element or id to assign the
6658      * @param {String}   eventName The type of event to listen for
6659      * @param {Function} handler The method the event invokes
6660      * @param {Object}   scope (optional) The scope in which to execute the handler
6661      * function. The handler function's "this" context.
6662      * @param {Object}   options (optional) An object containing handler configuration
6663      * properties. This may contain any of the following properties:<ul>
6664      * <li>scope {Object} The scope in which to execute the handler function. The handler function's "this" context.</li>
6665      * <li>delegate {String} A simple selector to filter the target or look for a descendant of the target</li>
6666      * <li>stopEvent {Boolean} True to stop the event. That is stop propagation, and prevent the default action.</li>
6667      * <li>preventDefault {Boolean} True to prevent the default action</li>
6668      * <li>stopPropagation {Boolean} True to prevent event propagation</li>
6669      * <li>normalized {Boolean} False to pass a browser event to the handler function instead of an Roo.EventObject</li>
6670      * <li>delay {Number} The number of milliseconds to delay the invocation of the handler after te event fires.</li>
6671      * <li>single {Boolean} True to add a handler to handle just the next firing of the event, and then remove itself.</li>
6672      * <li>buffer {Number} Causes the handler to be scheduled to run in an {@link Roo.util.DelayedTask} delayed
6673      * by the specified number of milliseconds. If the event fires again within that time, the original
6674      * handler is <em>not</em> invoked, but the new handler is scheduled in its place.</li>
6675      * </ul><br>
6676      * <p>
6677      * <b>Combining Options</b><br>
6678      * Using the options argument, it is possible to combine different types of listeners:<br>
6679      * <br>
6680      * A normalized, delayed, one-time listener that auto stops the event and passes a custom argument (forumId)<div style="margin: 5px 20px 20px;">
6681      * Code:<pre><code>
6682 el.on('click', this.onClick, this, {
6683     single: true,
6684     delay: 100,
6685     stopEvent : true,
6686     forumId: 4
6687 });</code></pre>
6688      * <p>
6689      * <b>Attaching multiple handlers in 1 call</b><br>
6690       * The method also allows for a single argument to be passed which is a config object containing properties
6691      * which specify multiple handlers.
6692      * <p>
6693      * Code:<pre><code>
6694 el.on({
6695     'click' : {
6696         fn: this.onClick
6697         scope: this,
6698         delay: 100
6699     },
6700     'mouseover' : {
6701         fn: this.onMouseOver
6702         scope: this
6703     },
6704     'mouseout' : {
6705         fn: this.onMouseOut
6706         scope: this
6707     }
6708 });</code></pre>
6709      * <p>
6710      * Or a shorthand syntax:<br>
6711      * Code:<pre><code>
6712 el.on({
6713     'click' : this.onClick,
6714     'mouseover' : this.onMouseOver,
6715     'mouseout' : this.onMouseOut
6716     scope: this
6717 });</code></pre>
6718      */
6719     pub.on = pub.addListener;
6720     pub.un = pub.removeListener;
6721
6722     pub.stoppedMouseDownEvent = new Roo.util.Event();
6723     return pub;
6724 }();
6725 /**
6726   * Fires when the document is ready (before onload and before images are loaded).  Shorthand of {@link Roo.EventManager#onDocumentReady}.
6727   * @param {Function} fn        The method the event invokes
6728   * @param {Object}   scope    An  object that becomes the scope of the handler
6729   * @param {boolean}  override If true, the obj passed in becomes
6730   *                             the execution scope of the listener
6731   * @member Roo
6732   * @method onReady
6733  */
6734 Roo.onReady = Roo.EventManager.onDocumentReady;
6735
6736 Roo.onReady(function(){
6737     var bd = Roo.get(document.body);
6738     if(!bd){ return; }
6739
6740     var cls = [
6741             Roo.isIE ? "roo-ie"
6742             : Roo.isIE11 ? "roo-ie11"
6743             : Roo.isEdge ? "roo-edge"
6744             : Roo.isGecko ? "roo-gecko"
6745             : Roo.isOpera ? "roo-opera"
6746             : Roo.isSafari ? "roo-safari" : ""];
6747
6748     if(Roo.isMac){
6749         cls.push("roo-mac");
6750     }
6751     if(Roo.isLinux){
6752         cls.push("roo-linux");
6753     }
6754     if(Roo.isIOS){
6755         cls.push("roo-ios");
6756     }
6757     if(Roo.isTouch){
6758         cls.push("roo-touch");
6759     }
6760     if(Roo.isBorderBox){
6761         cls.push('roo-border-box');
6762     }
6763     if(Roo.isStrict){ // add to the parent to allow for selectors like ".ext-strict .ext-ie"
6764         var p = bd.dom.parentNode;
6765         if(p){
6766             p.className += ' roo-strict';
6767         }
6768     }
6769     bd.addClass(cls.join(' '));
6770 });
6771
6772 /**
6773  * @class Roo.EventObject
6774  * EventObject exposes the Yahoo! UI Event functionality directly on the object
6775  * passed to your event handler. It exists mostly for convenience. It also fixes the annoying null checks automatically to cleanup your code 
6776  * Example:
6777  * <pre><code>
6778  function handleClick(e){ // e is not a standard event object, it is a Roo.EventObject
6779     e.preventDefault();
6780     var target = e.getTarget();
6781     ...
6782  }
6783  var myDiv = Roo.get("myDiv");
6784  myDiv.on("click", handleClick);
6785  //or
6786  Roo.EventManager.on("myDiv", 'click', handleClick);
6787  Roo.EventManager.addListener("myDiv", 'click', handleClick);
6788  </code></pre>
6789  * @singleton
6790  */
6791 Roo.EventObject = function(){
6792     
6793     var E = Roo.lib.Event;
6794     
6795     // safari keypress events for special keys return bad keycodes
6796     var safariKeys = {
6797         63234 : 37, // left
6798         63235 : 39, // right
6799         63232 : 38, // up
6800         63233 : 40, // down
6801         63276 : 33, // page up
6802         63277 : 34, // page down
6803         63272 : 46, // delete
6804         63273 : 36, // home
6805         63275 : 35  // end
6806     };
6807
6808     // normalize button clicks
6809     var btnMap = Roo.isIE ? {1:0,4:1,2:2} :
6810                 (Roo.isSafari ? {1:0,2:1,3:2} : {0:0,1:1,2:2});
6811
6812     Roo.EventObjectImpl = function(e){
6813         if(e){
6814             this.setEvent(e.browserEvent || e);
6815         }
6816     };
6817     Roo.EventObjectImpl.prototype = {
6818         /**
6819          * Used to fix doc tools.
6820          * @scope Roo.EventObject.prototype
6821          */
6822             
6823
6824         
6825         
6826         /** The normal browser event */
6827         browserEvent : null,
6828         /** The button pressed in a mouse event */
6829         button : -1,
6830         /** True if the shift key was down during the event */
6831         shiftKey : false,
6832         /** True if the control key was down during the event */
6833         ctrlKey : false,
6834         /** True if the alt key was down during the event */
6835         altKey : false,
6836
6837         /** Key constant 
6838         * @type Number */
6839         BACKSPACE : 8,
6840         /** Key constant 
6841         * @type Number */
6842         TAB : 9,
6843         /** Key constant 
6844         * @type Number */
6845         RETURN : 13,
6846         /** Key constant 
6847         * @type Number */
6848         ENTER : 13,
6849         /** Key constant 
6850         * @type Number */
6851         SHIFT : 16,
6852         /** Key constant 
6853         * @type Number */
6854         CONTROL : 17,
6855         /** Key constant 
6856         * @type Number */
6857         ESC : 27,
6858         /** Key constant 
6859         * @type Number */
6860         SPACE : 32,
6861         /** Key constant 
6862         * @type Number */
6863         PAGEUP : 33,
6864         /** Key constant 
6865         * @type Number */
6866         PAGEDOWN : 34,
6867         /** Key constant 
6868         * @type Number */
6869         END : 35,
6870         /** Key constant 
6871         * @type Number */
6872         HOME : 36,
6873         /** Key constant 
6874         * @type Number */
6875         LEFT : 37,
6876         /** Key constant 
6877         * @type Number */
6878         UP : 38,
6879         /** Key constant 
6880         * @type Number */
6881         RIGHT : 39,
6882         /** Key constant 
6883         * @type Number */
6884         DOWN : 40,
6885         /** Key constant 
6886         * @type Number */
6887         DELETE : 46,
6888         /** Key constant 
6889         * @type Number */
6890         F5 : 116,
6891
6892            /** @private */
6893         setEvent : function(e){
6894             if(e == this || (e && e.browserEvent)){ // already wrapped
6895                 return e;
6896             }
6897             this.browserEvent = e;
6898             if(e){
6899                 // normalize buttons
6900                 this.button = e.button ? btnMap[e.button] : (e.which ? e.which-1 : -1);
6901                 if(e.type == 'click' && this.button == -1){
6902                     this.button = 0;
6903                 }
6904                 this.type = e.type;
6905                 this.shiftKey = e.shiftKey;
6906                 // mac metaKey behaves like ctrlKey
6907                 this.ctrlKey = e.ctrlKey || e.metaKey;
6908                 this.altKey = e.altKey;
6909                 // in getKey these will be normalized for the mac
6910                 this.keyCode = e.keyCode;
6911                 // keyup warnings on firefox.
6912                 this.charCode = (e.type == 'keyup' || e.type == 'keydown') ? 0 : e.charCode;
6913                 // cache the target for the delayed and or buffered events
6914                 this.target = E.getTarget(e);
6915                 // same for XY
6916                 this.xy = E.getXY(e);
6917             }else{
6918                 this.button = -1;
6919                 this.shiftKey = false;
6920                 this.ctrlKey = false;
6921                 this.altKey = false;
6922                 this.keyCode = 0;
6923                 this.charCode =0;
6924                 this.target = null;
6925                 this.xy = [0, 0];
6926             }
6927             return this;
6928         },
6929
6930         /**
6931          * Stop the event (preventDefault and stopPropagation)
6932          */
6933         stopEvent : function(){
6934             if(this.browserEvent){
6935                 if(this.browserEvent.type == 'mousedown'){
6936                     Roo.EventManager.stoppedMouseDownEvent.fire(this);
6937                 }
6938                 E.stopEvent(this.browserEvent);
6939             }
6940         },
6941
6942         /**
6943          * Prevents the browsers default handling of the event.
6944          */
6945         preventDefault : function(){
6946             if(this.browserEvent){
6947                 E.preventDefault(this.browserEvent);
6948             }
6949         },
6950
6951         /** @private */
6952         isNavKeyPress : function(){
6953             var k = this.keyCode;
6954             k = Roo.isSafari ? (safariKeys[k] || k) : k;
6955             return (k >= 33 && k <= 40) || k == this.RETURN || k == this.TAB || k == this.ESC;
6956         },
6957
6958         isSpecialKey : function(){
6959             var k = this.keyCode;
6960             return (this.type == 'keypress' && this.ctrlKey) || k == 9 || k == 13  || k == 40 || k == 27 ||
6961             (k == 16) || (k == 17) ||
6962             (k >= 18 && k <= 20) ||
6963             (k >= 33 && k <= 35) ||
6964             (k >= 36 && k <= 39) ||
6965             (k >= 44 && k <= 45);
6966         },
6967         /**
6968          * Cancels bubbling of the event.
6969          */
6970         stopPropagation : function(){
6971             if(this.browserEvent){
6972                 if(this.type == 'mousedown'){
6973                     Roo.EventManager.stoppedMouseDownEvent.fire(this);
6974                 }
6975                 E.stopPropagation(this.browserEvent);
6976             }
6977         },
6978
6979         /**
6980          * Gets the key code for the event.
6981          * @return {Number}
6982          */
6983         getCharCode : function(){
6984             return this.charCode || this.keyCode;
6985         },
6986
6987         /**
6988          * Returns a normalized keyCode for the event.
6989          * @return {Number} The key code
6990          */
6991         getKey : function(){
6992             var k = this.keyCode || this.charCode;
6993             return Roo.isSafari ? (safariKeys[k] || k) : k;
6994         },
6995
6996         /**
6997          * Gets the x coordinate of the event.
6998          * @return {Number}
6999          */
7000         getPageX : function(){
7001             return this.xy[0];
7002         },
7003
7004         /**
7005          * Gets the y coordinate of the event.
7006          * @return {Number}
7007          */
7008         getPageY : function(){
7009             return this.xy[1];
7010         },
7011
7012         /**
7013          * Gets the time of the event.
7014          * @return {Number}
7015          */
7016         getTime : function(){
7017             if(this.browserEvent){
7018                 return E.getTime(this.browserEvent);
7019             }
7020             return null;
7021         },
7022
7023         /**
7024          * Gets the page coordinates of the event.
7025          * @return {Array} The xy values like [x, y]
7026          */
7027         getXY : function(){
7028             return this.xy;
7029         },
7030
7031         /**
7032          * Gets the target for the event.
7033          * @param {String} selector (optional) A simple selector to filter the target or look for an ancestor of the target
7034          * @param {Number/String/HTMLElement/Element} maxDepth (optional) The max depth to
7035                 search as a number or element (defaults to 10 || document.body)
7036          * @param {Boolean} returnEl (optional) True to return a Roo.Element object instead of DOM node
7037          * @return {HTMLelement}
7038          */
7039         getTarget : function(selector, maxDepth, returnEl){
7040             return selector ? Roo.fly(this.target).findParent(selector, maxDepth, returnEl) : this.target;
7041         },
7042         /**
7043          * Gets the related target.
7044          * @return {HTMLElement}
7045          */
7046         getRelatedTarget : function(){
7047             if(this.browserEvent){
7048                 return E.getRelatedTarget(this.browserEvent);
7049             }
7050             return null;
7051         },
7052
7053         /**
7054          * Normalizes mouse wheel delta across browsers
7055          * @return {Number} The delta
7056          */
7057         getWheelDelta : function(){
7058             var e = this.browserEvent;
7059             var delta = 0;
7060             if(e.wheelDelta){ /* IE/Opera. */
7061                 delta = e.wheelDelta/120;
7062             }else if(e.detail){ /* Mozilla case. */
7063                 delta = -e.detail/3;
7064             }
7065             return delta;
7066         },
7067
7068         /**
7069          * Returns true if the control, meta, shift or alt key was pressed during this event.
7070          * @return {Boolean}
7071          */
7072         hasModifier : function(){
7073             return !!((this.ctrlKey || this.altKey) || this.shiftKey);
7074         },
7075
7076         /**
7077          * Returns true if the target of this event equals el or is a child of el
7078          * @param {String/HTMLElement/Element} el
7079          * @param {Boolean} related (optional) true to test if the related target is within el instead of the target
7080          * @return {Boolean}
7081          */
7082         within : function(el, related){
7083             var t = this[related ? "getRelatedTarget" : "getTarget"]();
7084             return t && Roo.fly(el).contains(t);
7085         },
7086
7087         getPoint : function(){
7088             return new Roo.lib.Point(this.xy[0], this.xy[1]);
7089         }
7090     };
7091
7092     return new Roo.EventObjectImpl();
7093 }();
7094             
7095     /*
7096  * Based on:
7097  * Ext JS Library 1.1.1
7098  * Copyright(c) 2006-2007, Ext JS, LLC.
7099  *
7100  * Originally Released Under LGPL - original licence link has changed is not relivant.
7101  *
7102  * Fork - LGPL
7103  * <script type="text/javascript">
7104  */
7105
7106  
7107 // was in Composite Element!??!?!
7108  
7109 (function(){
7110     var D = Roo.lib.Dom;
7111     var E = Roo.lib.Event;
7112     var A = Roo.lib.Anim;
7113
7114     // local style camelizing for speed
7115     var propCache = {};
7116     var camelRe = /(-[a-z])/gi;
7117     var camelFn = function(m, a){ return a.charAt(1).toUpperCase(); };
7118     var view = document.defaultView;
7119
7120 /**
7121  * @class Roo.Element
7122  * Represents an Element in the DOM.<br><br>
7123  * Usage:<br>
7124 <pre><code>
7125 var el = Roo.get("my-div");
7126
7127 // or with getEl
7128 var el = getEl("my-div");
7129
7130 // or with a DOM element
7131 var el = Roo.get(myDivElement);
7132 </code></pre>
7133  * Using Roo.get() or getEl() instead of calling the constructor directly ensures you get the same object
7134  * each call instead of constructing a new one.<br><br>
7135  * <b>Animations</b><br />
7136  * Many of the functions for manipulating an element have an optional "animate" parameter. The animate parameter
7137  * should either be a boolean (true) or an object literal with animation options. The animation options are:
7138 <pre>
7139 Option    Default   Description
7140 --------- --------  ---------------------------------------------
7141 duration  .35       The duration of the animation in seconds
7142 easing    easeOut   The YUI easing method
7143 callback  none      A function to execute when the anim completes
7144 scope     this      The scope (this) of the callback function
7145 </pre>
7146 * Also, the Anim object being used for the animation will be set on your options object as "anim", which allows you to stop or
7147 * manipulate the animation. Here's an example:
7148 <pre><code>
7149 var el = Roo.get("my-div");
7150
7151 // no animation
7152 el.setWidth(100);
7153
7154 // default animation
7155 el.setWidth(100, true);
7156
7157 // animation with some options set
7158 el.setWidth(100, {
7159     duration: 1,
7160     callback: this.foo,
7161     scope: this
7162 });
7163
7164 // using the "anim" property to get the Anim object
7165 var opt = {
7166     duration: 1,
7167     callback: this.foo,
7168     scope: this
7169 };
7170 el.setWidth(100, opt);
7171 ...
7172 if(opt.anim.isAnimated()){
7173     opt.anim.stop();
7174 }
7175 </code></pre>
7176 * <b> Composite (Collections of) Elements</b><br />
7177  * For working with collections of Elements, see <a href="Roo.CompositeElement.html">Roo.CompositeElement</a>
7178  * @constructor Create a new Element directly.
7179  * @param {String/HTMLElement} element
7180  * @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).
7181  */
7182     Roo.Element = function(element, forceNew)
7183     {
7184         var dom = typeof element == "string" ?
7185                 document.getElementById(element) : element;
7186         
7187         this.listeners = {};
7188         
7189         if(!dom){ // invalid id/element
7190             return null;
7191         }
7192         var id = dom.id;
7193         if(forceNew !== true && id && Roo.Element.cache[id]){ // element object already exists
7194             return Roo.Element.cache[id];
7195         }
7196
7197         /**
7198          * The DOM element
7199          * @type HTMLElement
7200          */
7201         this.dom = dom;
7202
7203         /**
7204          * The DOM element ID
7205          * @type String
7206          */
7207         this.id = id || Roo.id(dom);
7208         
7209         return this; // assumed for cctor?
7210     };
7211
7212     var El = Roo.Element;
7213
7214     El.prototype = {
7215         /**
7216          * The element's default display mode  (defaults to "") 
7217          * @type String
7218          */
7219         originalDisplay : "",
7220
7221         
7222         // note this is overridden in BS version..
7223         visibilityMode : 1, 
7224         /**
7225          * The default unit to append to CSS values where a unit isn't provided (defaults to px).
7226          * @type String
7227          */
7228         defaultUnit : "px",
7229         
7230         /**
7231          * Sets the element's visibility mode. When setVisible() is called it
7232          * will use this to determine whether to set the visibility or the display property.
7233          * @param visMode Element.VISIBILITY or Element.DISPLAY
7234          * @return {Roo.Element} this
7235          */
7236         setVisibilityMode : function(visMode){
7237             this.visibilityMode = visMode;
7238             return this;
7239         },
7240         /**
7241          * Convenience method for setVisibilityMode(Element.DISPLAY)
7242          * @param {String} display (optional) What to set display to when visible
7243          * @return {Roo.Element} this
7244          */
7245         enableDisplayMode : function(display){
7246             this.setVisibilityMode(El.DISPLAY);
7247             if(typeof display != "undefined") { this.originalDisplay = display; }
7248             return this;
7249         },
7250
7251         /**
7252          * 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)
7253          * @param {String} selector The simple selector to test
7254          * @param {Number/String/HTMLElement/Element} maxDepth (optional) The max depth to
7255                 search as a number or element (defaults to 10 || document.body)
7256          * @param {Boolean} returnEl (optional) True to return a Roo.Element object instead of DOM node
7257          * @return {HTMLElement} The matching DOM node (or null if no match was found)
7258          */
7259         findParent : function(simpleSelector, maxDepth, returnEl){
7260             var p = this.dom, b = document.body, depth = 0, dq = Roo.DomQuery, stopEl;
7261             maxDepth = maxDepth || 50;
7262             if(typeof maxDepth != "number"){
7263                 stopEl = Roo.getDom(maxDepth);
7264                 maxDepth = 10;
7265             }
7266             while(p && p.nodeType == 1 && depth < maxDepth && p != b && p != stopEl){
7267                 if(dq.is(p, simpleSelector)){
7268                     return returnEl ? Roo.get(p) : p;
7269                 }
7270                 depth++;
7271                 p = p.parentNode;
7272             }
7273             return null;
7274         },
7275
7276
7277         /**
7278          * Looks at parent nodes for a match of the passed simple selector (e.g. div.some-class or span:first-child)
7279          * @param {String} selector The simple selector to test
7280          * @param {Number/String/HTMLElement/Element} maxDepth (optional) The max depth to
7281                 search as a number or element (defaults to 10 || document.body)
7282          * @param {Boolean} returnEl (optional) True to return a Roo.Element object instead of DOM node
7283          * @return {HTMLElement} The matching DOM node (or null if no match was found)
7284          */
7285         findParentNode : function(simpleSelector, maxDepth, returnEl){
7286             var p = Roo.fly(this.dom.parentNode, '_internal');
7287             return p ? p.findParent(simpleSelector, maxDepth, returnEl) : null;
7288         },
7289         
7290         /**
7291          * Looks at  the scrollable parent element
7292          */
7293         findScrollableParent : function()
7294         {
7295             var overflowRegex = /(auto|scroll)/;
7296             
7297             if(this.getStyle('position') === 'fixed'){
7298                 return Roo.isAndroid ? Roo.get(document.documentElement) : Roo.get(document.body);
7299             }
7300             
7301             var excludeStaticParent = this.getStyle('position') === "absolute";
7302             
7303             for (var parent = this; (parent = Roo.get(parent.dom.parentNode));){
7304                 
7305                 if (excludeStaticParent && parent.getStyle('position') === "static") {
7306                     continue;
7307                 }
7308                 
7309                 if (overflowRegex.test(parent.getStyle('overflow') + parent.getStyle('overflow-x') + parent.getStyle('overflow-y'))){
7310                     return parent;
7311                 }
7312                 
7313                 if(parent.dom.nodeName.toLowerCase() == 'body'){
7314                     return Roo.isAndroid ? Roo.get(document.documentElement) : Roo.get(document.body);
7315                 }
7316             }
7317             
7318             return Roo.isAndroid ? Roo.get(document.documentElement) : Roo.get(document.body);
7319         },
7320
7321         /**
7322          * Walks up the dom looking for a parent node that matches the passed simple selector (e.g. div.some-class or span:first-child).
7323          * This is a shortcut for findParentNode() that always returns an Roo.Element.
7324          * @param {String} selector The simple selector to test
7325          * @param {Number/String/HTMLElement/Element} maxDepth (optional) The max depth to
7326                 search as a number or element (defaults to 10 || document.body)
7327          * @return {Roo.Element} The matching DOM node (or null if no match was found)
7328          */
7329         up : function(simpleSelector, maxDepth){
7330             return this.findParentNode(simpleSelector, maxDepth, true);
7331         },
7332
7333
7334
7335         /**
7336          * Returns true if this element matches the passed simple selector (e.g. div.some-class or span:first-child)
7337          * @param {String} selector The simple selector to test
7338          * @return {Boolean} True if this element matches the selector, else false
7339          */
7340         is : function(simpleSelector){
7341             return Roo.DomQuery.is(this.dom, simpleSelector);
7342         },
7343
7344         /**
7345          * Perform animation on this element.
7346          * @param {Object} args The YUI animation control args
7347          * @param {Float} duration (optional) How long the animation lasts in seconds (defaults to .35)
7348          * @param {Function} onComplete (optional) Function to call when animation completes
7349          * @param {String} easing (optional) Easing method to use (defaults to 'easeOut')
7350          * @param {String} animType (optional) 'run' is the default. Can also be 'color', 'motion', or 'scroll'
7351          * @return {Roo.Element} this
7352          */
7353         animate : function(args, duration, onComplete, easing, animType){
7354             this.anim(args, {duration: duration, callback: onComplete, easing: easing}, animType);
7355             return this;
7356         },
7357
7358         /*
7359          * @private Internal animation call
7360          */
7361         anim : function(args, opt, animType, defaultDur, defaultEase, cb){
7362             animType = animType || 'run';
7363             opt = opt || {};
7364             var anim = Roo.lib.Anim[animType](
7365                 this.dom, args,
7366                 (opt.duration || defaultDur) || .35,
7367                 (opt.easing || defaultEase) || 'easeOut',
7368                 function(){
7369                     Roo.callback(cb, this);
7370                     Roo.callback(opt.callback, opt.scope || this, [this, opt]);
7371                 },
7372                 this
7373             );
7374             opt.anim = anim;
7375             return anim;
7376         },
7377
7378         // private legacy anim prep
7379         preanim : function(a, i){
7380             return !a[i] ? false : (typeof a[i] == "object" ? a[i]: {duration: a[i+1], callback: a[i+2], easing: a[i+3]});
7381         },
7382
7383         /**
7384          * Removes worthless text nodes
7385          * @param {Boolean} forceReclean (optional) By default the element
7386          * keeps track if it has been cleaned already so
7387          * you can call this over and over. However, if you update the element and
7388          * need to force a reclean, you can pass true.
7389          */
7390         clean : function(forceReclean){
7391             if(this.isCleaned && forceReclean !== true){
7392                 return this;
7393             }
7394             var ns = /\S/;
7395             var d = this.dom, n = d.firstChild, ni = -1;
7396             while(n){
7397                 var nx = n.nextSibling;
7398                 if(n.nodeType == 3 && !ns.test(n.nodeValue)){
7399                     d.removeChild(n);
7400                 }else{
7401                     n.nodeIndex = ++ni;
7402                 }
7403                 n = nx;
7404             }
7405             this.isCleaned = true;
7406             return this;
7407         },
7408
7409         // private
7410         calcOffsetsTo : function(el){
7411             el = Roo.get(el);
7412             var d = el.dom;
7413             var restorePos = false;
7414             if(el.getStyle('position') == 'static'){
7415                 el.position('relative');
7416                 restorePos = true;
7417             }
7418             var x = 0, y =0;
7419             var op = this.dom;
7420             while(op && op != d && op.tagName != 'HTML'){
7421                 x+= op.offsetLeft;
7422                 y+= op.offsetTop;
7423                 op = op.offsetParent;
7424             }
7425             if(restorePos){
7426                 el.position('static');
7427             }
7428             return [x, y];
7429         },
7430
7431         /**
7432          * Scrolls this element into view within the passed container.
7433          * @param {String/HTMLElement/Element} container (optional) The container element to scroll (defaults to document.body)
7434          * @param {Boolean} hscroll (optional) False to disable horizontal scroll (defaults to true)
7435          * @return {Roo.Element} this
7436          */
7437         scrollIntoView : function(container, hscroll){
7438             var c = Roo.getDom(container) || document.body;
7439             var el = this.dom;
7440
7441             var o = this.calcOffsetsTo(c),
7442                 l = o[0],
7443                 t = o[1],
7444                 b = t+el.offsetHeight,
7445                 r = l+el.offsetWidth;
7446
7447             var ch = c.clientHeight;
7448             var ct = parseInt(c.scrollTop, 10);
7449             var cl = parseInt(c.scrollLeft, 10);
7450             var cb = ct + ch;
7451             var cr = cl + c.clientWidth;
7452
7453             if(t < ct){
7454                 c.scrollTop = t;
7455             }else if(b > cb){
7456                 c.scrollTop = b-ch;
7457             }
7458
7459             if(hscroll !== false){
7460                 if(l < cl){
7461                     c.scrollLeft = l;
7462                 }else if(r > cr){
7463                     c.scrollLeft = r-c.clientWidth;
7464                 }
7465             }
7466             return this;
7467         },
7468
7469         // private
7470         scrollChildIntoView : function(child, hscroll){
7471             Roo.fly(child, '_scrollChildIntoView').scrollIntoView(this, hscroll);
7472         },
7473
7474         /**
7475          * Measures the element's content height and updates height to match. Note: this function uses setTimeout so
7476          * the new height may not be available immediately.
7477          * @param {Boolean} animate (optional) Animate the transition (defaults to false)
7478          * @param {Float} duration (optional) Length of the animation in seconds (defaults to .35)
7479          * @param {Function} onComplete (optional) Function to call when animation completes
7480          * @param {String} easing (optional) Easing method to use (defaults to easeOut)
7481          * @return {Roo.Element} this
7482          */
7483         autoHeight : function(animate, duration, onComplete, easing){
7484             var oldHeight = this.getHeight();
7485             this.clip();
7486             this.setHeight(1); // force clipping
7487             setTimeout(function(){
7488                 var height = parseInt(this.dom.scrollHeight, 10); // parseInt for Safari
7489                 if(!animate){
7490                     this.setHeight(height);
7491                     this.unclip();
7492                     if(typeof onComplete == "function"){
7493                         onComplete();
7494                     }
7495                 }else{
7496                     this.setHeight(oldHeight); // restore original height
7497                     this.setHeight(height, animate, duration, function(){
7498                         this.unclip();
7499                         if(typeof onComplete == "function") { onComplete(); }
7500                     }.createDelegate(this), easing);
7501                 }
7502             }.createDelegate(this), 0);
7503             return this;
7504         },
7505
7506         /**
7507          * Returns true if this element is an ancestor of the passed element
7508          * @param {HTMLElement/String} el The element to check
7509          * @return {Boolean} True if this element is an ancestor of el, else false
7510          */
7511         contains : function(el){
7512             if(!el){return false;}
7513             return D.isAncestor(this.dom, el.dom ? el.dom : el);
7514         },
7515
7516         /**
7517          * Checks whether the element is currently visible using both visibility and display properties.
7518          * @param {Boolean} deep (optional) True to walk the dom and see if parent elements are hidden (defaults to false)
7519          * @return {Boolean} True if the element is currently visible, else false
7520          */
7521         isVisible : function(deep) {
7522             var vis = !(this.getStyle("visibility") == "hidden" || this.getStyle("display") == "none");
7523             if(deep !== true || !vis){
7524                 return vis;
7525             }
7526             var p = this.dom.parentNode;
7527             while(p && p.tagName.toLowerCase() != "body"){
7528                 if(!Roo.fly(p, '_isVisible').isVisible()){
7529                     return false;
7530                 }
7531                 p = p.parentNode;
7532             }
7533             return true;
7534         },
7535
7536         /**
7537          * Creates a {@link Roo.CompositeElement} for child nodes based on the passed CSS selector (the selector should not contain an id).
7538          * @param {String} selector The CSS selector
7539          * @param {Boolean} unique (optional) True to create a unique Roo.Element for each child (defaults to false, which creates a single shared flyweight object)
7540          * @return {CompositeElement/CompositeElementLite} The composite element
7541          */
7542         select : function(selector, unique){
7543             return El.select(selector, unique, this.dom);
7544         },
7545
7546         /**
7547          * Selects child nodes based on the passed CSS selector (the selector should not contain an id).
7548          * @param {String} selector The CSS selector
7549          * @return {Array} An array of the matched nodes
7550          */
7551         query : function(selector, unique){
7552             return Roo.DomQuery.select(selector, this.dom);
7553         },
7554
7555         /**
7556          * Selects a single child at any depth below this element based on the passed CSS selector (the selector should not contain an id).
7557          * @param {String} selector The CSS selector
7558          * @param {Boolean} returnDom (optional) True to return the DOM node instead of Roo.Element (defaults to false)
7559          * @return {HTMLElement/Roo.Element} The child Roo.Element (or DOM node if returnDom = true)
7560          */
7561         child : function(selector, returnDom){
7562             var n = Roo.DomQuery.selectNode(selector, this.dom);
7563             return returnDom ? n : Roo.get(n);
7564         },
7565
7566         /**
7567          * Selects a single *direct* child based on the passed CSS selector (the selector should not contain an id).
7568          * @param {String} selector The CSS selector
7569          * @param {Boolean} returnDom (optional) True to return the DOM node instead of Roo.Element (defaults to false)
7570          * @return {HTMLElement/Roo.Element} The child Roo.Element (or DOM node if returnDom = true)
7571          */
7572         down : function(selector, returnDom){
7573             var n = Roo.DomQuery.selectNode(" > " + selector, this.dom);
7574             return returnDom ? n : Roo.get(n);
7575         },
7576
7577         /**
7578          * Initializes a {@link Roo.dd.DD} drag drop object for this element.
7579          * @param {String} group The group the DD object is member of
7580          * @param {Object} config The DD config object
7581          * @param {Object} overrides An object containing methods to override/implement on the DD object
7582          * @return {Roo.dd.DD} The DD object
7583          */
7584         initDD : function(group, config, overrides){
7585             var dd = new Roo.dd.DD(Roo.id(this.dom), group, config);
7586             return Roo.apply(dd, overrides);
7587         },
7588
7589         /**
7590          * Initializes a {@link Roo.dd.DDProxy} object for this element.
7591          * @param {String} group The group the DDProxy object is member of
7592          * @param {Object} config The DDProxy config object
7593          * @param {Object} overrides An object containing methods to override/implement on the DDProxy object
7594          * @return {Roo.dd.DDProxy} The DDProxy object
7595          */
7596         initDDProxy : function(group, config, overrides){
7597             var dd = new Roo.dd.DDProxy(Roo.id(this.dom), group, config);
7598             return Roo.apply(dd, overrides);
7599         },
7600
7601         /**
7602          * Initializes a {@link Roo.dd.DDTarget} object for this element.
7603          * @param {String} group The group the DDTarget object is member of
7604          * @param {Object} config The DDTarget config object
7605          * @param {Object} overrides An object containing methods to override/implement on the DDTarget object
7606          * @return {Roo.dd.DDTarget} The DDTarget object
7607          */
7608         initDDTarget : function(group, config, overrides){
7609             var dd = new Roo.dd.DDTarget(Roo.id(this.dom), group, config);
7610             return Roo.apply(dd, overrides);
7611         },
7612
7613         /**
7614          * Sets the visibility of the element (see details). If the visibilityMode is set to Element.DISPLAY, it will use
7615          * the display property to hide the element, otherwise it uses visibility. The default is to hide and show using the visibility property.
7616          * @param {Boolean} visible Whether the element is visible
7617          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7618          * @return {Roo.Element} this
7619          */
7620          setVisible : function(visible, animate){
7621             if(!animate || !A){
7622                 if(this.visibilityMode == El.DISPLAY){
7623                     this.setDisplayed(visible);
7624                 }else{
7625                     this.fixDisplay();
7626                     this.dom.style.visibility = visible ? "visible" : "hidden";
7627                 }
7628             }else{
7629                 // closure for composites
7630                 var dom = this.dom;
7631                 var visMode = this.visibilityMode;
7632                 if(visible){
7633                     this.setOpacity(.01);
7634                     this.setVisible(true);
7635                 }
7636                 this.anim({opacity: { to: (visible?1:0) }},
7637                       this.preanim(arguments, 1),
7638                       null, .35, 'easeIn', function(){
7639                          if(!visible){
7640                              if(visMode == El.DISPLAY){
7641                                  dom.style.display = "none";
7642                              }else{
7643                                  dom.style.visibility = "hidden";
7644                              }
7645                              Roo.get(dom).setOpacity(1);
7646                          }
7647                      });
7648             }
7649             return this;
7650         },
7651
7652         /**
7653          * Returns true if display is not "none"
7654          * @return {Boolean}
7655          */
7656         isDisplayed : function() {
7657             return this.getStyle("display") != "none";
7658         },
7659
7660         /**
7661          * Toggles the element's visibility or display, depending on visibility mode.
7662          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7663          * @return {Roo.Element} this
7664          */
7665         toggle : function(animate){
7666             this.setVisible(!this.isVisible(), this.preanim(arguments, 0));
7667             return this;
7668         },
7669
7670         /**
7671          * Sets the CSS display property. Uses originalDisplay if the specified value is a boolean true.
7672          * @param {Boolean} value Boolean value to display the element using its default display, or a string to set the display directly
7673          * @return {Roo.Element} this
7674          */
7675         setDisplayed : function(value) {
7676             if(typeof value == "boolean"){
7677                value = value ? this.originalDisplay : "none";
7678             }
7679             this.setStyle("display", value);
7680             return this;
7681         },
7682
7683         /**
7684          * Tries to focus the element. Any exceptions are caught and ignored.
7685          * @return {Roo.Element} this
7686          */
7687         focus : function() {
7688             try{
7689                 this.dom.focus();
7690             }catch(e){}
7691             return this;
7692         },
7693
7694         /**
7695          * Tries to blur the element. Any exceptions are caught and ignored.
7696          * @return {Roo.Element} this
7697          */
7698         blur : function() {
7699             try{
7700                 this.dom.blur();
7701             }catch(e){}
7702             return this;
7703         },
7704
7705         /**
7706          * Adds one or more CSS classes to the element. Duplicate classes are automatically filtered out.
7707          * @param {String/Array} className The CSS class to add, or an array of classes
7708          * @return {Roo.Element} this
7709          */
7710         addClass : function(className){
7711             if(className instanceof Array){
7712                 for(var i = 0, len = className.length; i < len; i++) {
7713                     this.addClass(className[i]);
7714                 }
7715             }else{
7716                 if(className && !this.hasClass(className)){
7717                     if (this.dom instanceof SVGElement) {
7718                         this.dom.className.baseVal =this.dom.className.baseVal  + " " + className;
7719                     } else {
7720                         this.dom.className = this.dom.className + " " + className;
7721                     }
7722                 }
7723             }
7724             return this;
7725         },
7726
7727         /**
7728          * Adds one or more CSS classes to this element and removes the same class(es) from all siblings.
7729          * @param {String/Array} className The CSS class to add, or an array of classes
7730          * @return {Roo.Element} this
7731          */
7732         radioClass : function(className){
7733             var siblings = this.dom.parentNode.childNodes;
7734             for(var i = 0; i < siblings.length; i++) {
7735                 var s = siblings[i];
7736                 if(s.nodeType == 1){
7737                     Roo.get(s).removeClass(className);
7738                 }
7739             }
7740             this.addClass(className);
7741             return this;
7742         },
7743
7744         /**
7745          * Removes one or more CSS classes from the element.
7746          * @param {String/Array} className The CSS class to remove, or an array of classes
7747          * @return {Roo.Element} this
7748          */
7749         removeClass : function(className){
7750             
7751             var cn = this.dom instanceof SVGElement ? this.dom.className.baseVal : this.dom.className;
7752             if(!className || !cn){
7753                 return this;
7754             }
7755             if(className instanceof Array){
7756                 for(var i = 0, len = className.length; i < len; i++) {
7757                     this.removeClass(className[i]);
7758                 }
7759             }else{
7760                 if(this.hasClass(className)){
7761                     var re = this.classReCache[className];
7762                     if (!re) {
7763                        re = new RegExp('(?:^|\\s+)' + className + '(?:\\s+|$)', "g");
7764                        this.classReCache[className] = re;
7765                     }
7766                     if (this.dom instanceof SVGElement) {
7767                         this.dom.className.baseVal = cn.replace(re, " ");
7768                     } else {
7769                         this.dom.className = cn.replace(re, " ");
7770                     }
7771                 }
7772             }
7773             return this;
7774         },
7775
7776         // private
7777         classReCache: {},
7778
7779         /**
7780          * Toggles the specified CSS class on this element (removes it if it already exists, otherwise adds it).
7781          * @param {String} className The CSS class to toggle
7782          * @return {Roo.Element} this
7783          */
7784         toggleClass : function(className){
7785             if(this.hasClass(className)){
7786                 this.removeClass(className);
7787             }else{
7788                 this.addClass(className);
7789             }
7790             return this;
7791         },
7792
7793         /**
7794          * Checks if the specified CSS class exists on this element's DOM node.
7795          * @param {String} className The CSS class to check for
7796          * @return {Boolean} True if the class exists, else false
7797          */
7798         hasClass : function(className){
7799             if (this.dom instanceof SVGElement) {
7800                 return className && (' '+this.dom.className.baseVal +' ').indexOf(' '+className+' ') != -1; 
7801             } 
7802             return className && (' '+this.dom.className+' ').indexOf(' '+className+' ') != -1;
7803         },
7804
7805         /**
7806          * Replaces a CSS class on the element with another.  If the old name does not exist, the new name will simply be added.
7807          * @param {String} oldClassName The CSS class to replace
7808          * @param {String} newClassName The replacement CSS class
7809          * @return {Roo.Element} this
7810          */
7811         replaceClass : function(oldClassName, newClassName){
7812             this.removeClass(oldClassName);
7813             this.addClass(newClassName);
7814             return this;
7815         },
7816
7817         /**
7818          * Returns an object with properties matching the styles requested.
7819          * For example, el.getStyles('color', 'font-size', 'width') might return
7820          * {'color': '#FFFFFF', 'font-size': '13px', 'width': '100px'}.
7821          * @param {String} style1 A style name
7822          * @param {String} style2 A style name
7823          * @param {String} etc.
7824          * @return {Object} The style object
7825          */
7826         getStyles : function(){
7827             var a = arguments, len = a.length, r = {};
7828             for(var i = 0; i < len; i++){
7829                 r[a[i]] = this.getStyle(a[i]);
7830             }
7831             return r;
7832         },
7833
7834         /**
7835          * Normalizes currentStyle and computedStyle. This is not YUI getStyle, it is an optimised version.
7836          * @param {String} property The style property whose value is returned.
7837          * @return {String} The current value of the style property for this element.
7838          */
7839         getStyle : function(){
7840             return view && view.getComputedStyle ?
7841                 function(prop){
7842                     var el = this.dom, v, cs, camel;
7843                     if(prop == 'float'){
7844                         prop = "cssFloat";
7845                     }
7846                     if(el.style && (v = el.style[prop])){
7847                         return v;
7848                     }
7849                     if(cs = view.getComputedStyle(el, "")){
7850                         if(!(camel = propCache[prop])){
7851                             camel = propCache[prop] = prop.replace(camelRe, camelFn);
7852                         }
7853                         return cs[camel];
7854                     }
7855                     return null;
7856                 } :
7857                 function(prop){
7858                     var el = this.dom, v, cs, camel;
7859                     if(prop == 'opacity'){
7860                         if(typeof el.style.filter == 'string'){
7861                             var m = el.style.filter.match(/alpha\(opacity=(.*)\)/i);
7862                             if(m){
7863                                 var fv = parseFloat(m[1]);
7864                                 if(!isNaN(fv)){
7865                                     return fv ? fv / 100 : 0;
7866                                 }
7867                             }
7868                         }
7869                         return 1;
7870                     }else if(prop == 'float'){
7871                         prop = "styleFloat";
7872                     }
7873                     if(!(camel = propCache[prop])){
7874                         camel = propCache[prop] = prop.replace(camelRe, camelFn);
7875                     }
7876                     if(v = el.style[camel]){
7877                         return v;
7878                     }
7879                     if(cs = el.currentStyle){
7880                         return cs[camel];
7881                     }
7882                     return null;
7883                 };
7884         }(),
7885
7886         /**
7887          * Wrapper for setting style properties, also takes single object parameter of multiple styles.
7888          * @param {String/Object} property The style property to be set, or an object of multiple styles.
7889          * @param {String} value (optional) The value to apply to the given property, or null if an object was passed.
7890          * @return {Roo.Element} this
7891          */
7892         setStyle : function(prop, value){
7893             if(typeof prop == "string"){
7894                 
7895                 if (prop == 'float') {
7896                     this.setStyle(Roo.isIE ? 'styleFloat'  : 'cssFloat', value);
7897                     return this;
7898                 }
7899                 
7900                 var camel;
7901                 if(!(camel = propCache[prop])){
7902                     camel = propCache[prop] = prop.replace(camelRe, camelFn);
7903                 }
7904                 
7905                 if(camel == 'opacity') {
7906                     this.setOpacity(value);
7907                 }else{
7908                     this.dom.style[camel] = value;
7909                 }
7910             }else{
7911                 for(var style in prop){
7912                     if(typeof prop[style] != "function"){
7913                        this.setStyle(style, prop[style]);
7914                     }
7915                 }
7916             }
7917             return this;
7918         },
7919
7920         /**
7921          * More flexible version of {@link #setStyle} for setting style properties.
7922          * @param {String/Object/Function} styles A style specification string, e.g. "width:100px", or object in the form {width:"100px"}, or
7923          * a function which returns such a specification.
7924          * @return {Roo.Element} this
7925          */
7926         applyStyles : function(style){
7927             Roo.DomHelper.applyStyles(this.dom, style);
7928             return this;
7929         },
7930
7931         /**
7932           * 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).
7933           * @return {Number} The X position of the element
7934           */
7935         getX : function(){
7936             return D.getX(this.dom);
7937         },
7938
7939         /**
7940           * 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).
7941           * @return {Number} The Y position of the element
7942           */
7943         getY : function(){
7944             return D.getY(this.dom);
7945         },
7946
7947         /**
7948           * 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).
7949           * @return {Array} The XY position of the element
7950           */
7951         getXY : function(){
7952             return D.getXY(this.dom);
7953         },
7954
7955         /**
7956          * 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).
7957          * @param {Number} The X position of the element
7958          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7959          * @return {Roo.Element} this
7960          */
7961         setX : function(x, animate){
7962             if(!animate || !A){
7963                 D.setX(this.dom, x);
7964             }else{
7965                 this.setXY([x, this.getY()], this.preanim(arguments, 1));
7966             }
7967             return this;
7968         },
7969
7970         /**
7971          * 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).
7972          * @param {Number} The Y position of the element
7973          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7974          * @return {Roo.Element} this
7975          */
7976         setY : function(y, animate){
7977             if(!animate || !A){
7978                 D.setY(this.dom, y);
7979             }else{
7980                 this.setXY([this.getX(), y], this.preanim(arguments, 1));
7981             }
7982             return this;
7983         },
7984
7985         /**
7986          * Sets the element's left position directly using CSS style (instead of {@link #setX}).
7987          * @param {String} left The left CSS property value
7988          * @return {Roo.Element} this
7989          */
7990         setLeft : function(left){
7991             this.setStyle("left", this.addUnits(left));
7992             return this;
7993         },
7994
7995         /**
7996          * Sets the element's top position directly using CSS style (instead of {@link #setY}).
7997          * @param {String} top The top CSS property value
7998          * @return {Roo.Element} this
7999          */
8000         setTop : function(top){
8001             this.setStyle("top", this.addUnits(top));
8002             return this;
8003         },
8004
8005         /**
8006          * Sets the element's CSS right style.
8007          * @param {String} right The right CSS property value
8008          * @return {Roo.Element} this
8009          */
8010         setRight : function(right){
8011             this.setStyle("right", this.addUnits(right));
8012             return this;
8013         },
8014
8015         /**
8016          * Sets the element's CSS bottom style.
8017          * @param {String} bottom The bottom CSS property value
8018          * @return {Roo.Element} this
8019          */
8020         setBottom : function(bottom){
8021             this.setStyle("bottom", this.addUnits(bottom));
8022             return this;
8023         },
8024
8025         /**
8026          * Sets the position of the element in page coordinates, regardless of how the element is positioned.
8027          * The element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
8028          * @param {Array} pos Contains X & Y [x, y] values for new position (coordinates are page-based)
8029          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
8030          * @return {Roo.Element} this
8031          */
8032         setXY : function(pos, animate){
8033             if(!animate || !A){
8034                 D.setXY(this.dom, pos);
8035             }else{
8036                 this.anim({points: {to: pos}}, this.preanim(arguments, 1), 'motion');
8037             }
8038             return this;
8039         },
8040
8041         /**
8042          * Sets the position of the element in page coordinates, regardless of how the element is positioned.
8043          * The element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
8044          * @param {Number} x X value for new position (coordinates are page-based)
8045          * @param {Number} y Y value for new position (coordinates are page-based)
8046          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
8047          * @return {Roo.Element} this
8048          */
8049         setLocation : function(x, y, animate){
8050             this.setXY([x, y], this.preanim(arguments, 2));
8051             return this;
8052         },
8053
8054         /**
8055          * Sets the position of the element in page coordinates, regardless of how the element is positioned.
8056          * The element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
8057          * @param {Number} x X value for new position (coordinates are page-based)
8058          * @param {Number} y Y value for new position (coordinates are page-based)
8059          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
8060          * @return {Roo.Element} this
8061          */
8062         moveTo : function(x, y, animate){
8063             this.setXY([x, y], this.preanim(arguments, 2));
8064             return this;
8065         },
8066
8067         /**
8068          * Returns the region of the given element.
8069          * The element must be part of the DOM tree to have a region (display:none or elements not appended return false).
8070          * @return {Region} A Roo.lib.Region containing "top, left, bottom, right" member data.
8071          */
8072         getRegion : function(){
8073             return D.getRegion(this.dom);
8074         },
8075
8076         /**
8077          * Returns the offset height of the element
8078          * @param {Boolean} contentHeight (optional) true to get the height minus borders and padding
8079          * @return {Number} The element's height
8080          */
8081         getHeight : function(contentHeight){
8082             var h = this.dom.offsetHeight || 0;
8083             return contentHeight !== true ? h : h-this.getBorderWidth("tb")-this.getPadding("tb");
8084         },
8085
8086         /**
8087          * Returns the offset width of the element
8088          * @param {Boolean} contentWidth (optional) true to get the width minus borders and padding
8089          * @return {Number} The element's width
8090          */
8091         getWidth : function(contentWidth){
8092             var w = this.dom.offsetWidth || 0;
8093             return contentWidth !== true ? w : w-this.getBorderWidth("lr")-this.getPadding("lr");
8094         },
8095
8096         /**
8097          * Returns either the offsetHeight or the height of this element based on CSS height adjusted by padding or borders
8098          * when needed to simulate offsetHeight when offsets aren't available. This may not work on display:none elements
8099          * if a height has not been set using CSS.
8100          * @return {Number}
8101          */
8102         getComputedHeight : function(){
8103             var h = Math.max(this.dom.offsetHeight, this.dom.clientHeight);
8104             if(!h){
8105                 h = parseInt(this.getStyle('height'), 10) || 0;
8106                 if(!this.isBorderBox()){
8107                     h += this.getFrameWidth('tb');
8108                 }
8109             }
8110             return h;
8111         },
8112
8113         /**
8114          * Returns either the offsetWidth or the width of this element based on CSS width adjusted by padding or borders
8115          * when needed to simulate offsetWidth when offsets aren't available. This may not work on display:none elements
8116          * if a width has not been set using CSS.
8117          * @return {Number}
8118          */
8119         getComputedWidth : function(){
8120             var w = Math.max(this.dom.offsetWidth, this.dom.clientWidth);
8121             if(!w){
8122                 w = parseInt(this.getStyle('width'), 10) || 0;
8123                 if(!this.isBorderBox()){
8124                     w += this.getFrameWidth('lr');
8125                 }
8126             }
8127             return w;
8128         },
8129
8130         /**
8131          * Returns the size of the element.
8132          * @param {Boolean} contentSize (optional) true to get the width/size minus borders and padding
8133          * @return {Object} An object containing the element's size {width: (element width), height: (element height)}
8134          */
8135         getSize : function(contentSize){
8136             return {width: this.getWidth(contentSize), height: this.getHeight(contentSize)};
8137         },
8138
8139         /**
8140          * Returns the width and height of the viewport.
8141          * @return {Object} An object containing the viewport's size {width: (viewport width), height: (viewport height)}
8142          */
8143         getViewSize : function(){
8144             var d = this.dom, doc = document, aw = 0, ah = 0;
8145             if(d == doc || d == doc.body){
8146                 return {width : D.getViewWidth(), height: D.getViewHeight()};
8147             }else{
8148                 return {
8149                     width : d.clientWidth,
8150                     height: d.clientHeight
8151                 };
8152             }
8153         },
8154
8155         /**
8156          * Returns the value of the "value" attribute
8157          * @param {Boolean} asNumber true to parse the value as a number
8158          * @return {String/Number}
8159          */
8160         getValue : function(asNumber){
8161             return asNumber ? parseInt(this.dom.value, 10) : this.dom.value;
8162         },
8163
8164         // private
8165         adjustWidth : function(width){
8166             if(typeof width == "number"){
8167                 if(this.autoBoxAdjust && !this.isBorderBox()){
8168                    width -= (this.getBorderWidth("lr") + this.getPadding("lr"));
8169                 }
8170                 if(width < 0){
8171                     width = 0;
8172                 }
8173             }
8174             return width;
8175         },
8176
8177         // private
8178         adjustHeight : function(height){
8179             if(typeof height == "number"){
8180                if(this.autoBoxAdjust && !this.isBorderBox()){
8181                    height -= (this.getBorderWidth("tb") + this.getPadding("tb"));
8182                }
8183                if(height < 0){
8184                    height = 0;
8185                }
8186             }
8187             return height;
8188         },
8189
8190         /**
8191          * Set the width of the element
8192          * @param {Number} width The new width
8193          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8194          * @return {Roo.Element} this
8195          */
8196         setWidth : function(width, animate){
8197             width = this.adjustWidth(width);
8198             if(!animate || !A){
8199                 this.dom.style.width = this.addUnits(width);
8200             }else{
8201                 this.anim({width: {to: width}}, this.preanim(arguments, 1));
8202             }
8203             return this;
8204         },
8205
8206         /**
8207          * Set the height of the element
8208          * @param {Number} height The new height
8209          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8210          * @return {Roo.Element} this
8211          */
8212          setHeight : function(height, animate){
8213             height = this.adjustHeight(height);
8214             if(!animate || !A){
8215                 this.dom.style.height = this.addUnits(height);
8216             }else{
8217                 this.anim({height: {to: height}}, this.preanim(arguments, 1));
8218             }
8219             return this;
8220         },
8221
8222         /**
8223          * Set the size of the element. If animation is true, both width an height will be animated concurrently.
8224          * @param {Number} width The new width
8225          * @param {Number} height The new height
8226          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8227          * @return {Roo.Element} this
8228          */
8229          setSize : function(width, height, animate){
8230             if(typeof width == "object"){ // in case of object from getSize()
8231                 height = width.height; width = width.width;
8232             }
8233             width = this.adjustWidth(width); height = this.adjustHeight(height);
8234             if(!animate || !A){
8235                 this.dom.style.width = this.addUnits(width);
8236                 this.dom.style.height = this.addUnits(height);
8237             }else{
8238                 this.anim({width: {to: width}, height: {to: height}}, this.preanim(arguments, 2));
8239             }
8240             return this;
8241         },
8242
8243         /**
8244          * Sets the element's position and size in one shot. If animation is true then width, height, x and y will be animated concurrently.
8245          * @param {Number} x X value for new position (coordinates are page-based)
8246          * @param {Number} y Y value for new position (coordinates are page-based)
8247          * @param {Number} width The new width
8248          * @param {Number} height The new height
8249          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8250          * @return {Roo.Element} this
8251          */
8252         setBounds : function(x, y, width, height, animate){
8253             if(!animate || !A){
8254                 this.setSize(width, height);
8255                 this.setLocation(x, y);
8256             }else{
8257                 width = this.adjustWidth(width); height = this.adjustHeight(height);
8258                 this.anim({points: {to: [x, y]}, width: {to: width}, height: {to: height}},
8259                               this.preanim(arguments, 4), 'motion');
8260             }
8261             return this;
8262         },
8263
8264         /**
8265          * 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.
8266          * @param {Roo.lib.Region} region The region to fill
8267          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8268          * @return {Roo.Element} this
8269          */
8270         setRegion : function(region, animate){
8271             this.setBounds(region.left, region.top, region.right-region.left, region.bottom-region.top, this.preanim(arguments, 1));
8272             return this;
8273         },
8274
8275         /**
8276          * Appends an event handler
8277          *
8278          * @param {String}   eventName     The type of event to append
8279          * @param {Function} fn        The method the event invokes
8280          * @param {Object} scope       (optional) The scope (this object) of the fn
8281          * @param {Object}   options   (optional)An object with standard {@link Roo.EventManager#addListener} options
8282          */
8283         addListener : function(eventName, fn, scope, options)
8284         {
8285             if (eventName == 'dblclick') { // doublclick (touchstart) - faked on touch.
8286                 this.addListener('touchstart', this.onTapHandler, this);
8287             }
8288             
8289             // we need to handle a special case where dom element is a svg element.
8290             // in this case we do not actua
8291             if (!this.dom) {
8292                 return;
8293             }
8294             
8295             if (this.dom instanceof SVGElement && !(this.dom instanceof SVGSVGElement)) {
8296                 if (typeof(this.listeners[eventName]) == 'undefined') {
8297                     this.listeners[eventName] =  new Roo.util.Event(this, eventName);
8298                 }
8299                 this.listeners[eventName].addListener(fn, scope, options);
8300                 return;
8301             }
8302             
8303                 
8304             Roo.EventManager.on(this.dom,  eventName, fn, scope || this, options);
8305             
8306             
8307         },
8308         tapedTwice : false,
8309         onTapHandler : function(event)
8310         {
8311             if(!this.tapedTwice) {
8312                 this.tapedTwice = true;
8313                 var s = this;
8314                 setTimeout( function() {
8315                     s.tapedTwice = false;
8316                 }, 300 );
8317                 return;
8318             }
8319             event.preventDefault();
8320             var revent = new MouseEvent('dblclick',  {
8321                 view: window,
8322                 bubbles: true,
8323                 cancelable: true
8324             });
8325              
8326             this.dom.dispatchEvent(revent);
8327             //action on double tap goes below
8328              
8329         }, 
8330  
8331         /**
8332          * Removes an event handler from this element
8333          * @param {String} eventName the type of event to remove
8334          * @param {Function} fn the method the event invokes
8335          * @param {Function} scope (needed for svg fake listeners)
8336          * @return {Roo.Element} this
8337          */
8338         removeListener : function(eventName, fn, scope){
8339             Roo.EventManager.removeListener(this.dom,  eventName, fn);
8340             if (typeof(this.listeners) == 'undefined'  || typeof(this.listeners[eventName]) == 'undefined') {
8341                 return this;
8342             }
8343             this.listeners[eventName].removeListener(fn, scope);
8344             return this;
8345         },
8346
8347         /**
8348          * Removes all previous added listeners from this element
8349          * @return {Roo.Element} this
8350          */
8351         removeAllListeners : function(){
8352             E.purgeElement(this.dom);
8353             this.listeners = {};
8354             return this;
8355         },
8356
8357         relayEvent : function(eventName, observable){
8358             this.on(eventName, function(e){
8359                 observable.fireEvent(eventName, e);
8360             });
8361         },
8362
8363         
8364         /**
8365          * Set the opacity of the element
8366          * @param {Float} opacity The new opacity. 0 = transparent, .5 = 50% visibile, 1 = fully visible, etc
8367          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8368          * @return {Roo.Element} this
8369          */
8370          setOpacity : function(opacity, animate){
8371             if(!animate || !A){
8372                 var s = this.dom.style;
8373                 if(Roo.isIE){
8374                     s.zoom = 1;
8375                     s.filter = (s.filter || '').replace(/alpha\([^\)]*\)/gi,"") +
8376                                (opacity == 1 ? "" : "alpha(opacity=" + opacity * 100 + ")");
8377                 }else{
8378                     s.opacity = opacity;
8379                 }
8380             }else{
8381                 this.anim({opacity: {to: opacity}}, this.preanim(arguments, 1), null, .35, 'easeIn');
8382             }
8383             return this;
8384         },
8385
8386         /**
8387          * Gets the left X coordinate
8388          * @param {Boolean} local True to get the local css position instead of page coordinate
8389          * @return {Number}
8390          */
8391         getLeft : function(local){
8392             if(!local){
8393                 return this.getX();
8394             }else{
8395                 return parseInt(this.getStyle("left"), 10) || 0;
8396             }
8397         },
8398
8399         /**
8400          * Gets the right X coordinate of the element (element X position + element width)
8401          * @param {Boolean} local True to get the local css position instead of page coordinate
8402          * @return {Number}
8403          */
8404         getRight : function(local){
8405             if(!local){
8406                 return this.getX() + this.getWidth();
8407             }else{
8408                 return (this.getLeft(true) + this.getWidth()) || 0;
8409             }
8410         },
8411
8412         /**
8413          * Gets the top Y coordinate
8414          * @param {Boolean} local True to get the local css position instead of page coordinate
8415          * @return {Number}
8416          */
8417         getTop : function(local) {
8418             if(!local){
8419                 return this.getY();
8420             }else{
8421                 return parseInt(this.getStyle("top"), 10) || 0;
8422             }
8423         },
8424
8425         /**
8426          * Gets the bottom Y coordinate of the element (element Y position + element height)
8427          * @param {Boolean} local True to get the local css position instead of page coordinate
8428          * @return {Number}
8429          */
8430         getBottom : function(local){
8431             if(!local){
8432                 return this.getY() + this.getHeight();
8433             }else{
8434                 return (this.getTop(true) + this.getHeight()) || 0;
8435             }
8436         },
8437
8438         /**
8439         * Initializes positioning on this element. If a desired position is not passed, it will make the
8440         * the element positioned relative IF it is not already positioned.
8441         * @param {String} pos (optional) Positioning to use "relative", "absolute" or "fixed"
8442         * @param {Number} zIndex (optional) The zIndex to apply
8443         * @param {Number} x (optional) Set the page X position
8444         * @param {Number} y (optional) Set the page Y position
8445         */
8446         position : function(pos, zIndex, x, y){
8447             if(!pos){
8448                if(this.getStyle('position') == 'static'){
8449                    this.setStyle('position', 'relative');
8450                }
8451             }else{
8452                 this.setStyle("position", pos);
8453             }
8454             if(zIndex){
8455                 this.setStyle("z-index", zIndex);
8456             }
8457             if(x !== undefined && y !== undefined){
8458                 this.setXY([x, y]);
8459             }else if(x !== undefined){
8460                 this.setX(x);
8461             }else if(y !== undefined){
8462                 this.setY(y);
8463             }
8464         },
8465
8466         /**
8467         * Clear positioning back to the default when the document was loaded
8468         * @param {String} value (optional) The value to use for the left,right,top,bottom, defaults to '' (empty string). You could use 'auto'.
8469         * @return {Roo.Element} this
8470          */
8471         clearPositioning : function(value){
8472             value = value ||'';
8473             this.setStyle({
8474                 "left": value,
8475                 "right": value,
8476                 "top": value,
8477                 "bottom": value,
8478                 "z-index": "",
8479                 "position" : "static"
8480             });
8481             return this;
8482         },
8483
8484         /**
8485         * Gets an object with all CSS positioning properties. Useful along with setPostioning to get
8486         * snapshot before performing an update and then restoring the element.
8487         * @return {Object}
8488         */
8489         getPositioning : function(){
8490             var l = this.getStyle("left");
8491             var t = this.getStyle("top");
8492             return {
8493                 "position" : this.getStyle("position"),
8494                 "left" : l,
8495                 "right" : l ? "" : this.getStyle("right"),
8496                 "top" : t,
8497                 "bottom" : t ? "" : this.getStyle("bottom"),
8498                 "z-index" : this.getStyle("z-index")
8499             };
8500         },
8501
8502         /**
8503          * Gets the width of the border(s) for the specified side(s)
8504          * @param {String} side Can be t, l, r, b or any combination of those to add multiple values. For example,
8505          * passing lr would get the border (l)eft width + the border (r)ight width.
8506          * @return {Number} The width of the sides passed added together
8507          */
8508         getBorderWidth : function(side){
8509             return this.addStyles(side, El.borders);
8510         },
8511
8512         /**
8513          * Gets the width of the padding(s) for the specified side(s)
8514          * @param {String} side Can be t, l, r, b or any combination of those to add multiple values. For example,
8515          * passing lr would get the padding (l)eft + the padding (r)ight.
8516          * @return {Number} The padding of the sides passed added together
8517          */
8518         getPadding : function(side){
8519             return this.addStyles(side, El.paddings);
8520         },
8521
8522         /**
8523         * Set positioning with an object returned by getPositioning().
8524         * @param {Object} posCfg
8525         * @return {Roo.Element} this
8526          */
8527         setPositioning : function(pc){
8528             this.applyStyles(pc);
8529             if(pc.right == "auto"){
8530                 this.dom.style.right = "";
8531             }
8532             if(pc.bottom == "auto"){
8533                 this.dom.style.bottom = "";
8534             }
8535             return this;
8536         },
8537
8538         // private
8539         fixDisplay : function(){
8540             if(this.getStyle("display") == "none"){
8541                 this.setStyle("visibility", "hidden");
8542                 this.setStyle("display", this.originalDisplay); // first try reverting to default
8543                 if(this.getStyle("display") == "none"){ // if that fails, default to block
8544                     this.setStyle("display", "block");
8545                 }
8546             }
8547         },
8548
8549         /**
8550          * Quick set left and top adding default units
8551          * @param {String} left The left CSS property value
8552          * @param {String} top The top CSS property value
8553          * @return {Roo.Element} this
8554          */
8555          setLeftTop : function(left, top){
8556             this.dom.style.left = this.addUnits(left);
8557             this.dom.style.top = this.addUnits(top);
8558             return this;
8559         },
8560
8561         /**
8562          * Move this element relative to its current position.
8563          * @param {String} direction Possible values are: "l","left" - "r","right" - "t","top","up" - "b","bottom","down".
8564          * @param {Number} distance How far to move the element in pixels
8565          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8566          * @return {Roo.Element} this
8567          */
8568          move : function(direction, distance, animate){
8569             var xy = this.getXY();
8570             direction = direction.toLowerCase();
8571             switch(direction){
8572                 case "l":
8573                 case "left":
8574                     this.moveTo(xy[0]-distance, xy[1], this.preanim(arguments, 2));
8575                     break;
8576                case "r":
8577                case "right":
8578                     this.moveTo(xy[0]+distance, xy[1], this.preanim(arguments, 2));
8579                     break;
8580                case "t":
8581                case "top":
8582                case "up":
8583                     this.moveTo(xy[0], xy[1]-distance, this.preanim(arguments, 2));
8584                     break;
8585                case "b":
8586                case "bottom":
8587                case "down":
8588                     this.moveTo(xy[0], xy[1]+distance, this.preanim(arguments, 2));
8589                     break;
8590             }
8591             return this;
8592         },
8593
8594         /**
8595          *  Store the current overflow setting and clip overflow on the element - use {@link #unclip} to remove
8596          * @return {Roo.Element} this
8597          */
8598         clip : function(){
8599             if(!this.isClipped){
8600                this.isClipped = true;
8601                this.originalClip = {
8602                    "o": this.getStyle("overflow"),
8603                    "x": this.getStyle("overflow-x"),
8604                    "y": this.getStyle("overflow-y")
8605                };
8606                this.setStyle("overflow", "hidden");
8607                this.setStyle("overflow-x", "hidden");
8608                this.setStyle("overflow-y", "hidden");
8609             }
8610             return this;
8611         },
8612
8613         /**
8614          *  Return clipping (overflow) to original clipping before clip() was called
8615          * @return {Roo.Element} this
8616          */
8617         unclip : function(){
8618             if(this.isClipped){
8619                 this.isClipped = false;
8620                 var o = this.originalClip;
8621                 if(o.o){this.setStyle("overflow", o.o);}
8622                 if(o.x){this.setStyle("overflow-x", o.x);}
8623                 if(o.y){this.setStyle("overflow-y", o.y);}
8624             }
8625             return this;
8626         },
8627
8628
8629         /**
8630          * Gets the x,y coordinates specified by the anchor position on the element.
8631          * @param {String} anchor (optional) The specified anchor position (defaults to "c").  See {@link #alignTo} for details on supported anchor positions.
8632          * @param {Object} size (optional) An object containing the size to use for calculating anchor position
8633          *                       {width: (target width), height: (target height)} (defaults to the element's current size)
8634          * @param {Boolean} local (optional) True to get the local (element top/left-relative) anchor position instead of page coordinates
8635          * @return {Array} [x, y] An array containing the element's x and y coordinates
8636          */
8637         getAnchorXY : function(anchor, local, s){
8638             //Passing a different size is useful for pre-calculating anchors,
8639             //especially for anchored animations that change the el size.
8640
8641             var w, h, vp = false;
8642             if(!s){
8643                 var d = this.dom;
8644                 if(d == document.body || d == document){
8645                     vp = true;
8646                     w = D.getViewWidth(); h = D.getViewHeight();
8647                 }else{
8648                     w = this.getWidth(); h = this.getHeight();
8649                 }
8650             }else{
8651                 w = s.width;  h = s.height;
8652             }
8653             var x = 0, y = 0, r = Math.round;
8654             switch((anchor || "tl").toLowerCase()){
8655                 case "c":
8656                     x = r(w*.5);
8657                     y = r(h*.5);
8658                 break;
8659                 case "t":
8660                     x = r(w*.5);
8661                     y = 0;
8662                 break;
8663                 case "l":
8664                     x = 0;
8665                     y = r(h*.5);
8666                 break;
8667                 case "r":
8668                     x = w;
8669                     y = r(h*.5);
8670                 break;
8671                 case "b":
8672                     x = r(w*.5);
8673                     y = h;
8674                 break;
8675                 case "tl":
8676                     x = 0;
8677                     y = 0;
8678                 break;
8679                 case "bl":
8680                     x = 0;
8681                     y = h;
8682                 break;
8683                 case "br":
8684                     x = w;
8685                     y = h;
8686                 break;
8687                 case "tr":
8688                     x = w;
8689                     y = 0;
8690                 break;
8691             }
8692             if(local === true){
8693                 return [x, y];
8694             }
8695             if(vp){
8696                 var sc = this.getScroll();
8697                 return [x + sc.left, y + sc.top];
8698             }
8699             //Add the element's offset xy
8700             var o = this.getXY();
8701             return [x+o[0], y+o[1]];
8702         },
8703
8704         /**
8705          * Gets the x,y coordinates to align this element with another element. See {@link #alignTo} for more info on the
8706          * supported position values.
8707          * @param {String/HTMLElement/Roo.Element} element The element to align to.
8708          * @param {String} position The position to align to.
8709          * @param {Array} offsets (optional) Offset the positioning by [x, y]
8710          * @return {Array} [x, y]
8711          */
8712         getAlignToXY : function(el, p, o)
8713         {
8714             el = Roo.get(el);
8715             var d = this.dom;
8716             if(!el.dom){
8717                 throw "Element.alignTo with an element that doesn't exist";
8718             }
8719             var c = false; //constrain to viewport
8720             var p1 = "", p2 = "";
8721             o = o || [0,0];
8722
8723             if(!p){
8724                 p = "tl-bl";
8725             }else if(p == "?"){
8726                 p = "tl-bl?";
8727             }else if(p.indexOf("-") == -1){
8728                 p = "tl-" + p;
8729             }
8730             p = p.toLowerCase();
8731             var m = p.match(/^([a-z]+)-([a-z]+)(\?)?$/);
8732             if(!m){
8733                throw "Element.alignTo with an invalid alignment " + p;
8734             }
8735             p1 = m[1]; p2 = m[2]; c = !!m[3];
8736
8737             //Subtract the aligned el's internal xy from the target's offset xy
8738             //plus custom offset to get the aligned el's new offset xy
8739             var a1 = this.getAnchorXY(p1, true);
8740             var a2 = el.getAnchorXY(p2, false);
8741             var x = a2[0] - a1[0] + o[0];
8742             var y = a2[1] - a1[1] + o[1];
8743             if(c){
8744                 //constrain the aligned el to viewport if necessary
8745                 var w = this.getWidth(), h = this.getHeight(), r = el.getRegion();
8746                 // 5px of margin for ie
8747                 var dw = D.getViewWidth()-5, dh = D.getViewHeight()-5;
8748
8749                 //If we are at a viewport boundary and the aligned el is anchored on a target border that is
8750                 //perpendicular to the vp border, allow the aligned el to slide on that border,
8751                 //otherwise swap the aligned el to the opposite border of the target.
8752                 var p1y = p1.charAt(0), p1x = p1.charAt(p1.length-1);
8753                var p2y = p2.charAt(0), p2x = p2.charAt(p2.length-1);
8754                var swapY = ((p1y=="t" && p2y=="b") || (p1y=="b" && p2y=="t")  );
8755                var swapX = ((p1x=="r" && p2x=="l") || (p1x=="l" && p2x=="r"));
8756
8757                var doc = document;
8758                var scrollX = (doc.documentElement.scrollLeft || doc.body.scrollLeft || 0)+5;
8759                var scrollY = (doc.documentElement.scrollTop || doc.body.scrollTop || 0)+5;
8760
8761                if((x+w) > dw + scrollX){
8762                     x = swapX ? r.left-w : dw+scrollX-w;
8763                 }
8764                if(x < scrollX){
8765                    x = swapX ? r.right : scrollX;
8766                }
8767                if((y+h) > dh + scrollY){
8768                     y = swapY ? r.top-h : dh+scrollY-h;
8769                 }
8770                if (y < scrollY){
8771                    y = swapY ? r.bottom : scrollY;
8772                }
8773             }
8774             return [x,y];
8775         },
8776
8777         // private
8778         getConstrainToXY : function(){
8779             var os = {top:0, left:0, bottom:0, right: 0};
8780
8781             return function(el, local, offsets, proposedXY){
8782                 el = Roo.get(el);
8783                 offsets = offsets ? Roo.applyIf(offsets, os) : os;
8784
8785                 var vw, vh, vx = 0, vy = 0;
8786                 if(el.dom == document.body || el.dom == document){
8787                     vw = Roo.lib.Dom.getViewWidth();
8788                     vh = Roo.lib.Dom.getViewHeight();
8789                 }else{
8790                     vw = el.dom.clientWidth;
8791                     vh = el.dom.clientHeight;
8792                     if(!local){
8793                         var vxy = el.getXY();
8794                         vx = vxy[0];
8795                         vy = vxy[1];
8796                     }
8797                 }
8798
8799                 var s = el.getScroll();
8800
8801                 vx += offsets.left + s.left;
8802                 vy += offsets.top + s.top;
8803
8804                 vw -= offsets.right;
8805                 vh -= offsets.bottom;
8806
8807                 var vr = vx+vw;
8808                 var vb = vy+vh;
8809
8810                 var xy = proposedXY || (!local ? this.getXY() : [this.getLeft(true), this.getTop(true)]);
8811                 var x = xy[0], y = xy[1];
8812                 var w = this.dom.offsetWidth, h = this.dom.offsetHeight;
8813
8814                 // only move it if it needs it
8815                 var moved = false;
8816
8817                 // first validate right/bottom
8818                 if((x + w) > vr){
8819                     x = vr - w;
8820                     moved = true;
8821                 }
8822                 if((y + h) > vb){
8823                     y = vb - h;
8824                     moved = true;
8825                 }
8826                 // then make sure top/left isn't negative
8827                 if(x < vx){
8828                     x = vx;
8829                     moved = true;
8830                 }
8831                 if(y < vy){
8832                     y = vy;
8833                     moved = true;
8834                 }
8835                 return moved ? [x, y] : false;
8836             };
8837         }(),
8838
8839         // private
8840         adjustForConstraints : function(xy, parent, offsets){
8841             return this.getConstrainToXY(parent || document, false, offsets, xy) ||  xy;
8842         },
8843
8844         /**
8845          * Aligns this element with another element relative to the specified anchor points. If the other element is the
8846          * document it aligns it to the viewport.
8847          * The position parameter is optional, and can be specified in any one of the following formats:
8848          * <ul>
8849          *   <li><b>Blank</b>: Defaults to aligning the element's top-left corner to the target's bottom-left corner ("tl-bl").</li>
8850          *   <li><b>One anchor (deprecated)</b>: The passed anchor position is used as the target element's anchor point.
8851          *       The element being aligned will position its top-left corner (tl) to that point.  <i>This method has been
8852          *       deprecated in favor of the newer two anchor syntax below</i>.</li>
8853          *   <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
8854          *       element's anchor point, and the second value is used as the target's anchor point.</li>
8855          * </ul>
8856          * In addition to the anchor points, the position parameter also supports the "?" character.  If "?" is passed at the end of
8857          * the position string, the element will attempt to align as specified, but the position will be adjusted to constrain to
8858          * the viewport if necessary.  Note that the element being aligned might be swapped to align to a different position than
8859          * that specified in order to enforce the viewport constraints.
8860          * Following are all of the supported anchor positions:
8861     <pre>
8862     Value  Description
8863     -----  -----------------------------
8864     tl     The top left corner (default)
8865     t      The center of the top edge
8866     tr     The top right corner
8867     l      The center of the left edge
8868     c      In the center of the element
8869     r      The center of the right edge
8870     bl     The bottom left corner
8871     b      The center of the bottom edge
8872     br     The bottom right corner
8873     </pre>
8874     Example Usage:
8875     <pre><code>
8876     // align el to other-el using the default positioning ("tl-bl", non-constrained)
8877     el.alignTo("other-el");
8878
8879     // align the top left corner of el with the top right corner of other-el (constrained to viewport)
8880     el.alignTo("other-el", "tr?");
8881
8882     // align the bottom right corner of el with the center left edge of other-el
8883     el.alignTo("other-el", "br-l?");
8884
8885     // align the center of el with the bottom left corner of other-el and
8886     // adjust the x position by -6 pixels (and the y position by 0)
8887     el.alignTo("other-el", "c-bl", [-6, 0]);
8888     </code></pre>
8889          * @param {String/HTMLElement/Roo.Element} element The element to align to.
8890          * @param {String} position The position to align to.
8891          * @param {Array} offsets (optional) Offset the positioning by [x, y]
8892          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8893          * @return {Roo.Element} this
8894          */
8895         alignTo : function(element, position, offsets, animate){
8896             var xy = this.getAlignToXY(element, position, offsets);
8897             this.setXY(xy, this.preanim(arguments, 3));
8898             return this;
8899         },
8900
8901         /**
8902          * Anchors an element to another element and realigns it when the window is resized.
8903          * @param {String/HTMLElement/Roo.Element} element The element to align to.
8904          * @param {String} position The position to align to.
8905          * @param {Array} offsets (optional) Offset the positioning by [x, y]
8906          * @param {Boolean/Object} animate (optional) True for the default animation or a standard Element animation config object
8907          * @param {Boolean/Number} monitorScroll (optional) True to monitor body scroll and reposition. If this parameter
8908          * is a number, it is used as the buffer delay (defaults to 50ms).
8909          * @param {Function} callback The function to call after the animation finishes
8910          * @return {Roo.Element} this
8911          */
8912         anchorTo : function(el, alignment, offsets, animate, monitorScroll, callback){
8913             var action = function(){
8914                 this.alignTo(el, alignment, offsets, animate);
8915                 Roo.callback(callback, this);
8916             };
8917             Roo.EventManager.onWindowResize(action, this);
8918             var tm = typeof monitorScroll;
8919             if(tm != 'undefined'){
8920                 Roo.EventManager.on(window, 'scroll', action, this,
8921                     {buffer: tm == 'number' ? monitorScroll : 50});
8922             }
8923             action.call(this); // align immediately
8924             return this;
8925         },
8926         /**
8927          * Clears any opacity settings from this element. Required in some cases for IE.
8928          * @return {Roo.Element} this
8929          */
8930         clearOpacity : function(){
8931             if (window.ActiveXObject) {
8932                 if(typeof this.dom.style.filter == 'string' && (/alpha/i).test(this.dom.style.filter)){
8933                     this.dom.style.filter = "";
8934                 }
8935             } else {
8936                 this.dom.style.opacity = "";
8937                 this.dom.style["-moz-opacity"] = "";
8938                 this.dom.style["-khtml-opacity"] = "";
8939             }
8940             return this;
8941         },
8942
8943         /**
8944          * Hide this element - Uses display mode to determine whether to use "display" or "visibility". See {@link #setVisible}.
8945          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8946          * @return {Roo.Element} this
8947          */
8948         hide : function(animate){
8949             this.setVisible(false, this.preanim(arguments, 0));
8950             return this;
8951         },
8952
8953         /**
8954         * Show this element - Uses display mode to determine whether to use "display" or "visibility". See {@link #setVisible}.
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         show : function(animate){
8959             this.setVisible(true, this.preanim(arguments, 0));
8960             return this;
8961         },
8962
8963         /**
8964          * @private Test if size has a unit, otherwise appends the default
8965          */
8966         addUnits : function(size){
8967             return Roo.Element.addUnits(size, this.defaultUnit);
8968         },
8969
8970         /**
8971          * Temporarily enables offsets (width,height,x,y) for an element with display:none, use endMeasure() when done.
8972          * @return {Roo.Element} this
8973          */
8974         beginMeasure : function(){
8975             var el = this.dom;
8976             if(el.offsetWidth || el.offsetHeight){
8977                 return this; // offsets work already
8978             }
8979             var changed = [];
8980             var p = this.dom, b = document.body; // start with this element
8981             while((!el.offsetWidth && !el.offsetHeight) && p && p.tagName && p != b){
8982                 var pe = Roo.get(p);
8983                 if(pe.getStyle('display') == 'none'){
8984                     changed.push({el: p, visibility: pe.getStyle("visibility")});
8985                     p.style.visibility = "hidden";
8986                     p.style.display = "block";
8987                 }
8988                 p = p.parentNode;
8989             }
8990             this._measureChanged = changed;
8991             return this;
8992
8993         },
8994
8995         /**
8996          * Restores displays to before beginMeasure was called
8997          * @return {Roo.Element} this
8998          */
8999         endMeasure : function(){
9000             var changed = this._measureChanged;
9001             if(changed){
9002                 for(var i = 0, len = changed.length; i < len; i++) {
9003                     var r = changed[i];
9004                     r.el.style.visibility = r.visibility;
9005                     r.el.style.display = "none";
9006                 }
9007                 this._measureChanged = null;
9008             }
9009             return this;
9010         },
9011
9012         /**
9013         * Update the innerHTML of this element, optionally searching for and processing scripts
9014         * @param {String} html The new HTML
9015         * @param {Boolean} loadScripts (optional) true to look for and process scripts
9016         * @param {Function} callback For async script loading you can be noticed when the update completes
9017         * @return {Roo.Element} this
9018          */
9019         update : function(html, loadScripts, callback){
9020             if(typeof html == "undefined"){
9021                 html = "";
9022             }
9023             if(loadScripts !== true){
9024                 this.dom.innerHTML = html;
9025                 if(typeof callback == "function"){
9026                     callback();
9027                 }
9028                 return this;
9029             }
9030             var id = Roo.id();
9031             var dom = this.dom;
9032
9033             html += '<span id="' + id + '"></span>';
9034
9035             E.onAvailable(id, function(){
9036                 var hd = document.getElementsByTagName("head")[0];
9037                 var re = /(?:<script([^>]*)?>)((\n|\r|.)*?)(?:<\/script>)/ig;
9038                 var srcRe = /\ssrc=([\'\"])(.*?)\1/i;
9039                 var typeRe = /\stype=([\'\"])(.*?)\1/i;
9040
9041                 var match;
9042                 while(match = re.exec(html)){
9043                     var attrs = match[1];
9044                     var srcMatch = attrs ? attrs.match(srcRe) : false;
9045                     if(srcMatch && srcMatch[2]){
9046                        var s = document.createElement("script");
9047                        s.src = srcMatch[2];
9048                        var typeMatch = attrs.match(typeRe);
9049                        if(typeMatch && typeMatch[2]){
9050                            s.type = typeMatch[2];
9051                        }
9052                        hd.appendChild(s);
9053                     }else if(match[2] && match[2].length > 0){
9054                         if(window.execScript) {
9055                            window.execScript(match[2]);
9056                         } else {
9057                             /**
9058                              * eval:var:id
9059                              * eval:var:dom
9060                              * eval:var:html
9061                              * 
9062                              */
9063                            window.eval(match[2]);
9064                         }
9065                     }
9066                 }
9067                 var el = document.getElementById(id);
9068                 if(el){el.parentNode.removeChild(el);}
9069                 if(typeof callback == "function"){
9070                     callback();
9071                 }
9072             });
9073             dom.innerHTML = html.replace(/(?:<script.*?>)((\n|\r|.)*?)(?:<\/script>)/ig, "");
9074             return this;
9075         },
9076
9077         /**
9078          * Direct access to the UpdateManager update() method (takes the same parameters).
9079          * @param {String/Function} url The url for this request or a function to call to get the url
9080          * @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}
9081          * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess)
9082          * @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.
9083          * @return {Roo.Element} this
9084          */
9085         load : function(){
9086             var um = this.getUpdateManager();
9087             um.update.apply(um, arguments);
9088             return this;
9089         },
9090
9091         /**
9092         * Gets this element's UpdateManager
9093         * @return {Roo.UpdateManager} The UpdateManager
9094         */
9095         getUpdateManager : function(){
9096             if(!this.updateManager){
9097                 this.updateManager = new Roo.UpdateManager(this);
9098             }
9099             return this.updateManager;
9100         },
9101
9102         /**
9103          * Disables text selection for this element (normalized across browsers)
9104          * @return {Roo.Element} this
9105          */
9106         unselectable : function(){
9107             this.dom.unselectable = "on";
9108             this.swallowEvent("selectstart", true);
9109             this.applyStyles("-moz-user-select:none;-khtml-user-select:none;");
9110             this.addClass("x-unselectable");
9111             return this;
9112         },
9113
9114         /**
9115         * Calculates the x, y to center this element on the screen
9116         * @return {Array} The x, y values [x, y]
9117         */
9118         getCenterXY : function(){
9119             return this.getAlignToXY(document, 'c-c');
9120         },
9121
9122         /**
9123         * Centers the Element in either the viewport, or another Element.
9124         * @param {String/HTMLElement/Roo.Element} centerIn (optional) The element in which to center the element.
9125         */
9126         center : function(centerIn){
9127             this.alignTo(centerIn || document, 'c-c');
9128             return this;
9129         },
9130
9131         /**
9132          * Tests various css rules/browsers to determine if this element uses a border box
9133          * @return {Boolean}
9134          */
9135         isBorderBox : function(){
9136             return noBoxAdjust[this.dom.tagName.toLowerCase()] || Roo.isBorderBox;
9137         },
9138
9139         /**
9140          * Return a box {x, y, width, height} that can be used to set another elements
9141          * size/location to match this element.
9142          * @param {Boolean} contentBox (optional) If true a box for the content of the element is returned.
9143          * @param {Boolean} local (optional) If true the element's left and top are returned instead of page x/y.
9144          * @return {Object} box An object in the format {x, y, width, height}
9145          */
9146         getBox : function(contentBox, local){
9147             var xy;
9148             if(!local){
9149                 xy = this.getXY();
9150             }else{
9151                 var left = parseInt(this.getStyle("left"), 10) || 0;
9152                 var top = parseInt(this.getStyle("top"), 10) || 0;
9153                 xy = [left, top];
9154             }
9155             var el = this.dom, w = el.offsetWidth, h = el.offsetHeight, bx;
9156             if(!contentBox){
9157                 bx = {x: xy[0], y: xy[1], 0: xy[0], 1: xy[1], width: w, height: h};
9158             }else{
9159                 var l = this.getBorderWidth("l")+this.getPadding("l");
9160                 var r = this.getBorderWidth("r")+this.getPadding("r");
9161                 var t = this.getBorderWidth("t")+this.getPadding("t");
9162                 var b = this.getBorderWidth("b")+this.getPadding("b");
9163                 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)};
9164             }
9165             bx.right = bx.x + bx.width;
9166             bx.bottom = bx.y + bx.height;
9167             return bx;
9168         },
9169
9170         /**
9171          * Returns the sum width of the padding and borders for the passed "sides". See getBorderWidth()
9172          for more information about the sides.
9173          * @param {String} sides
9174          * @return {Number}
9175          */
9176         getFrameWidth : function(sides, onlyContentBox){
9177             return onlyContentBox && Roo.isBorderBox ? 0 : (this.getPadding(sides) + this.getBorderWidth(sides));
9178         },
9179
9180         /**
9181          * 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.
9182          * @param {Object} box The box to fill {x, y, width, height}
9183          * @param {Boolean} adjust (optional) Whether to adjust for box-model issues automatically
9184          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
9185          * @return {Roo.Element} this
9186          */
9187         setBox : function(box, adjust, animate){
9188             var w = box.width, h = box.height;
9189             if((adjust && !this.autoBoxAdjust) && !this.isBorderBox()){
9190                w -= (this.getBorderWidth("lr") + this.getPadding("lr"));
9191                h -= (this.getBorderWidth("tb") + this.getPadding("tb"));
9192             }
9193             this.setBounds(box.x, box.y, w, h, this.preanim(arguments, 2));
9194             return this;
9195         },
9196
9197         /**
9198          * Forces the browser to repaint this element
9199          * @return {Roo.Element} this
9200          */
9201          repaint : function(){
9202             var dom = this.dom;
9203             this.addClass("x-repaint");
9204             setTimeout(function(){
9205                 Roo.get(dom).removeClass("x-repaint");
9206             }, 1);
9207             return this;
9208         },
9209
9210         /**
9211          * Returns an object with properties top, left, right and bottom representing the margins of this element unless sides is passed,
9212          * then it returns the calculated width of the sides (see getPadding)
9213          * @param {String} sides (optional) Any combination of l, r, t, b to get the sum of those sides
9214          * @return {Object/Number}
9215          */
9216         getMargins : function(side){
9217             if(!side){
9218                 return {
9219                     top: parseInt(this.getStyle("margin-top"), 10) || 0,
9220                     left: parseInt(this.getStyle("margin-left"), 10) || 0,
9221                     bottom: parseInt(this.getStyle("margin-bottom"), 10) || 0,
9222                     right: parseInt(this.getStyle("margin-right"), 10) || 0
9223                 };
9224             }else{
9225                 return this.addStyles(side, El.margins);
9226              }
9227         },
9228
9229         // private
9230         addStyles : function(sides, styles){
9231             var val = 0, v, w;
9232             for(var i = 0, len = sides.length; i < len; i++){
9233                 v = this.getStyle(styles[sides.charAt(i)]);
9234                 if(v){
9235                      w = parseInt(v, 10);
9236                      if(w){ val += w; }
9237                 }
9238             }
9239             return val;
9240         },
9241
9242         /**
9243          * Creates a proxy element of this element
9244          * @param {String/Object} config The class name of the proxy element or a DomHelper config object
9245          * @param {String/HTMLElement} renderTo (optional) The element or element id to render the proxy to (defaults to document.body)
9246          * @param {Boolean} matchBox (optional) True to align and size the proxy to this element now (defaults to false)
9247          * @return {Roo.Element} The new proxy element
9248          */
9249         createProxy : function(config, renderTo, matchBox){
9250             if(renderTo){
9251                 renderTo = Roo.getDom(renderTo);
9252             }else{
9253                 renderTo = document.body;
9254             }
9255             config = typeof config == "object" ?
9256                 config : {tag : "div", cls: config};
9257             var proxy = Roo.DomHelper.append(renderTo, config, true);
9258             if(matchBox){
9259                proxy.setBox(this.getBox());
9260             }
9261             return proxy;
9262         },
9263
9264         /**
9265          * Puts a mask over this element to disable user interaction. Requires core.css.
9266          * This method can only be applied to elements which accept child nodes.
9267          * @param {String} msg (optional) A message to display in the mask
9268          * @param {String} msgCls (optional) A css class to apply to the msg element
9269          * @return {Element} The mask  element
9270          */
9271         mask : function(msg, msgCls)
9272         {
9273             if(this.getStyle("position") == "static" && this.dom.tagName !== 'BODY'){
9274                 this.setStyle("position", "relative");
9275             }
9276             if(!this._mask){
9277                 this._mask = Roo.DomHelper.append(this.dom, {cls:"roo-el-mask"}, true);
9278             }
9279             
9280             this.addClass("x-masked");
9281             this._mask.setDisplayed(true);
9282             
9283             // we wander
9284             var z = 0;
9285             var dom = this.dom;
9286             while (dom && dom.style) {
9287                 if (!isNaN(parseInt(dom.style.zIndex))) {
9288                     z = Math.max(z, parseInt(dom.style.zIndex));
9289                 }
9290                 dom = dom.parentNode;
9291             }
9292             // if we are masking the body - then it hides everything..
9293             if (this.dom == document.body) {
9294                 z = 1000000;
9295                 this._mask.setWidth(Roo.lib.Dom.getDocumentWidth());
9296                 this._mask.setHeight(Roo.lib.Dom.getDocumentHeight());
9297             }
9298            
9299             if(typeof msg == 'string'){
9300                 if(!this._maskMsg){
9301                     this._maskMsg = Roo.DomHelper.append(this.dom, {
9302                         cls: "roo-el-mask-msg", 
9303                         cn: [
9304                             {
9305                                 tag: 'i',
9306                                 cls: 'fa fa-spinner fa-spin'
9307                             },
9308                             {
9309                                 tag: 'div'
9310                             }   
9311                         ]
9312                     }, true);
9313                 }
9314                 var mm = this._maskMsg;
9315                 mm.dom.className = msgCls ? "roo-el-mask-msg " + msgCls : "roo-el-mask-msg";
9316                 if (mm.dom.lastChild) { // weird IE issue?
9317                     mm.dom.lastChild.innerHTML = msg;
9318                 }
9319                 mm.setDisplayed(true);
9320                 mm.center(this);
9321                 mm.setStyle('z-index', z + 102);
9322             }
9323             if(Roo.isIE && !(Roo.isIE7 && Roo.isStrict) && this.getStyle('height') == 'auto'){ // ie will not expand full height automatically
9324                 this._mask.setHeight(this.getHeight());
9325             }
9326             this._mask.setStyle('z-index', z + 100);
9327             
9328             return this._mask;
9329         },
9330
9331         /**
9332          * Removes a previously applied mask. If removeEl is true the mask overlay is destroyed, otherwise
9333          * it is cached for reuse.
9334          */
9335         unmask : function(removeEl){
9336             if(this._mask){
9337                 if(removeEl === true){
9338                     this._mask.remove();
9339                     delete this._mask;
9340                     if(this._maskMsg){
9341                         this._maskMsg.remove();
9342                         delete this._maskMsg;
9343                     }
9344                 }else{
9345                     this._mask.setDisplayed(false);
9346                     if(this._maskMsg){
9347                         this._maskMsg.setDisplayed(false);
9348                     }
9349                 }
9350             }
9351             this.removeClass("x-masked");
9352         },
9353
9354         /**
9355          * Returns true if this element is masked
9356          * @return {Boolean}
9357          */
9358         isMasked : function(){
9359             return this._mask && this._mask.isVisible();
9360         },
9361
9362         /**
9363          * Creates an iframe shim for this element to keep selects and other windowed objects from
9364          * showing through.
9365          * @return {Roo.Element} The new shim element
9366          */
9367         createShim : function(){
9368             var el = document.createElement('iframe');
9369             el.frameBorder = 'no';
9370             el.className = 'roo-shim';
9371             if(Roo.isIE && Roo.isSecure){
9372                 el.src = Roo.SSL_SECURE_URL;
9373             }
9374             var shim = Roo.get(this.dom.parentNode.insertBefore(el, this.dom));
9375             shim.autoBoxAdjust = false;
9376             return shim;
9377         },
9378
9379         /**
9380          * Removes this element from the DOM and deletes it from the cache
9381          */
9382         remove : function(){
9383             if(this.dom.parentNode){
9384                 this.dom.parentNode.removeChild(this.dom);
9385             }
9386             delete El.cache[this.dom.id];
9387         },
9388
9389         /**
9390          * Sets up event handlers to add and remove a css class when the mouse is over this element
9391          * @param {String} className
9392          * @param {Boolean} preventFlicker (optional) If set to true, it prevents flickering by filtering
9393          * mouseout events for children elements
9394          * @return {Roo.Element} this
9395          */
9396         addClassOnOver : function(className, preventFlicker){
9397             this.on("mouseover", function(){
9398                 Roo.fly(this, '_internal').addClass(className);
9399             }, this.dom);
9400             var removeFn = function(e){
9401                 if(preventFlicker !== true || !e.within(this, true)){
9402                     Roo.fly(this, '_internal').removeClass(className);
9403                 }
9404             };
9405             this.on("mouseout", removeFn, this.dom);
9406             return this;
9407         },
9408
9409         /**
9410          * Sets up event handlers to add and remove a css class when this element has the focus
9411          * @param {String} className
9412          * @return {Roo.Element} this
9413          */
9414         addClassOnFocus : function(className){
9415             this.on("focus", function(){
9416                 Roo.fly(this, '_internal').addClass(className);
9417             }, this.dom);
9418             this.on("blur", function(){
9419                 Roo.fly(this, '_internal').removeClass(className);
9420             }, this.dom);
9421             return this;
9422         },
9423         /**
9424          * 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)
9425          * @param {String} className
9426          * @return {Roo.Element} this
9427          */
9428         addClassOnClick : function(className){
9429             var dom = this.dom;
9430             this.on("mousedown", function(){
9431                 Roo.fly(dom, '_internal').addClass(className);
9432                 var d = Roo.get(document);
9433                 var fn = function(){
9434                     Roo.fly(dom, '_internal').removeClass(className);
9435                     d.removeListener("mouseup", fn);
9436                 };
9437                 d.on("mouseup", fn);
9438             });
9439             return this;
9440         },
9441
9442         /**
9443          * Stops the specified event from bubbling and optionally prevents the default action
9444          * @param {String} eventName
9445          * @param {Boolean} preventDefault (optional) true to prevent the default action too
9446          * @return {Roo.Element} this
9447          */
9448         swallowEvent : function(eventName, preventDefault){
9449             var fn = function(e){
9450                 e.stopPropagation();
9451                 if(preventDefault){
9452                     e.preventDefault();
9453                 }
9454             };
9455             if(eventName instanceof Array){
9456                 for(var i = 0, len = eventName.length; i < len; i++){
9457                      this.on(eventName[i], fn);
9458                 }
9459                 return this;
9460             }
9461             this.on(eventName, fn);
9462             return this;
9463         },
9464
9465         /**
9466          * @private
9467          */
9468         fitToParentDelegate : Roo.emptyFn, // keep a reference to the fitToParent delegate
9469
9470         /**
9471          * Sizes this element to its parent element's dimensions performing
9472          * neccessary box adjustments.
9473          * @param {Boolean} monitorResize (optional) If true maintains the fit when the browser window is resized.
9474          * @param {String/HTMLElment/Element} targetParent (optional) The target parent, default to the parentNode.
9475          * @return {Roo.Element} this
9476          */
9477         fitToParent : function(monitorResize, targetParent) {
9478           Roo.EventManager.removeResizeListener(this.fitToParentDelegate); // always remove previous fitToParent delegate from onWindowResize
9479           this.fitToParentDelegate = Roo.emptyFn; // remove reference to previous delegate
9480           if (monitorResize === true && !this.dom.parentNode) { // check if this Element still exists
9481             return this;
9482           }
9483           var p = Roo.get(targetParent || this.dom.parentNode);
9484           this.setSize(p.getComputedWidth() - p.getFrameWidth('lr'), p.getComputedHeight() - p.getFrameWidth('tb'));
9485           if (monitorResize === true) {
9486             this.fitToParentDelegate = this.fitToParent.createDelegate(this, [true, targetParent]);
9487             Roo.EventManager.onWindowResize(this.fitToParentDelegate);
9488           }
9489           return this;
9490         },
9491
9492         /**
9493          * Gets the next sibling, skipping text nodes
9494          * @return {HTMLElement} The next sibling or null
9495          */
9496         getNextSibling : function(){
9497             var n = this.dom.nextSibling;
9498             while(n && n.nodeType != 1){
9499                 n = n.nextSibling;
9500             }
9501             return n;
9502         },
9503
9504         /**
9505          * Gets the previous sibling, skipping text nodes
9506          * @return {HTMLElement} The previous sibling or null
9507          */
9508         getPrevSibling : function(){
9509             var n = this.dom.previousSibling;
9510             while(n && n.nodeType != 1){
9511                 n = n.previousSibling;
9512             }
9513             return n;
9514         },
9515
9516
9517         /**
9518          * Appends the passed element(s) to this element
9519          * @param {String/HTMLElement/Array/Element/CompositeElement} el
9520          * @return {Roo.Element} this
9521          */
9522         appendChild: function(el){
9523             el = Roo.get(el);
9524             el.appendTo(this);
9525             return this;
9526         },
9527
9528         /**
9529          * Creates the passed DomHelper config and appends it to this element or optionally inserts it before the passed child element.
9530          * @param {Object} config DomHelper element config object.  If no tag is specified (e.g., {tag:'input'}) then a div will be
9531          * automatically generated with the specified attributes.
9532          * @param {HTMLElement} insertBefore (optional) a child element of this element
9533          * @param {Boolean} returnDom (optional) true to return the dom node instead of creating an Element
9534          * @return {Roo.Element} The new child element
9535          */
9536         createChild: function(config, insertBefore, returnDom){
9537             config = config || {tag:'div'};
9538             if(insertBefore){
9539                 return Roo.DomHelper.insertBefore(insertBefore, config, returnDom !== true);
9540             }
9541             return Roo.DomHelper[!this.dom.firstChild ? 'overwrite' : 'append'](this.dom, config,  returnDom !== true);
9542         },
9543
9544         /**
9545          * Appends this element to the passed element
9546          * @param {String/HTMLElement/Element} el The new parent element
9547          * @return {Roo.Element} this
9548          */
9549         appendTo: function(el){
9550             el = Roo.getDom(el);
9551             el.appendChild(this.dom);
9552             return this;
9553         },
9554
9555         /**
9556          * Inserts this element before the passed element in the DOM
9557          * @param {String/HTMLElement/Element} el The element to insert before
9558          * @return {Roo.Element} this
9559          */
9560         insertBefore: function(el){
9561             el = Roo.getDom(el);
9562             el.parentNode.insertBefore(this.dom, el);
9563             return this;
9564         },
9565
9566         /**
9567          * Inserts this element after the passed element in the DOM
9568          * @param {String/HTMLElement/Element} el The element to insert after
9569          * @return {Roo.Element} this
9570          */
9571         insertAfter: function(el){
9572             el = Roo.getDom(el);
9573             el.parentNode.insertBefore(this.dom, el.nextSibling);
9574             return this;
9575         },
9576
9577         /**
9578          * Inserts (or creates) an element (or DomHelper config) as the first child of the this element
9579          * @param {String/HTMLElement/Element/Object} el The id or element to insert or a DomHelper config to create and insert
9580          * @return {Roo.Element} The new child
9581          */
9582         insertFirst: function(el, returnDom){
9583             el = el || {};
9584             if(typeof el == 'object' && !el.nodeType){ // dh config
9585                 return this.createChild(el, this.dom.firstChild, returnDom);
9586             }else{
9587                 el = Roo.getDom(el);
9588                 this.dom.insertBefore(el, this.dom.firstChild);
9589                 return !returnDom ? Roo.get(el) : el;
9590             }
9591         },
9592
9593         /**
9594          * Inserts (or creates) the passed element (or DomHelper config) as a sibling of this element
9595          * @param {String/HTMLElement/Element/Object} el The id or element to insert or a DomHelper config to create and insert
9596          * @param {String} where (optional) 'before' or 'after' defaults to before
9597          * @param {Boolean} returnDom (optional) True to return the raw DOM element instead of Roo.Element
9598          * @return {Roo.Element} the inserted Element
9599          */
9600         insertSibling: function(el, where, returnDom){
9601             where = where ? where.toLowerCase() : 'before';
9602             el = el || {};
9603             var rt, refNode = where == 'before' ? this.dom : this.dom.nextSibling;
9604
9605             if(typeof el == 'object' && !el.nodeType){ // dh config
9606                 if(where == 'after' && !this.dom.nextSibling){
9607                     rt = Roo.DomHelper.append(this.dom.parentNode, el, !returnDom);
9608                 }else{
9609                     rt = Roo.DomHelper[where == 'after' ? 'insertAfter' : 'insertBefore'](this.dom, el, !returnDom);
9610                 }
9611
9612             }else{
9613                 rt = this.dom.parentNode.insertBefore(Roo.getDom(el),
9614                             where == 'before' ? this.dom : this.dom.nextSibling);
9615                 if(!returnDom){
9616                     rt = Roo.get(rt);
9617                 }
9618             }
9619             return rt;
9620         },
9621
9622         /**
9623          * Creates and wraps this element with another element
9624          * @param {Object} config (optional) DomHelper element config object for the wrapper element or null for an empty div
9625          * @param {Boolean} returnDom (optional) True to return the raw DOM element instead of Roo.Element
9626          * @return {HTMLElement/Element} The newly created wrapper element
9627          */
9628         wrap: function(config, returnDom){
9629             if(!config){
9630                 config = {tag: "div"};
9631             }
9632             var newEl = Roo.DomHelper.insertBefore(this.dom, config, !returnDom);
9633             newEl.dom ? newEl.dom.appendChild(this.dom) : newEl.appendChild(this.dom);
9634             return newEl;
9635         },
9636
9637         /**
9638          * Replaces the passed element with this element
9639          * @param {String/HTMLElement/Element} el The element to replace
9640          * @return {Roo.Element} this
9641          */
9642         replace: function(el){
9643             el = Roo.get(el);
9644             this.insertBefore(el);
9645             el.remove();
9646             return this;
9647         },
9648
9649         /**
9650          * Inserts an html fragment into this element
9651          * @param {String} where Where to insert the html in relation to the this element - beforeBegin, afterBegin, beforeEnd, afterEnd.
9652          * @param {String} html The HTML fragment
9653          * @param {Boolean} returnEl True to return an Roo.Element
9654          * @return {HTMLElement/Roo.Element} The inserted node (or nearest related if more than 1 inserted)
9655          */
9656         insertHtml : function(where, html, returnEl){
9657             var el = Roo.DomHelper.insertHtml(where, this.dom, html);
9658             return returnEl ? Roo.get(el) : el;
9659         },
9660
9661         /**
9662          * Sets the passed attributes as attributes of this element (a style attribute can be a string, object or function)
9663          * @param {Object} o The object with the attributes
9664          * @param {Boolean} useSet (optional) false to override the default setAttribute to use expandos.
9665          * @return {Roo.Element} this
9666          */
9667         set : function(o, useSet){
9668             var el = this.dom;
9669             useSet = typeof useSet == 'undefined' ? (el.setAttribute ? true : false) : useSet;
9670             for(var attr in o){
9671                 if(attr == "style" || typeof o[attr] == "function")  { continue; }
9672                 if(attr=="cls"){
9673                     el.className = o["cls"];
9674                 }else{
9675                     if(useSet) {
9676                         el.setAttribute(attr, o[attr]);
9677                     } else {
9678                         el[attr] = o[attr];
9679                     }
9680                 }
9681             }
9682             if(o.style){
9683                 Roo.DomHelper.applyStyles(el, o.style);
9684             }
9685             return this;
9686         },
9687
9688         /**
9689          * Convenience method for constructing a KeyMap
9690          * @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:
9691          *                                  {key: (number or array), shift: (true/false), ctrl: (true/false), alt: (true/false)}
9692          * @param {Function} fn The function to call
9693          * @param {Object} scope (optional) The scope of the function
9694          * @return {Roo.KeyMap} The KeyMap created
9695          */
9696         addKeyListener : function(key, fn, scope){
9697             var config;
9698             if(typeof key != "object" || key instanceof Array){
9699                 config = {
9700                     key: key,
9701                     fn: fn,
9702                     scope: scope
9703                 };
9704             }else{
9705                 config = {
9706                     key : key.key,
9707                     shift : key.shift,
9708                     ctrl : key.ctrl,
9709                     alt : key.alt,
9710                     fn: fn,
9711                     scope: scope
9712                 };
9713             }
9714             return new Roo.KeyMap(this, config);
9715         },
9716
9717         /**
9718          * Creates a KeyMap for this element
9719          * @param {Object} config The KeyMap config. See {@link Roo.KeyMap} for more details
9720          * @return {Roo.KeyMap} The KeyMap created
9721          */
9722         addKeyMap : function(config){
9723             return new Roo.KeyMap(this, config);
9724         },
9725
9726         /**
9727          * Returns true if this element is scrollable.
9728          * @return {Boolean}
9729          */
9730          isScrollable : function(){
9731             var dom = this.dom;
9732             return dom.scrollHeight > dom.clientHeight || dom.scrollWidth > dom.clientWidth;
9733         },
9734
9735         /**
9736          * 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().
9737          * @param {String} side Either "left" for scrollLeft values or "top" for scrollTop values.
9738          * @param {Number} value The new scroll value
9739          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
9740          * @return {Element} this
9741          */
9742
9743         scrollTo : function(side, value, animate){
9744             var prop = side.toLowerCase() == "left" ? "scrollLeft" : "scrollTop";
9745             if(!animate || !A){
9746                 this.dom[prop] = value;
9747             }else{
9748                 var to = prop == "scrollLeft" ? [value, this.dom.scrollTop] : [this.dom.scrollLeft, value];
9749                 this.anim({scroll: {"to": to}}, this.preanim(arguments, 2), 'scroll');
9750             }
9751             return this;
9752         },
9753
9754         /**
9755          * Scrolls this element the specified direction. Does bounds checking to make sure the scroll is
9756          * within this element's scrollable range.
9757          * @param {String} direction Possible values are: "l","left" - "r","right" - "t","top","up" - "b","bottom","down".
9758          * @param {Number} distance How far to scroll the element in pixels
9759          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
9760          * @return {Boolean} Returns true if a scroll was triggered or false if the element
9761          * was scrolled as far as it could go.
9762          */
9763          scroll : function(direction, distance, animate){
9764              if(!this.isScrollable()){
9765                  return;
9766              }
9767              var el = this.dom;
9768              var l = el.scrollLeft, t = el.scrollTop;
9769              var w = el.scrollWidth, h = el.scrollHeight;
9770              var cw = el.clientWidth, ch = el.clientHeight;
9771              direction = direction.toLowerCase();
9772              var scrolled = false;
9773              var a = this.preanim(arguments, 2);
9774              switch(direction){
9775                  case "l":
9776                  case "left":
9777                      if(w - l > cw){
9778                          var v = Math.min(l + distance, w-cw);
9779                          this.scrollTo("left", v, a);
9780                          scrolled = true;
9781                      }
9782                      break;
9783                 case "r":
9784                 case "right":
9785                      if(l > 0){
9786                          var v = Math.max(l - distance, 0);
9787                          this.scrollTo("left", v, a);
9788                          scrolled = true;
9789                      }
9790                      break;
9791                 case "t":
9792                 case "top":
9793                 case "up":
9794                      if(t > 0){
9795                          var v = Math.max(t - distance, 0);
9796                          this.scrollTo("top", v, a);
9797                          scrolled = true;
9798                      }
9799                      break;
9800                 case "b":
9801                 case "bottom":
9802                 case "down":
9803                      if(h - t > ch){
9804                          var v = Math.min(t + distance, h-ch);
9805                          this.scrollTo("top", v, a);
9806                          scrolled = true;
9807                      }
9808                      break;
9809              }
9810              return scrolled;
9811         },
9812
9813         /**
9814          * Translates the passed page coordinates into left/top css values for this element
9815          * @param {Number/Array} x The page x or an array containing [x, y]
9816          * @param {Number} y The page y
9817          * @return {Object} An object with left and top properties. e.g. {left: (value), top: (value)}
9818          */
9819         translatePoints : function(x, y){
9820             if(typeof x == 'object' || x instanceof Array){
9821                 y = x[1]; x = x[0];
9822             }
9823             var p = this.getStyle('position');
9824             var o = this.getXY();
9825
9826             var l = parseInt(this.getStyle('left'), 10);
9827             var t = parseInt(this.getStyle('top'), 10);
9828
9829             if(isNaN(l)){
9830                 l = (p == "relative") ? 0 : this.dom.offsetLeft;
9831             }
9832             if(isNaN(t)){
9833                 t = (p == "relative") ? 0 : this.dom.offsetTop;
9834             }
9835
9836             return {left: (x - o[0] + l), top: (y - o[1] + t)};
9837         },
9838
9839         /**
9840          * Returns the current scroll position of the element.
9841          * @return {Object} An object containing the scroll position in the format {left: (scrollLeft), top: (scrollTop)}
9842          */
9843         getScroll : function(){
9844             var d = this.dom, doc = document;
9845             if(d == doc || d == doc.body){
9846                 var l = window.pageXOffset || doc.documentElement.scrollLeft || doc.body.scrollLeft || 0;
9847                 var t = window.pageYOffset || doc.documentElement.scrollTop || doc.body.scrollTop || 0;
9848                 return {left: l, top: t};
9849             }else{
9850                 return {left: d.scrollLeft, top: d.scrollTop};
9851             }
9852         },
9853
9854         /**
9855          * Return the CSS color for the specified CSS attribute. rgb, 3 digit (like #fff) and valid values
9856          * are convert to standard 6 digit hex color.
9857          * @param {String} attr The css attribute
9858          * @param {String} defaultValue The default value to use when a valid color isn't found
9859          * @param {String} prefix (optional) defaults to #. Use an empty string when working with
9860          * YUI color anims.
9861          */
9862         getColor : function(attr, defaultValue, prefix){
9863             var v = this.getStyle(attr);
9864             if(!v || v == "transparent" || v == "inherit") {
9865                 return defaultValue;
9866             }
9867             var color = typeof prefix == "undefined" ? "#" : prefix;
9868             if(v.substr(0, 4) == "rgb("){
9869                 var rvs = v.slice(4, v.length -1).split(",");
9870                 for(var i = 0; i < 3; i++){
9871                     var h = parseInt(rvs[i]).toString(16);
9872                     if(h < 16){
9873                         h = "0" + h;
9874                     }
9875                     color += h;
9876                 }
9877             } else {
9878                 if(v.substr(0, 1) == "#"){
9879                     if(v.length == 4) {
9880                         for(var i = 1; i < 4; i++){
9881                             var c = v.charAt(i);
9882                             color +=  c + c;
9883                         }
9884                     }else if(v.length == 7){
9885                         color += v.substr(1);
9886                     }
9887                 }
9888             }
9889             return(color.length > 5 ? color.toLowerCase() : defaultValue);
9890         },
9891
9892         /**
9893          * Wraps the specified element with a special markup/CSS block that renders by default as a gray container with a
9894          * gradient background, rounded corners and a 4-way shadow.
9895          * @param {String} class (optional) A base CSS class to apply to the containing wrapper element (defaults to 'x-box').
9896          * Note that there are a number of CSS rules that are dependent on this name to make the overall effect work,
9897          * so if you supply an alternate base class, make sure you also supply all of the necessary rules.
9898          * @return {Roo.Element} this
9899          */
9900         boxWrap : function(cls){
9901             cls = cls || 'x-box';
9902             var el = Roo.get(this.insertHtml('beforeBegin', String.format('<div class="{0}">'+El.boxMarkup+'</div>', cls)));
9903             el.child('.'+cls+'-mc').dom.appendChild(this.dom);
9904             return el;
9905         },
9906
9907         /**
9908          * Returns the value of a namespaced attribute from the element's underlying DOM node.
9909          * @param {String} namespace The namespace in which to look for the attribute
9910          * @param {String} name The attribute name
9911          * @return {String} The attribute value
9912          */
9913         getAttributeNS : Roo.isIE ? function(ns, name){
9914             var d = this.dom;
9915             var type = typeof d[ns+":"+name];
9916             if(type != 'undefined' && type != 'unknown'){
9917                 return d[ns+":"+name];
9918             }
9919             return d[name];
9920         } : function(ns, name){
9921             var d = this.dom;
9922             return d.getAttributeNS(ns, name) || d.getAttribute(ns+":"+name) || d.getAttribute(name) || d[name];
9923         },
9924         
9925         
9926         /**
9927          * Sets or Returns the value the dom attribute value
9928          * @param {String|Object} name The attribute name (or object to set multiple attributes)
9929          * @param {String} value (optional) The value to set the attribute to
9930          * @return {String} The attribute value
9931          */
9932         attr : function(name){
9933             if (arguments.length > 1) {
9934                 this.dom.setAttribute(name, arguments[1]);
9935                 return arguments[1];
9936             }
9937             if (typeof(name) == 'object') {
9938                 for(var i in name) {
9939                     this.attr(i, name[i]);
9940                 }
9941                 return name;
9942             }
9943             
9944             
9945             if (!this.dom.hasAttribute(name)) {
9946                 return undefined;
9947             }
9948             return this.dom.getAttribute(name);
9949         }
9950         
9951         
9952         
9953     };
9954
9955     var ep = El.prototype;
9956
9957     /**
9958      * Appends an event handler (Shorthand for addListener)
9959      * @param {String}   eventName     The type of event to append
9960      * @param {Function} fn        The method the event invokes
9961      * @param {Object} scope       (optional) The scope (this object) of the fn
9962      * @param {Object}   options   (optional)An object with standard {@link Roo.EventManager#addListener} options
9963      * @method
9964      */
9965     ep.on = ep.addListener;
9966         // backwards compat
9967     ep.mon = ep.addListener;
9968
9969     /**
9970      * Removes an event handler from this element (shorthand for removeListener)
9971      * @param {String} eventName the type of event to remove
9972      * @param {Function} fn the method the event invokes
9973      * @return {Roo.Element} this
9974      * @method
9975      */
9976     ep.un = ep.removeListener;
9977
9978     /**
9979      * true to automatically adjust width and height settings for box-model issues (default to true)
9980      */
9981     ep.autoBoxAdjust = true;
9982
9983     // private
9984     El.unitPattern = /\d+(px|em|%|en|ex|pt|in|cm|mm|pc)$/i;
9985
9986     // private
9987     El.addUnits = function(v, defaultUnit){
9988         if(v === "" || v == "auto"){
9989             return v;
9990         }
9991         if(v === undefined){
9992             return '';
9993         }
9994         if(typeof v == "number" || !El.unitPattern.test(v)){
9995             return v + (defaultUnit || 'px');
9996         }
9997         return v;
9998     };
9999
10000     // special markup used throughout Roo when box wrapping elements
10001     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>';
10002     /**
10003      * Visibility mode constant - Use visibility to hide element
10004      * @static
10005      * @type Number
10006      */
10007     El.VISIBILITY = 1;
10008     /**
10009      * Visibility mode constant - Use display to hide element
10010      * @static
10011      * @type Number
10012      */
10013     El.DISPLAY = 2;
10014
10015     El.borders = {l: "border-left-width", r: "border-right-width", t: "border-top-width", b: "border-bottom-width"};
10016     El.paddings = {l: "padding-left", r: "padding-right", t: "padding-top", b: "padding-bottom"};
10017     El.margins = {l: "margin-left", r: "margin-right", t: "margin-top", b: "margin-bottom"};
10018
10019
10020
10021     /**
10022      * @private
10023      */
10024     El.cache = {};
10025
10026     var docEl;
10027
10028     /**
10029      * Static method to retrieve Element objects. Uses simple caching to consistently return the same object.
10030      * Automatically fixes if an object was recreated with the same id via AJAX or DOM.
10031      * @param {String/HTMLElement/Element} el The id of the node, a DOM Node or an existing Element.
10032      * @return {Element} The Element object
10033      * @static
10034      */
10035     El.get = function(el){
10036         var ex, elm, id;
10037         if(!el){ return null; }
10038         if(typeof el == "string"){ // element id
10039             if(!(elm = document.getElementById(el))){
10040                 return null;
10041             }
10042             if(ex = El.cache[el]){
10043                 ex.dom = elm;
10044             }else{
10045                 ex = El.cache[el] = new El(elm);
10046             }
10047             return ex;
10048         }else if(el.tagName){ // dom element
10049             if(!(id = el.id)){
10050                 id = Roo.id(el);
10051             }
10052             if(ex = El.cache[id]){
10053                 ex.dom = el;
10054             }else{
10055                 ex = El.cache[id] = new El(el);
10056             }
10057             return ex;
10058         }else if(el instanceof El){
10059             if(el != docEl){
10060                 el.dom = document.getElementById(el.id) || el.dom; // refresh dom element in case no longer valid,
10061                                                               // catch case where it hasn't been appended
10062                 El.cache[el.id] = el; // in case it was created directly with Element(), let's cache it
10063             }
10064             return el;
10065         }else if(el.isComposite){
10066             return el;
10067         }else if(el instanceof Array){
10068             return El.select(el);
10069         }else if(el == document){
10070             // create a bogus element object representing the document object
10071             if(!docEl){
10072                 var f = function(){};
10073                 f.prototype = El.prototype;
10074                 docEl = new f();
10075                 docEl.dom = document;
10076             }
10077             return docEl;
10078         }
10079         return null;
10080     };
10081
10082     // private
10083     El.uncache = function(el){
10084         for(var i = 0, a = arguments, len = a.length; i < len; i++) {
10085             if(a[i]){
10086                 delete El.cache[a[i].id || a[i]];
10087             }
10088         }
10089     };
10090
10091     // private
10092     // Garbage collection - uncache elements/purge listeners on orphaned elements
10093     // so we don't hold a reference and cause the browser to retain them
10094     El.garbageCollect = function(){
10095         if(!Roo.enableGarbageCollector){
10096             clearInterval(El.collectorThread);
10097             return;
10098         }
10099         for(var eid in El.cache){
10100             var el = El.cache[eid], d = el.dom;
10101             // -------------------------------------------------------
10102             // Determining what is garbage:
10103             // -------------------------------------------------------
10104             // !d
10105             // dom node is null, definitely garbage
10106             // -------------------------------------------------------
10107             // !d.parentNode
10108             // no parentNode == direct orphan, definitely garbage
10109             // -------------------------------------------------------
10110             // !d.offsetParent && !document.getElementById(eid)
10111             // display none elements have no offsetParent so we will
10112             // also try to look it up by it's id. However, check
10113             // offsetParent first so we don't do unneeded lookups.
10114             // This enables collection of elements that are not orphans
10115             // directly, but somewhere up the line they have an orphan
10116             // parent.
10117             // -------------------------------------------------------
10118             if(!d || !d.parentNode || (!d.offsetParent && !document.getElementById(eid))){
10119                 delete El.cache[eid];
10120                 if(d && Roo.enableListenerCollection){
10121                     E.purgeElement(d);
10122                 }
10123             }
10124         }
10125     }
10126     El.collectorThreadId = setInterval(El.garbageCollect, 30000);
10127
10128
10129     // dom is optional
10130     El.Flyweight = function(dom){
10131         this.dom = dom;
10132     };
10133     El.Flyweight.prototype = El.prototype;
10134
10135     El._flyweights = {};
10136     /**
10137      * Gets the globally shared flyweight Element, with the passed node as the active element. Do not store a reference to this element -
10138      * the dom node can be overwritten by other code.
10139      * @param {String/HTMLElement} el The dom node or id
10140      * @param {String} named (optional) Allows for creation of named reusable flyweights to
10141      *                                  prevent conflicts (e.g. internally Roo uses "_internal")
10142      * @static
10143      * @return {Element} The shared Element object
10144      */
10145     El.fly = function(el, named){
10146         named = named || '_global';
10147         el = Roo.getDom(el);
10148         if(!el){
10149             return null;
10150         }
10151         if(!El._flyweights[named]){
10152             El._flyweights[named] = new El.Flyweight();
10153         }
10154         El._flyweights[named].dom = el;
10155         return El._flyweights[named];
10156     };
10157
10158     /**
10159      * Static method to retrieve Element objects. Uses simple caching to consistently return the same object.
10160      * Automatically fixes if an object was recreated with the same id via AJAX or DOM.
10161      * Shorthand of {@link Roo.Element#get}
10162      * @param {String/HTMLElement/Element} el The id of the node, a DOM Node or an existing Element.
10163      * @return {Element} The Element object
10164      * @member Roo
10165      * @method get
10166      */
10167     Roo.get = El.get;
10168     /**
10169      * Gets the globally shared flyweight Element, with the passed node as the active element. Do not store a reference to this element -
10170      * the dom node can be overwritten by other code.
10171      * Shorthand of {@link Roo.Element#fly}
10172      * @param {String/HTMLElement} el The dom node or id
10173      * @param {String} named (optional) Allows for creation of named reusable flyweights to
10174      *                                  prevent conflicts (e.g. internally Roo uses "_internal")
10175      * @static
10176      * @return {Element} The shared Element object
10177      * @member Roo
10178      * @method fly
10179      */
10180     Roo.fly = El.fly;
10181
10182     // speedy lookup for elements never to box adjust
10183     var noBoxAdjust = Roo.isStrict ? {
10184         select:1
10185     } : {
10186         input:1, select:1, textarea:1
10187     };
10188     if(Roo.isIE || Roo.isGecko){
10189         noBoxAdjust['button'] = 1;
10190     }
10191
10192
10193     Roo.EventManager.on(window, 'unload', function(){
10194         delete El.cache;
10195         delete El._flyweights;
10196     });
10197 })();
10198
10199
10200
10201
10202 if(Roo.DomQuery){
10203     Roo.Element.selectorFunction = Roo.DomQuery.select;
10204 }
10205
10206 Roo.Element.select = function(selector, unique, root){
10207     var els;
10208     if(typeof selector == "string"){
10209         els = Roo.Element.selectorFunction(selector, root);
10210     }else if(selector.length !== undefined){
10211         els = selector;
10212     }else{
10213         throw "Invalid selector";
10214     }
10215     if(unique === true){
10216         return new Roo.CompositeElement(els);
10217     }else{
10218         return new Roo.CompositeElementLite(els);
10219     }
10220 };
10221 /**
10222  * Selects elements based on the passed CSS selector to enable working on them as 1.
10223  * @param {String/Array} selector The CSS selector or an array of elements
10224  * @param {Boolean} unique (optional) true to create a unique Roo.Element for each element (defaults to a shared flyweight object)
10225  * @param {HTMLElement/String} root (optional) The root element of the query or id of the root
10226  * @return {CompositeElementLite/CompositeElement}
10227  * @member Roo
10228  * @method select
10229  */
10230 Roo.select = Roo.Element.select;
10231
10232
10233
10234
10235
10236
10237
10238
10239
10240
10241
10242
10243
10244
10245 /*
10246  * Based on:
10247  * Ext JS Library 1.1.1
10248  * Copyright(c) 2006-2007, Ext JS, LLC.
10249  *
10250  * Originally Released Under LGPL - original licence link has changed is not relivant.
10251  *
10252  * Fork - LGPL
10253  * <script type="text/javascript">
10254  */
10255
10256
10257
10258 //Notifies Element that fx methods are available
10259 Roo.enableFx = true;
10260
10261 /**
10262  * @class Roo.Fx
10263  * <p>A class to provide basic animation and visual effects support.  <b>Note:</b> This class is automatically applied
10264  * to the {@link Roo.Element} interface when included, so all effects calls should be performed via Element.
10265  * Conversely, since the effects are not actually defined in Element, Roo.Fx <b>must</b> be included in order for the 
10266  * Element effects to work.</p><br/>
10267  *
10268  * <p>It is important to note that although the Fx methods and many non-Fx Element methods support "method chaining" in that
10269  * they return the Element object itself as the method return value, it is not always possible to mix the two in a single
10270  * method chain.  The Fx methods use an internal effects queue so that each effect can be properly timed and sequenced.
10271  * Non-Fx methods, on the other hand, have no such internal queueing and will always execute immediately.  For this reason,
10272  * while it may be possible to mix certain Fx and non-Fx method calls in a single chain, it may not always provide the
10273  * expected results and should be done with care.</p><br/>
10274  *
10275  * <p>Motion effects support 8-way anchoring, meaning that you can choose one of 8 different anchor points on the Element
10276  * that will serve as either the start or end point of the animation.  Following are all of the supported anchor positions:</p>
10277 <pre>
10278 Value  Description
10279 -----  -----------------------------
10280 tl     The top left corner
10281 t      The center of the top edge
10282 tr     The top right corner
10283 l      The center of the left edge
10284 r      The center of the right edge
10285 bl     The bottom left corner
10286 b      The center of the bottom edge
10287 br     The bottom right corner
10288 </pre>
10289  * <b>Although some Fx methods accept specific custom config parameters, the ones shown in the Config Options section
10290  * below are common options that can be passed to any Fx method.</b>
10291  * @cfg {Function} callback A function called when the effect is finished
10292  * @cfg {Object} scope The scope of the effect function
10293  * @cfg {String} easing A valid Easing value for the effect
10294  * @cfg {String} afterCls A css class to apply after the effect
10295  * @cfg {Number} duration The length of time (in seconds) that the effect should last
10296  * @cfg {Boolean} remove Whether the Element should be removed from the DOM and destroyed after the effect finishes
10297  * @cfg {Boolean} useDisplay Whether to use the <i>display</i> CSS property instead of <i>visibility</i> when hiding Elements (only applies to 
10298  * effects that end with the element being visually hidden, ignored otherwise)
10299  * @cfg {String/Object/Function} afterStyle A style specification string, e.g. "width:100px", or an object in the form {width:"100px"}, or
10300  * a function which returns such a specification that will be applied to the Element after the effect finishes
10301  * @cfg {Boolean} block Whether the effect should block other effects from queueing while it runs
10302  * @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
10303  * @cfg {Boolean} stopFx Whether subsequent effects should be stopped and removed after the current effect finishes
10304  */
10305 Roo.Fx = {
10306         /**
10307          * Slides the element into view.  An anchor point can be optionally passed to set the point of
10308          * origin for the slide effect.  This function automatically handles wrapping the element with
10309          * a fixed-size container if needed.  See the Fx class overview for valid anchor point options.
10310          * Usage:
10311          *<pre><code>
10312 // default: slide the element in from the top
10313 el.slideIn();
10314
10315 // custom: slide the element in from the right with a 2-second duration
10316 el.slideIn('r', { duration: 2 });
10317
10318 // common config options shown with default values
10319 el.slideIn('t', {
10320     easing: 'easeOut',
10321     duration: .5
10322 });
10323 </code></pre>
10324          * @param {String} anchor (optional) One of the valid Fx anchor positions (defaults to top: 't')
10325          * @param {Object} options (optional) Object literal with any of the Fx config options
10326          * @return {Roo.Element} The Element
10327          */
10328     slideIn : function(anchor, o){
10329         var el = this.getFxEl();
10330         o = o || {};
10331
10332         el.queueFx(o, function(){
10333
10334             anchor = anchor || "t";
10335
10336             // fix display to visibility
10337             this.fixDisplay();
10338
10339             // restore values after effect
10340             var r = this.getFxRestore();
10341             var b = this.getBox();
10342             // fixed size for slide
10343             this.setSize(b);
10344
10345             // wrap if needed
10346             var wrap = this.fxWrap(r.pos, o, "hidden");
10347
10348             var st = this.dom.style;
10349             st.visibility = "visible";
10350             st.position = "absolute";
10351
10352             // clear out temp styles after slide and unwrap
10353             var after = function(){
10354                 el.fxUnwrap(wrap, r.pos, o);
10355                 st.width = r.width;
10356                 st.height = r.height;
10357                 el.afterFx(o);
10358             };
10359             // time to calc the positions
10360             var a, pt = {to: [b.x, b.y]}, bw = {to: b.width}, bh = {to: b.height};
10361
10362             switch(anchor.toLowerCase()){
10363                 case "t":
10364                     wrap.setSize(b.width, 0);
10365                     st.left = st.bottom = "0";
10366                     a = {height: bh};
10367                 break;
10368                 case "l":
10369                     wrap.setSize(0, b.height);
10370                     st.right = st.top = "0";
10371                     a = {width: bw};
10372                 break;
10373                 case "r":
10374                     wrap.setSize(0, b.height);
10375                     wrap.setX(b.right);
10376                     st.left = st.top = "0";
10377                     a = {width: bw, points: pt};
10378                 break;
10379                 case "b":
10380                     wrap.setSize(b.width, 0);
10381                     wrap.setY(b.bottom);
10382                     st.left = st.top = "0";
10383                     a = {height: bh, points: pt};
10384                 break;
10385                 case "tl":
10386                     wrap.setSize(0, 0);
10387                     st.right = st.bottom = "0";
10388                     a = {width: bw, height: bh};
10389                 break;
10390                 case "bl":
10391                     wrap.setSize(0, 0);
10392                     wrap.setY(b.y+b.height);
10393                     st.right = st.top = "0";
10394                     a = {width: bw, height: bh, points: pt};
10395                 break;
10396                 case "br":
10397                     wrap.setSize(0, 0);
10398                     wrap.setXY([b.right, b.bottom]);
10399                     st.left = st.top = "0";
10400                     a = {width: bw, height: bh, points: pt};
10401                 break;
10402                 case "tr":
10403                     wrap.setSize(0, 0);
10404                     wrap.setX(b.x+b.width);
10405                     st.left = st.bottom = "0";
10406                     a = {width: bw, height: bh, points: pt};
10407                 break;
10408             }
10409             this.dom.style.visibility = "visible";
10410             wrap.show();
10411
10412             arguments.callee.anim = wrap.fxanim(a,
10413                 o,
10414                 'motion',
10415                 .5,
10416                 'easeOut', after);
10417         });
10418         return this;
10419     },
10420     
10421         /**
10422          * Slides the element out of view.  An anchor point can be optionally passed to set the end point
10423          * for the slide effect.  When the effect is completed, the element will be hidden (visibility = 
10424          * 'hidden') but block elements will still take up space in the document.  The element must be removed
10425          * from the DOM using the 'remove' config option if desired.  This function automatically handles 
10426          * wrapping the element with a fixed-size container if needed.  See the Fx class overview for valid anchor point options.
10427          * Usage:
10428          *<pre><code>
10429 // default: slide the element out to the top
10430 el.slideOut();
10431
10432 // custom: slide the element out to the right with a 2-second duration
10433 el.slideOut('r', { duration: 2 });
10434
10435 // common config options shown with default values
10436 el.slideOut('t', {
10437     easing: 'easeOut',
10438     duration: .5,
10439     remove: false,
10440     useDisplay: false
10441 });
10442 </code></pre>
10443          * @param {String} anchor (optional) One of the valid Fx anchor positions (defaults to top: 't')
10444          * @param {Object} options (optional) Object literal with any of the Fx config options
10445          * @return {Roo.Element} The Element
10446          */
10447     slideOut : function(anchor, o){
10448         var el = this.getFxEl();
10449         o = o || {};
10450
10451         el.queueFx(o, function(){
10452
10453             anchor = anchor || "t";
10454
10455             // restore values after effect
10456             var r = this.getFxRestore();
10457             
10458             var b = this.getBox();
10459             // fixed size for slide
10460             this.setSize(b);
10461
10462             // wrap if needed
10463             var wrap = this.fxWrap(r.pos, o, "visible");
10464
10465             var st = this.dom.style;
10466             st.visibility = "visible";
10467             st.position = "absolute";
10468
10469             wrap.setSize(b);
10470
10471             var after = function(){
10472                 if(o.useDisplay){
10473                     el.setDisplayed(false);
10474                 }else{
10475                     el.hide();
10476                 }
10477
10478                 el.fxUnwrap(wrap, r.pos, o);
10479
10480                 st.width = r.width;
10481                 st.height = r.height;
10482
10483                 el.afterFx(o);
10484             };
10485
10486             var a, zero = {to: 0};
10487             switch(anchor.toLowerCase()){
10488                 case "t":
10489                     st.left = st.bottom = "0";
10490                     a = {height: zero};
10491                 break;
10492                 case "l":
10493                     st.right = st.top = "0";
10494                     a = {width: zero};
10495                 break;
10496                 case "r":
10497                     st.left = st.top = "0";
10498                     a = {width: zero, points: {to:[b.right, b.y]}};
10499                 break;
10500                 case "b":
10501                     st.left = st.top = "0";
10502                     a = {height: zero, points: {to:[b.x, b.bottom]}};
10503                 break;
10504                 case "tl":
10505                     st.right = st.bottom = "0";
10506                     a = {width: zero, height: zero};
10507                 break;
10508                 case "bl":
10509                     st.right = st.top = "0";
10510                     a = {width: zero, height: zero, points: {to:[b.x, b.bottom]}};
10511                 break;
10512                 case "br":
10513                     st.left = st.top = "0";
10514                     a = {width: zero, height: zero, points: {to:[b.x+b.width, b.bottom]}};
10515                 break;
10516                 case "tr":
10517                     st.left = st.bottom = "0";
10518                     a = {width: zero, height: zero, points: {to:[b.right, b.y]}};
10519                 break;
10520             }
10521
10522             arguments.callee.anim = wrap.fxanim(a,
10523                 o,
10524                 'motion',
10525                 .5,
10526                 "easeOut", after);
10527         });
10528         return this;
10529     },
10530
10531         /**
10532          * Fades the element out while slowly expanding it in all directions.  When the effect is completed, the 
10533          * element will be hidden (visibility = 'hidden') but block elements will still take up space in the document. 
10534          * The element must be removed from the DOM using the 'remove' config option if desired.
10535          * Usage:
10536          *<pre><code>
10537 // default
10538 el.puff();
10539
10540 // common config options shown with default values
10541 el.puff({
10542     easing: 'easeOut',
10543     duration: .5,
10544     remove: false,
10545     useDisplay: false
10546 });
10547 </code></pre>
10548          * @param {Object} options (optional) Object literal with any of the Fx config options
10549          * @return {Roo.Element} The Element
10550          */
10551     puff : function(o){
10552         var el = this.getFxEl();
10553         o = o || {};
10554
10555         el.queueFx(o, function(){
10556             this.clearOpacity();
10557             this.show();
10558
10559             // restore values after effect
10560             var r = this.getFxRestore();
10561             var st = this.dom.style;
10562
10563             var after = function(){
10564                 if(o.useDisplay){
10565                     el.setDisplayed(false);
10566                 }else{
10567                     el.hide();
10568                 }
10569
10570                 el.clearOpacity();
10571
10572                 el.setPositioning(r.pos);
10573                 st.width = r.width;
10574                 st.height = r.height;
10575                 st.fontSize = '';
10576                 el.afterFx(o);
10577             };
10578
10579             var width = this.getWidth();
10580             var height = this.getHeight();
10581
10582             arguments.callee.anim = this.fxanim({
10583                     width : {to: this.adjustWidth(width * 2)},
10584                     height : {to: this.adjustHeight(height * 2)},
10585                     points : {by: [-(width * .5), -(height * .5)]},
10586                     opacity : {to: 0},
10587                     fontSize: {to:200, unit: "%"}
10588                 },
10589                 o,
10590                 'motion',
10591                 .5,
10592                 "easeOut", after);
10593         });
10594         return this;
10595     },
10596
10597         /**
10598          * Blinks the element as if it was clicked and then collapses on its center (similar to switching off a television).
10599          * When the effect is completed, the element will be hidden (visibility = 'hidden') but block elements will still 
10600          * take up space in the document. The element must be removed from the DOM using the 'remove' config option if desired.
10601          * Usage:
10602          *<pre><code>
10603 // default
10604 el.switchOff();
10605
10606 // all config options shown with default values
10607 el.switchOff({
10608     easing: 'easeIn',
10609     duration: .3,
10610     remove: false,
10611     useDisplay: false
10612 });
10613 </code></pre>
10614          * @param {Object} options (optional) Object literal with any of the Fx config options
10615          * @return {Roo.Element} The Element
10616          */
10617     switchOff : function(o){
10618         var el = this.getFxEl();
10619         o = o || {};
10620
10621         el.queueFx(o, function(){
10622             this.clearOpacity();
10623             this.clip();
10624
10625             // restore values after effect
10626             var r = this.getFxRestore();
10627             var st = this.dom.style;
10628
10629             var after = function(){
10630                 if(o.useDisplay){
10631                     el.setDisplayed(false);
10632                 }else{
10633                     el.hide();
10634                 }
10635
10636                 el.clearOpacity();
10637                 el.setPositioning(r.pos);
10638                 st.width = r.width;
10639                 st.height = r.height;
10640
10641                 el.afterFx(o);
10642             };
10643
10644             this.fxanim({opacity:{to:0.3}}, null, null, .1, null, function(){
10645                 this.clearOpacity();
10646                 (function(){
10647                     this.fxanim({
10648                         height:{to:1},
10649                         points:{by:[0, this.getHeight() * .5]}
10650                     }, o, 'motion', 0.3, 'easeIn', after);
10651                 }).defer(100, this);
10652             });
10653         });
10654         return this;
10655     },
10656
10657     /**
10658      * Highlights the Element by setting a color (applies to the background-color by default, but can be
10659      * changed using the "attr" config option) and then fading back to the original color. If no original
10660      * color is available, you should provide the "endColor" config option which will be cleared after the animation.
10661      * Usage:
10662 <pre><code>
10663 // default: highlight background to yellow
10664 el.highlight();
10665
10666 // custom: highlight foreground text to blue for 2 seconds
10667 el.highlight("0000ff", { attr: 'color', duration: 2 });
10668
10669 // common config options shown with default values
10670 el.highlight("ffff9c", {
10671     attr: "background-color", //can be any valid CSS property (attribute) that supports a color value
10672     endColor: (current color) or "ffffff",
10673     easing: 'easeIn',
10674     duration: 1
10675 });
10676 </code></pre>
10677      * @param {String} color (optional) The highlight color. Should be a 6 char hex color without the leading # (defaults to yellow: 'ffff9c')
10678      * @param {Object} options (optional) Object literal with any of the Fx config options
10679      * @return {Roo.Element} The Element
10680      */ 
10681     highlight : function(color, o){
10682         var el = this.getFxEl();
10683         o = o || {};
10684
10685         el.queueFx(o, function(){
10686             color = color || "ffff9c";
10687             attr = o.attr || "backgroundColor";
10688
10689             this.clearOpacity();
10690             this.show();
10691
10692             var origColor = this.getColor(attr);
10693             var restoreColor = this.dom.style[attr];
10694             endColor = (o.endColor || origColor) || "ffffff";
10695
10696             var after = function(){
10697                 el.dom.style[attr] = restoreColor;
10698                 el.afterFx(o);
10699             };
10700
10701             var a = {};
10702             a[attr] = {from: color, to: endColor};
10703             arguments.callee.anim = this.fxanim(a,
10704                 o,
10705                 'color',
10706                 1,
10707                 'easeIn', after);
10708         });
10709         return this;
10710     },
10711
10712    /**
10713     * Shows a ripple of exploding, attenuating borders to draw attention to an Element.
10714     * Usage:
10715 <pre><code>
10716 // default: a single light blue ripple
10717 el.frame();
10718
10719 // custom: 3 red ripples lasting 3 seconds total
10720 el.frame("ff0000", 3, { duration: 3 });
10721
10722 // common config options shown with default values
10723 el.frame("C3DAF9", 1, {
10724     duration: 1 //duration of entire animation (not each individual ripple)
10725     // Note: Easing is not configurable and will be ignored if included
10726 });
10727 </code></pre>
10728     * @param {String} color (optional) The color of the border.  Should be a 6 char hex color without the leading # (defaults to light blue: 'C3DAF9').
10729     * @param {Number} count (optional) The number of ripples to display (defaults to 1)
10730     * @param {Object} options (optional) Object literal with any of the Fx config options
10731     * @return {Roo.Element} The Element
10732     */
10733     frame : function(color, count, o){
10734         var el = this.getFxEl();
10735         o = o || {};
10736
10737         el.queueFx(o, function(){
10738             color = color || "#C3DAF9";
10739             if(color.length == 6){
10740                 color = "#" + color;
10741             }
10742             count = count || 1;
10743             duration = o.duration || 1;
10744             this.show();
10745
10746             var b = this.getBox();
10747             var animFn = function(){
10748                 var proxy = this.createProxy({
10749
10750                      style:{
10751                         visbility:"hidden",
10752                         position:"absolute",
10753                         "z-index":"35000", // yee haw
10754                         border:"0px solid " + color
10755                      }
10756                   });
10757                 var scale = Roo.isBorderBox ? 2 : 1;
10758                 proxy.animate({
10759                     top:{from:b.y, to:b.y - 20},
10760                     left:{from:b.x, to:b.x - 20},
10761                     borderWidth:{from:0, to:10},
10762                     opacity:{from:1, to:0},
10763                     height:{from:b.height, to:(b.height + (20*scale))},
10764                     width:{from:b.width, to:(b.width + (20*scale))}
10765                 }, duration, function(){
10766                     proxy.remove();
10767                 });
10768                 if(--count > 0){
10769                      animFn.defer((duration/2)*1000, this);
10770                 }else{
10771                     el.afterFx(o);
10772                 }
10773             };
10774             animFn.call(this);
10775         });
10776         return this;
10777     },
10778
10779    /**
10780     * Creates a pause before any subsequent queued effects begin.  If there are
10781     * no effects queued after the pause it will have no effect.
10782     * Usage:
10783 <pre><code>
10784 el.pause(1);
10785 </code></pre>
10786     * @param {Number} seconds The length of time to pause (in seconds)
10787     * @return {Roo.Element} The Element
10788     */
10789     pause : function(seconds){
10790         var el = this.getFxEl();
10791         var o = {};
10792
10793         el.queueFx(o, function(){
10794             setTimeout(function(){
10795                 el.afterFx(o);
10796             }, seconds * 1000);
10797         });
10798         return this;
10799     },
10800
10801    /**
10802     * Fade an element in (from transparent to opaque).  The ending opacity can be specified
10803     * using the "endOpacity" config option.
10804     * Usage:
10805 <pre><code>
10806 // default: fade in from opacity 0 to 100%
10807 el.fadeIn();
10808
10809 // custom: fade in from opacity 0 to 75% over 2 seconds
10810 el.fadeIn({ endOpacity: .75, duration: 2});
10811
10812 // common config options shown with default values
10813 el.fadeIn({
10814     endOpacity: 1, //can be any value between 0 and 1 (e.g. .5)
10815     easing: 'easeOut',
10816     duration: .5
10817 });
10818 </code></pre>
10819     * @param {Object} options (optional) Object literal with any of the Fx config options
10820     * @return {Roo.Element} The Element
10821     */
10822     fadeIn : function(o){
10823         var el = this.getFxEl();
10824         o = o || {};
10825         el.queueFx(o, function(){
10826             this.setOpacity(0);
10827             this.fixDisplay();
10828             this.dom.style.visibility = 'visible';
10829             var to = o.endOpacity || 1;
10830             arguments.callee.anim = this.fxanim({opacity:{to:to}},
10831                 o, null, .5, "easeOut", function(){
10832                 if(to == 1){
10833                     this.clearOpacity();
10834                 }
10835                 el.afterFx(o);
10836             });
10837         });
10838         return this;
10839     },
10840
10841    /**
10842     * Fade an element out (from opaque to transparent).  The ending opacity can be specified
10843     * using the "endOpacity" config option.
10844     * Usage:
10845 <pre><code>
10846 // default: fade out from the element's current opacity to 0
10847 el.fadeOut();
10848
10849 // custom: fade out from the element's current opacity to 25% over 2 seconds
10850 el.fadeOut({ endOpacity: .25, duration: 2});
10851
10852 // common config options shown with default values
10853 el.fadeOut({
10854     endOpacity: 0, //can be any value between 0 and 1 (e.g. .5)
10855     easing: 'easeOut',
10856     duration: .5
10857     remove: false,
10858     useDisplay: false
10859 });
10860 </code></pre>
10861     * @param {Object} options (optional) Object literal with any of the Fx config options
10862     * @return {Roo.Element} The Element
10863     */
10864     fadeOut : function(o){
10865         var el = this.getFxEl();
10866         o = o || {};
10867         el.queueFx(o, function(){
10868             arguments.callee.anim = this.fxanim({opacity:{to:o.endOpacity || 0}},
10869                 o, null, .5, "easeOut", function(){
10870                 if(this.visibilityMode == Roo.Element.DISPLAY || o.useDisplay){
10871                      this.dom.style.display = "none";
10872                 }else{
10873                      this.dom.style.visibility = "hidden";
10874                 }
10875                 this.clearOpacity();
10876                 el.afterFx(o);
10877             });
10878         });
10879         return this;
10880     },
10881
10882    /**
10883     * Animates the transition of an element's dimensions from a starting height/width
10884     * to an ending height/width.
10885     * Usage:
10886 <pre><code>
10887 // change height and width to 100x100 pixels
10888 el.scale(100, 100);
10889
10890 // common config options shown with default values.  The height and width will default to
10891 // the element's existing values if passed as null.
10892 el.scale(
10893     [element's width],
10894     [element's height], {
10895     easing: 'easeOut',
10896     duration: .35
10897 });
10898 </code></pre>
10899     * @param {Number} width  The new width (pass undefined to keep the original width)
10900     * @param {Number} height  The new height (pass undefined to keep the original height)
10901     * @param {Object} options (optional) Object literal with any of the Fx config options
10902     * @return {Roo.Element} The Element
10903     */
10904     scale : function(w, h, o){
10905         this.shift(Roo.apply({}, o, {
10906             width: w,
10907             height: h
10908         }));
10909         return this;
10910     },
10911
10912    /**
10913     * Animates the transition of any combination of an element's dimensions, xy position and/or opacity.
10914     * Any of these properties not specified in the config object will not be changed.  This effect 
10915     * requires that at least one new dimension, position or opacity setting must be passed in on
10916     * the config object in order for the function to have any effect.
10917     * Usage:
10918 <pre><code>
10919 // slide the element horizontally to x position 200 while changing the height and opacity
10920 el.shift({ x: 200, height: 50, opacity: .8 });
10921
10922 // common config options shown with default values.
10923 el.shift({
10924     width: [element's width],
10925     height: [element's height],
10926     x: [element's x position],
10927     y: [element's y position],
10928     opacity: [element's opacity],
10929     easing: 'easeOut',
10930     duration: .35
10931 });
10932 </code></pre>
10933     * @param {Object} options  Object literal with any of the Fx config options
10934     * @return {Roo.Element} The Element
10935     */
10936     shift : function(o){
10937         var el = this.getFxEl();
10938         o = o || {};
10939         el.queueFx(o, function(){
10940             var a = {}, w = o.width, h = o.height, x = o.x, y = o.y,  op = o.opacity;
10941             if(w !== undefined){
10942                 a.width = {to: this.adjustWidth(w)};
10943             }
10944             if(h !== undefined){
10945                 a.height = {to: this.adjustHeight(h)};
10946             }
10947             if(x !== undefined || y !== undefined){
10948                 a.points = {to: [
10949                     x !== undefined ? x : this.getX(),
10950                     y !== undefined ? y : this.getY()
10951                 ]};
10952             }
10953             if(op !== undefined){
10954                 a.opacity = {to: op};
10955             }
10956             if(o.xy !== undefined){
10957                 a.points = {to: o.xy};
10958             }
10959             arguments.callee.anim = this.fxanim(a,
10960                 o, 'motion', .35, "easeOut", function(){
10961                 el.afterFx(o);
10962             });
10963         });
10964         return this;
10965     },
10966
10967         /**
10968          * Slides the element while fading it out of view.  An anchor point can be optionally passed to set the 
10969          * ending point of the effect.
10970          * Usage:
10971          *<pre><code>
10972 // default: slide the element downward while fading out
10973 el.ghost();
10974
10975 // custom: slide the element out to the right with a 2-second duration
10976 el.ghost('r', { duration: 2 });
10977
10978 // common config options shown with default values
10979 el.ghost('b', {
10980     easing: 'easeOut',
10981     duration: .5
10982     remove: false,
10983     useDisplay: false
10984 });
10985 </code></pre>
10986          * @param {String} anchor (optional) One of the valid Fx anchor positions (defaults to bottom: 'b')
10987          * @param {Object} options (optional) Object literal with any of the Fx config options
10988          * @return {Roo.Element} The Element
10989          */
10990     ghost : function(anchor, o){
10991         var el = this.getFxEl();
10992         o = o || {};
10993
10994         el.queueFx(o, function(){
10995             anchor = anchor || "b";
10996
10997             // restore values after effect
10998             var r = this.getFxRestore();
10999             var w = this.getWidth(),
11000                 h = this.getHeight();
11001
11002             var st = this.dom.style;
11003
11004             var after = function(){
11005                 if(o.useDisplay){
11006                     el.setDisplayed(false);
11007                 }else{
11008                     el.hide();
11009                 }
11010
11011                 el.clearOpacity();
11012                 el.setPositioning(r.pos);
11013                 st.width = r.width;
11014                 st.height = r.height;
11015
11016                 el.afterFx(o);
11017             };
11018
11019             var a = {opacity: {to: 0}, points: {}}, pt = a.points;
11020             switch(anchor.toLowerCase()){
11021                 case "t":
11022                     pt.by = [0, -h];
11023                 break;
11024                 case "l":
11025                     pt.by = [-w, 0];
11026                 break;
11027                 case "r":
11028                     pt.by = [w, 0];
11029                 break;
11030                 case "b":
11031                     pt.by = [0, h];
11032                 break;
11033                 case "tl":
11034                     pt.by = [-w, -h];
11035                 break;
11036                 case "bl":
11037                     pt.by = [-w, h];
11038                 break;
11039                 case "br":
11040                     pt.by = [w, h];
11041                 break;
11042                 case "tr":
11043                     pt.by = [w, -h];
11044                 break;
11045             }
11046
11047             arguments.callee.anim = this.fxanim(a,
11048                 o,
11049                 'motion',
11050                 .5,
11051                 "easeOut", after);
11052         });
11053         return this;
11054     },
11055
11056         /**
11057          * Ensures that all effects queued after syncFx is called on the element are
11058          * run concurrently.  This is the opposite of {@link #sequenceFx}.
11059          * @return {Roo.Element} The Element
11060          */
11061     syncFx : function(){
11062         this.fxDefaults = Roo.apply(this.fxDefaults || {}, {
11063             block : false,
11064             concurrent : true,
11065             stopFx : false
11066         });
11067         return this;
11068     },
11069
11070         /**
11071          * Ensures that all effects queued after sequenceFx is called on the element are
11072          * run in sequence.  This is the opposite of {@link #syncFx}.
11073          * @return {Roo.Element} The Element
11074          */
11075     sequenceFx : function(){
11076         this.fxDefaults = Roo.apply(this.fxDefaults || {}, {
11077             block : false,
11078             concurrent : false,
11079             stopFx : false
11080         });
11081         return this;
11082     },
11083
11084         /* @private */
11085     nextFx : function(){
11086         var ef = this.fxQueue[0];
11087         if(ef){
11088             ef.call(this);
11089         }
11090     },
11091
11092         /**
11093          * Returns true if the element has any effects actively running or queued, else returns false.
11094          * @return {Boolean} True if element has active effects, else false
11095          */
11096     hasActiveFx : function(){
11097         return this.fxQueue && this.fxQueue[0];
11098     },
11099
11100         /**
11101          * Stops any running effects and clears the element's internal effects queue if it contains
11102          * any additional effects that haven't started yet.
11103          * @return {Roo.Element} The Element
11104          */
11105     stopFx : function(){
11106         if(this.hasActiveFx()){
11107             var cur = this.fxQueue[0];
11108             if(cur && cur.anim && cur.anim.isAnimated()){
11109                 this.fxQueue = [cur]; // clear out others
11110                 cur.anim.stop(true);
11111             }
11112         }
11113         return this;
11114     },
11115
11116         /* @private */
11117     beforeFx : function(o){
11118         if(this.hasActiveFx() && !o.concurrent){
11119            if(o.stopFx){
11120                this.stopFx();
11121                return true;
11122            }
11123            return false;
11124         }
11125         return true;
11126     },
11127
11128         /**
11129          * Returns true if the element is currently blocking so that no other effect can be queued
11130          * until this effect is finished, else returns false if blocking is not set.  This is commonly
11131          * used to ensure that an effect initiated by a user action runs to completion prior to the
11132          * same effect being restarted (e.g., firing only one effect even if the user clicks several times).
11133          * @return {Boolean} True if blocking, else false
11134          */
11135     hasFxBlock : function(){
11136         var q = this.fxQueue;
11137         return q && q[0] && q[0].block;
11138     },
11139
11140         /* @private */
11141     queueFx : function(o, fn){
11142         if(!this.fxQueue){
11143             this.fxQueue = [];
11144         }
11145         if(!this.hasFxBlock()){
11146             Roo.applyIf(o, this.fxDefaults);
11147             if(!o.concurrent){
11148                 var run = this.beforeFx(o);
11149                 fn.block = o.block;
11150                 this.fxQueue.push(fn);
11151                 if(run){
11152                     this.nextFx();
11153                 }
11154             }else{
11155                 fn.call(this);
11156             }
11157         }
11158         return this;
11159     },
11160
11161         /* @private */
11162     fxWrap : function(pos, o, vis){
11163         var wrap;
11164         if(!o.wrap || !(wrap = Roo.get(o.wrap))){
11165             var wrapXY;
11166             if(o.fixPosition){
11167                 wrapXY = this.getXY();
11168             }
11169             var div = document.createElement("div");
11170             div.style.visibility = vis;
11171             wrap = Roo.get(this.dom.parentNode.insertBefore(div, this.dom));
11172             wrap.setPositioning(pos);
11173             if(wrap.getStyle("position") == "static"){
11174                 wrap.position("relative");
11175             }
11176             this.clearPositioning('auto');
11177             wrap.clip();
11178             wrap.dom.appendChild(this.dom);
11179             if(wrapXY){
11180                 wrap.setXY(wrapXY);
11181             }
11182         }
11183         return wrap;
11184     },
11185
11186         /* @private */
11187     fxUnwrap : function(wrap, pos, o){
11188         this.clearPositioning();
11189         this.setPositioning(pos);
11190         if(!o.wrap){
11191             wrap.dom.parentNode.insertBefore(this.dom, wrap.dom);
11192             wrap.remove();
11193         }
11194     },
11195
11196         /* @private */
11197     getFxRestore : function(){
11198         var st = this.dom.style;
11199         return {pos: this.getPositioning(), width: st.width, height : st.height};
11200     },
11201
11202         /* @private */
11203     afterFx : function(o){
11204         if(o.afterStyle){
11205             this.applyStyles(o.afterStyle);
11206         }
11207         if(o.afterCls){
11208             this.addClass(o.afterCls);
11209         }
11210         if(o.remove === true){
11211             this.remove();
11212         }
11213         Roo.callback(o.callback, o.scope, [this]);
11214         if(!o.concurrent){
11215             this.fxQueue.shift();
11216             this.nextFx();
11217         }
11218     },
11219
11220         /* @private */
11221     getFxEl : function(){ // support for composite element fx
11222         return Roo.get(this.dom);
11223     },
11224
11225         /* @private */
11226     fxanim : function(args, opt, animType, defaultDur, defaultEase, cb){
11227         animType = animType || 'run';
11228         opt = opt || {};
11229         var anim = Roo.lib.Anim[animType](
11230             this.dom, args,
11231             (opt.duration || defaultDur) || .35,
11232             (opt.easing || defaultEase) || 'easeOut',
11233             function(){
11234                 Roo.callback(cb, this);
11235             },
11236             this
11237         );
11238         opt.anim = anim;
11239         return anim;
11240     }
11241 };
11242
11243 // backwords compat
11244 Roo.Fx.resize = Roo.Fx.scale;
11245
11246 //When included, Roo.Fx is automatically applied to Element so that all basic
11247 //effects are available directly via the Element API
11248 Roo.apply(Roo.Element.prototype, Roo.Fx);/*
11249  * Based on:
11250  * Ext JS Library 1.1.1
11251  * Copyright(c) 2006-2007, Ext JS, LLC.
11252  *
11253  * Originally Released Under LGPL - original licence link has changed is not relivant.
11254  *
11255  * Fork - LGPL
11256  * <script type="text/javascript">
11257  */
11258
11259
11260 /**
11261  * @class Roo.CompositeElement
11262  * Standard composite class. Creates a Roo.Element for every element in the collection.
11263  * <br><br>
11264  * <b>NOTE: Although they are not listed, this class supports all of the set/update methods of Roo.Element. All Roo.Element
11265  * actions will be performed on all the elements in this collection.</b>
11266  * <br><br>
11267  * All methods return <i>this</i> and can be chained.
11268  <pre><code>
11269  var els = Roo.select("#some-el div.some-class", true);
11270  // or select directly from an existing element
11271  var el = Roo.get('some-el');
11272  el.select('div.some-class', true);
11273
11274  els.setWidth(100); // all elements become 100 width
11275  els.hide(true); // all elements fade out and hide
11276  // or
11277  els.setWidth(100).hide(true);
11278  </code></pre>
11279  */
11280 Roo.CompositeElement = function(els){
11281     this.elements = [];
11282     this.addElements(els);
11283 };
11284 Roo.CompositeElement.prototype = {
11285     isComposite: true,
11286     addElements : function(els){
11287         if(!els) {
11288             return this;
11289         }
11290         if(typeof els == "string"){
11291             els = Roo.Element.selectorFunction(els);
11292         }
11293         var yels = this.elements;
11294         var index = yels.length-1;
11295         for(var i = 0, len = els.length; i < len; i++) {
11296                 yels[++index] = Roo.get(els[i]);
11297         }
11298         return this;
11299     },
11300
11301     /**
11302     * Clears this composite and adds the elements returned by the passed selector.
11303     * @param {String/Array} els A string CSS selector, an array of elements or an element
11304     * @return {CompositeElement} this
11305     */
11306     fill : function(els){
11307         this.elements = [];
11308         this.add(els);
11309         return this;
11310     },
11311
11312     /**
11313     * Filters this composite to only elements that match the passed selector.
11314     * @param {String} selector A string CSS selector
11315     * @param {Boolean} inverse return inverse filter (not matches)
11316     * @return {CompositeElement} this
11317     */
11318     filter : function(selector, inverse){
11319         var els = [];
11320         inverse = inverse || false;
11321         this.each(function(el){
11322             var match = inverse ? !el.is(selector) : el.is(selector);
11323             if(match){
11324                 els[els.length] = el.dom;
11325             }
11326         });
11327         this.fill(els);
11328         return this;
11329     },
11330
11331     invoke : function(fn, args){
11332         var els = this.elements;
11333         for(var i = 0, len = els.length; i < len; i++) {
11334                 Roo.Element.prototype[fn].apply(els[i], args);
11335         }
11336         return this;
11337     },
11338     /**
11339     * Adds elements to this composite.
11340     * @param {String/Array} els A string CSS selector, an array of elements or an element
11341     * @return {CompositeElement} this
11342     */
11343     add : function(els){
11344         if(typeof els == "string"){
11345             this.addElements(Roo.Element.selectorFunction(els));
11346         }else if(els.length !== undefined){
11347             this.addElements(els);
11348         }else{
11349             this.addElements([els]);
11350         }
11351         return this;
11352     },
11353     /**
11354     * Calls the passed function passing (el, this, index) for each element in this composite.
11355     * @param {Function} fn The function to call
11356     * @param {Object} scope (optional) The <i>this</i> object (defaults to the element)
11357     * @return {CompositeElement} this
11358     */
11359     each : function(fn, scope){
11360         var els = this.elements;
11361         for(var i = 0, len = els.length; i < len; i++){
11362             if(fn.call(scope || els[i], els[i], this, i) === false) {
11363                 break;
11364             }
11365         }
11366         return this;
11367     },
11368
11369     /**
11370      * Returns the Element object at the specified index
11371      * @param {Number} index
11372      * @return {Roo.Element}
11373      */
11374     item : function(index){
11375         return this.elements[index] || null;
11376     },
11377
11378     /**
11379      * Returns the first Element
11380      * @return {Roo.Element}
11381      */
11382     first : function(){
11383         return this.item(0);
11384     },
11385
11386     /**
11387      * Returns the last Element
11388      * @return {Roo.Element}
11389      */
11390     last : function(){
11391         return this.item(this.elements.length-1);
11392     },
11393
11394     /**
11395      * Returns the number of elements in this composite
11396      * @return Number
11397      */
11398     getCount : function(){
11399         return this.elements.length;
11400     },
11401
11402     /**
11403      * Returns true if this composite contains the passed element
11404      * @return Boolean
11405      */
11406     contains : function(el){
11407         return this.indexOf(el) !== -1;
11408     },
11409
11410     /**
11411      * Returns true if this composite contains the passed element
11412      * @return Boolean
11413      */
11414     indexOf : function(el){
11415         return this.elements.indexOf(Roo.get(el));
11416     },
11417
11418
11419     /**
11420     * Removes the specified element(s).
11421     * @param {Mixed} el The id of an element, the Element itself, the index of the element in this composite
11422     * or an array of any of those.
11423     * @param {Boolean} removeDom (optional) True to also remove the element from the document
11424     * @return {CompositeElement} this
11425     */
11426     removeElement : function(el, removeDom){
11427         if(el instanceof Array){
11428             for(var i = 0, len = el.length; i < len; i++){
11429                 this.removeElement(el[i]);
11430             }
11431             return this;
11432         }
11433         var index = typeof el == 'number' ? el : this.indexOf(el);
11434         if(index !== -1){
11435             if(removeDom){
11436                 var d = this.elements[index];
11437                 if(d.dom){
11438                     d.remove();
11439                 }else{
11440                     d.parentNode.removeChild(d);
11441                 }
11442             }
11443             this.elements.splice(index, 1);
11444         }
11445         return this;
11446     },
11447
11448     /**
11449     * Replaces the specified element with the passed element.
11450     * @param {String/HTMLElement/Element/Number} el The id of an element, the Element itself, the index of the element in this composite
11451     * to replace.
11452     * @param {String/HTMLElement/Element} replacement The id of an element or the Element itself.
11453     * @param {Boolean} domReplace (Optional) True to remove and replace the element in the document too.
11454     * @return {CompositeElement} this
11455     */
11456     replaceElement : function(el, replacement, domReplace){
11457         var index = typeof el == 'number' ? el : this.indexOf(el);
11458         if(index !== -1){
11459             if(domReplace){
11460                 this.elements[index].replaceWith(replacement);
11461             }else{
11462                 this.elements.splice(index, 1, Roo.get(replacement))
11463             }
11464         }
11465         return this;
11466     },
11467
11468     /**
11469      * Removes all elements.
11470      */
11471     clear : function(){
11472         this.elements = [];
11473     }
11474 };
11475 (function(){
11476     Roo.CompositeElement.createCall = function(proto, fnName){
11477         if(!proto[fnName]){
11478             proto[fnName] = function(){
11479                 return this.invoke(fnName, arguments);
11480             };
11481         }
11482     };
11483     for(var fnName in Roo.Element.prototype){
11484         if(typeof Roo.Element.prototype[fnName] == "function"){
11485             Roo.CompositeElement.createCall(Roo.CompositeElement.prototype, fnName);
11486         }
11487     };
11488 })();
11489 /*
11490  * Based on:
11491  * Ext JS Library 1.1.1
11492  * Copyright(c) 2006-2007, Ext JS, LLC.
11493  *
11494  * Originally Released Under LGPL - original licence link has changed is not relivant.
11495  *
11496  * Fork - LGPL
11497  * <script type="text/javascript">
11498  */
11499
11500 /**
11501  * @class Roo.CompositeElementLite
11502  * @extends Roo.CompositeElement
11503  * Flyweight composite class. Reuses the same Roo.Element for element operations.
11504  <pre><code>
11505  var els = Roo.select("#some-el div.some-class");
11506  // or select directly from an existing element
11507  var el = Roo.get('some-el');
11508  el.select('div.some-class');
11509
11510  els.setWidth(100); // all elements become 100 width
11511  els.hide(true); // all elements fade out and hide
11512  // or
11513  els.setWidth(100).hide(true);
11514  </code></pre><br><br>
11515  * <b>NOTE: Although they are not listed, this class supports all of the set/update methods of Roo.Element. All Roo.Element
11516  * actions will be performed on all the elements in this collection.</b>
11517  */
11518 Roo.CompositeElementLite = function(els){
11519     Roo.CompositeElementLite.superclass.constructor.call(this, els);
11520     this.el = new Roo.Element.Flyweight();
11521 };
11522 Roo.extend(Roo.CompositeElementLite, Roo.CompositeElement, {
11523     addElements : function(els){
11524         if(els){
11525             if(els instanceof Array){
11526                 this.elements = this.elements.concat(els);
11527             }else{
11528                 var yels = this.elements;
11529                 var index = yels.length-1;
11530                 for(var i = 0, len = els.length; i < len; i++) {
11531                     yels[++index] = els[i];
11532                 }
11533             }
11534         }
11535         return this;
11536     },
11537     invoke : function(fn, args){
11538         var els = this.elements;
11539         var el = this.el;
11540         for(var i = 0, len = els.length; i < len; i++) {
11541             el.dom = els[i];
11542                 Roo.Element.prototype[fn].apply(el, args);
11543         }
11544         return this;
11545     },
11546     /**
11547      * Returns a flyweight Element of the dom element object at the specified index
11548      * @param {Number} index
11549      * @return {Roo.Element}
11550      */
11551     item : function(index){
11552         if(!this.elements[index]){
11553             return null;
11554         }
11555         this.el.dom = this.elements[index];
11556         return this.el;
11557     },
11558
11559     // fixes scope with flyweight
11560     addListener : function(eventName, handler, scope, opt){
11561         var els = this.elements;
11562         for(var i = 0, len = els.length; i < len; i++) {
11563             Roo.EventManager.on(els[i], eventName, handler, scope || els[i], opt);
11564         }
11565         return this;
11566     },
11567
11568     /**
11569     * Calls the passed function passing (el, this, index) for each element in this composite. <b>The element
11570     * passed is the flyweight (shared) Roo.Element instance, so if you require a
11571     * a reference to the dom node, use el.dom.</b>
11572     * @param {Function} fn The function to call
11573     * @param {Object} scope (optional) The <i>this</i> object (defaults to the element)
11574     * @return {CompositeElement} this
11575     */
11576     each : function(fn, scope){
11577         var els = this.elements;
11578         var el = this.el;
11579         for(var i = 0, len = els.length; i < len; i++){
11580             el.dom = els[i];
11581                 if(fn.call(scope || el, el, this, i) === false){
11582                 break;
11583             }
11584         }
11585         return this;
11586     },
11587
11588     indexOf : function(el){
11589         return this.elements.indexOf(Roo.getDom(el));
11590     },
11591
11592     replaceElement : function(el, replacement, domReplace){
11593         var index = typeof el == 'number' ? el : this.indexOf(el);
11594         if(index !== -1){
11595             replacement = Roo.getDom(replacement);
11596             if(domReplace){
11597                 var d = this.elements[index];
11598                 d.parentNode.insertBefore(replacement, d);
11599                 d.parentNode.removeChild(d);
11600             }
11601             this.elements.splice(index, 1, replacement);
11602         }
11603         return this;
11604     }
11605 });
11606 Roo.CompositeElementLite.prototype.on = Roo.CompositeElementLite.prototype.addListener;
11607
11608 /*
11609  * Based on:
11610  * Ext JS Library 1.1.1
11611  * Copyright(c) 2006-2007, Ext JS, LLC.
11612  *
11613  * Originally Released Under LGPL - original licence link has changed is not relivant.
11614  *
11615  * Fork - LGPL
11616  * <script type="text/javascript">
11617  */
11618
11619  
11620
11621 /**
11622  * @class Roo.data.Connection
11623  * @extends Roo.util.Observable
11624  * The class encapsulates a connection to the page's originating domain, allowing requests to be made
11625  * either to a configured URL, or to a URL specified at request time. 
11626  * 
11627  * Requests made by this class are asynchronous, and will return immediately. No data from
11628  * the server will be available to the statement immediately following the {@link #request} call.
11629  * To process returned data, use a callback in the request options object, or an event listener.
11630  * 
11631  * Note: If you are doing a file upload, you will not get a normal response object sent back to
11632  * your callback or event handler.  Since the upload is handled via in IFRAME, there is no XMLHttpRequest.
11633  * The response object is created using the innerHTML of the IFRAME's document as the responseText
11634  * property and, if present, the IFRAME's XML document as the responseXML property.
11635  * 
11636  * This means that a valid XML or HTML document must be returned. If JSON data is required, it is suggested
11637  * that it be placed either inside a &lt;textarea> in an HTML document and retrieved from the responseText
11638  * using a regex, or inside a CDATA section in an XML document and retrieved from the responseXML using
11639  * standard DOM methods.
11640  * @constructor
11641  * @param {Object} config a configuration object.
11642  */
11643 Roo.data.Connection = function(config){
11644     Roo.apply(this, config);
11645     this.addEvents({
11646         /**
11647          * @event beforerequest
11648          * Fires before a network request is made to retrieve a data object.
11649          * @param {Connection} conn This Connection object.
11650          * @param {Object} options The options config object passed to the {@link #request} method.
11651          */
11652         "beforerequest" : true,
11653         /**
11654          * @event requestcomplete
11655          * Fires if the request was successfully completed.
11656          * @param {Connection} conn This Connection object.
11657          * @param {Object} response The XHR object containing the response data.
11658          * See {@link http://www.w3.org/TR/XMLHttpRequest/} for details.
11659          * @param {Object} options The options config object passed to the {@link #request} method.
11660          */
11661         "requestcomplete" : true,
11662         /**
11663          * @event requestexception
11664          * Fires if an error HTTP status was returned from the server.
11665          * See {@link http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html} for details of HTTP status codes.
11666          * @param {Connection} conn This Connection object.
11667          * @param {Object} response The XHR object containing the response data.
11668          * See {@link http://www.w3.org/TR/XMLHttpRequest/} for details.
11669          * @param {Object} options The options config object passed to the {@link #request} method.
11670          */
11671         "requestexception" : true
11672     });
11673     Roo.data.Connection.superclass.constructor.call(this);
11674 };
11675
11676 Roo.extend(Roo.data.Connection, Roo.util.Observable, {
11677     /**
11678      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
11679      */
11680     /**
11681      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
11682      * extra parameters to each request made by this object. (defaults to undefined)
11683      */
11684     /**
11685      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
11686      *  to each request made by this object. (defaults to undefined)
11687      */
11688     /**
11689      * @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)
11690      */
11691     /**
11692      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
11693      */
11694     timeout : 30000,
11695     /**
11696      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
11697      * @type Boolean
11698      */
11699     autoAbort:false,
11700
11701     /**
11702      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
11703      * @type Boolean
11704      */
11705     disableCaching: true,
11706
11707     /**
11708      * Sends an HTTP request to a remote server.
11709      * @param {Object} options An object which may contain the following properties:<ul>
11710      * <li><b>url</b> {String} (Optional) The URL to which to send the request. Defaults to configured URL</li>
11711      * <li><b>params</b> {Object/String/Function} (Optional) An object containing properties which are used as parameters to the
11712      * request, a url encoded string or a function to call to get either.</li>
11713      * <li><b>method</b> {String} (Optional) The HTTP method to use for the request. Defaults to the configured method, or
11714      * if no method was configured, "GET" if no parameters are being sent, and "POST" if parameters are being sent.</li>
11715      * <li><b>callback</b> {Function} (Optional) The function to be called upon receipt of the HTTP response.
11716      * The callback is called regardless of success or failure and is passed the following parameters:<ul>
11717      * <li>options {Object} The parameter to the request call.</li>
11718      * <li>success {Boolean} True if the request succeeded.</li>
11719      * <li>response {Object} The XMLHttpRequest object containing the response data.</li>
11720      * </ul></li>
11721      * <li><b>success</b> {Function} (Optional) The function to be called upon success of the request.
11722      * The callback is passed the following parameters:<ul>
11723      * <li>response {Object} The XMLHttpRequest object containing the response data.</li>
11724      * <li>options {Object} The parameter to the request call.</li>
11725      * </ul></li>
11726      * <li><b>failure</b> {Function} (Optional) The function to be called upon failure of the request.
11727      * The callback is passed the following parameters:<ul>
11728      * <li>response {Object} The XMLHttpRequest object containing the response data.</li>
11729      * <li>options {Object} The parameter to the request call.</li>
11730      * </ul></li>
11731      * <li><b>scope</b> {Object} (Optional) The scope in which to execute the callbacks: The "this" object
11732      * for the callback function. Defaults to the browser window.</li>
11733      * <li><b>form</b> {Object/String} (Optional) A form object or id to pull parameters from.</li>
11734      * <li><b>isUpload</b> {Boolean} (Optional) True if the form object is a file upload (will usually be automatically detected).</li>
11735      * <li><b>headers</b> {Object} (Optional) Request headers to set for the request.</li>
11736      * <li><b>xmlData</b> {Object} (Optional) XML document to use for the post. Note: This will be used instead of
11737      * params for the post data. Any params will be appended to the URL.</li>
11738      * <li><b>disableCaching</b> {Boolean} (Optional) True to add a unique cache-buster param to GET requests.</li>
11739      * </ul>
11740      * @return {Number} transactionId
11741      */
11742     request : function(o){
11743         if(this.fireEvent("beforerequest", this, o) !== false){
11744             var p = o.params;
11745
11746             if(typeof p == "function"){
11747                 p = p.call(o.scope||window, o);
11748             }
11749             if(typeof p == "object"){
11750                 p = Roo.urlEncode(o.params);
11751             }
11752             if(this.extraParams){
11753                 var extras = Roo.urlEncode(this.extraParams);
11754                 p = p ? (p + '&' + extras) : extras;
11755             }
11756
11757             var url = o.url || this.url;
11758             if(typeof url == 'function'){
11759                 url = url.call(o.scope||window, o);
11760             }
11761
11762             if(o.form){
11763                 var form = Roo.getDom(o.form);
11764                 url = url || form.action;
11765
11766                 var enctype = form.getAttribute("enctype");
11767                 
11768                 if (o.formData) {
11769                     return this.doFormDataUpload(o, url);
11770                 }
11771                 
11772                 if(o.isUpload || (enctype && enctype.toLowerCase() == 'multipart/form-data')){
11773                     return this.doFormUpload(o, p, url);
11774                 }
11775                 var f = Roo.lib.Ajax.serializeForm(form);
11776                 p = p ? (p + '&' + f) : f;
11777             }
11778             
11779             if (!o.form && o.formData) {
11780                 o.formData = o.formData === true ? new FormData() : o.formData;
11781                 for (var k in o.params) {
11782                     o.formData.append(k,o.params[k]);
11783                 }
11784                     
11785                 return this.doFormDataUpload(o, url);
11786             }
11787             
11788
11789             var hs = o.headers;
11790             if(this.defaultHeaders){
11791                 hs = Roo.apply(hs || {}, this.defaultHeaders);
11792                 if(!o.headers){
11793                     o.headers = hs;
11794                 }
11795             }
11796
11797             var cb = {
11798                 success: this.handleResponse,
11799                 failure: this.handleFailure,
11800                 scope: this,
11801                 argument: {options: o},
11802                 timeout : o.timeout || this.timeout
11803             };
11804
11805             var method = o.method||this.method||(p ? "POST" : "GET");
11806
11807             if(method == 'GET' && (this.disableCaching && o.disableCaching !== false) || o.disableCaching === true){
11808                 url += (url.indexOf('?') != -1 ? '&' : '?') + '_dc=' + (new Date().getTime());
11809             }
11810
11811             if(typeof o.autoAbort == 'boolean'){ // options gets top priority
11812                 if(o.autoAbort){
11813                     this.abort();
11814                 }
11815             }else if(this.autoAbort !== false){
11816                 this.abort();
11817             }
11818
11819             if((method == 'GET' && p) || o.xmlData){
11820                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
11821                 p = '';
11822             }
11823             Roo.lib.Ajax.useDefaultHeader = typeof(o.headers) == 'undefined' || typeof(o.headers['Content-Type']) == 'undefined';
11824             this.transId = Roo.lib.Ajax.request(method, url, cb, p, o);
11825             Roo.lib.Ajax.useDefaultHeader == true;
11826             return this.transId;
11827         }else{
11828             Roo.callback(o.callback, o.scope, [o, null, null]);
11829             return null;
11830         }
11831     },
11832
11833     /**
11834      * Determine whether this object has a request outstanding.
11835      * @param {Number} transactionId (Optional) defaults to the last transaction
11836      * @return {Boolean} True if there is an outstanding request.
11837      */
11838     isLoading : function(transId){
11839         if(transId){
11840             return Roo.lib.Ajax.isCallInProgress(transId);
11841         }else{
11842             return this.transId ? true : false;
11843         }
11844     },
11845
11846     /**
11847      * Aborts any outstanding request.
11848      * @param {Number} transactionId (Optional) defaults to the last transaction
11849      */
11850     abort : function(transId){
11851         if(transId || this.isLoading()){
11852             Roo.lib.Ajax.abort(transId || this.transId);
11853         }
11854     },
11855
11856     // private
11857     handleResponse : function(response){
11858         this.transId = false;
11859         var options = response.argument.options;
11860         response.argument = options ? options.argument : null;
11861         this.fireEvent("requestcomplete", this, response, options);
11862         Roo.callback(options.success, options.scope, [response, options]);
11863         Roo.callback(options.callback, options.scope, [options, true, response]);
11864     },
11865
11866     // private
11867     handleFailure : function(response, e){
11868         this.transId = false;
11869         var options = response.argument.options;
11870         response.argument = options ? options.argument : null;
11871         this.fireEvent("requestexception", this, response, options, e);
11872         Roo.callback(options.failure, options.scope, [response, options]);
11873         Roo.callback(options.callback, options.scope, [options, false, response]);
11874     },
11875
11876     // private
11877     doFormUpload : function(o, ps, url){
11878         var id = Roo.id();
11879         var frame = document.createElement('iframe');
11880         frame.id = id;
11881         frame.name = id;
11882         frame.className = 'x-hidden';
11883         if(Roo.isIE){
11884             frame.src = Roo.SSL_SECURE_URL;
11885         }
11886         document.body.appendChild(frame);
11887
11888         if(Roo.isIE){
11889            document.frames[id].name = id;
11890         }
11891
11892         var form = Roo.getDom(o.form);
11893         form.target = id;
11894         form.method = 'POST';
11895         form.enctype = form.encoding = 'multipart/form-data';
11896         if(url){
11897             form.action = url;
11898         }
11899
11900         var hiddens, hd;
11901         if(ps){ // add dynamic params
11902             hiddens = [];
11903             ps = Roo.urlDecode(ps, false);
11904             for(var k in ps){
11905                 if(ps.hasOwnProperty(k)){
11906                     hd = document.createElement('input');
11907                     hd.type = 'hidden';
11908                     hd.name = k;
11909                     hd.value = ps[k];
11910                     form.appendChild(hd);
11911                     hiddens.push(hd);
11912                 }
11913             }
11914         }
11915
11916         function cb(){
11917             var r = {  // bogus response object
11918                 responseText : '',
11919                 responseXML : null
11920             };
11921
11922             r.argument = o ? o.argument : null;
11923
11924             try { //
11925                 var doc;
11926                 if(Roo.isIE){
11927                     doc = frame.contentWindow.document;
11928                 }else {
11929                     doc = (frame.contentDocument || window.frames[id].document);
11930                 }
11931                 if(doc && doc.body){
11932                     r.responseText = doc.body.innerHTML;
11933                 }
11934                 if(doc && doc.XMLDocument){
11935                     r.responseXML = doc.XMLDocument;
11936                 }else {
11937                     r.responseXML = doc;
11938                 }
11939             }
11940             catch(e) {
11941                 // ignore
11942             }
11943
11944             Roo.EventManager.removeListener(frame, 'load', cb, this);
11945
11946             this.fireEvent("requestcomplete", this, r, o);
11947             Roo.callback(o.success, o.scope, [r, o]);
11948             Roo.callback(o.callback, o.scope, [o, true, r]);
11949
11950             setTimeout(function(){document.body.removeChild(frame);}, 100);
11951         }
11952
11953         Roo.EventManager.on(frame, 'load', cb, this);
11954         form.submit();
11955
11956         if(hiddens){ // remove dynamic params
11957             for(var i = 0, len = hiddens.length; i < len; i++){
11958                 form.removeChild(hiddens[i]);
11959             }
11960         }
11961     },
11962     // this is a 'formdata version???'
11963     
11964     
11965     doFormDataUpload : function(o,  url)
11966     {
11967         var formData;
11968         if (o.form) {
11969             var form =  Roo.getDom(o.form);
11970             form.enctype = form.encoding = 'multipart/form-data';
11971             formData = o.formData === true ? new FormData(form) : o.formData;
11972         } else {
11973             formData = o.formData === true ? new FormData() : o.formData;
11974         }
11975         
11976       
11977         var cb = {
11978             success: this.handleResponse,
11979             failure: this.handleFailure,
11980             scope: this,
11981             argument: {options: o},
11982             timeout : o.timeout || this.timeout
11983         };
11984  
11985         if(typeof o.autoAbort == 'boolean'){ // options gets top priority
11986             if(o.autoAbort){
11987                 this.abort();
11988             }
11989         }else if(this.autoAbort !== false){
11990             this.abort();
11991         }
11992
11993         //Roo.lib.Ajax.defaultPostHeader = null;
11994         Roo.lib.Ajax.useDefaultHeader = false;
11995         this.transId = Roo.lib.Ajax.request( "POST", url, cb,  formData, o);
11996         Roo.lib.Ajax.useDefaultHeader = true;
11997  
11998          
11999     }
12000     
12001 });
12002 /*
12003  * Based on:
12004  * Ext JS Library 1.1.1
12005  * Copyright(c) 2006-2007, Ext JS, LLC.
12006  *
12007  * Originally Released Under LGPL - original licence link has changed is not relivant.
12008  *
12009  * Fork - LGPL
12010  * <script type="text/javascript">
12011  */
12012  
12013 /**
12014  * Global Ajax request class.
12015  * 
12016  * @class Roo.Ajax
12017  * @extends Roo.data.Connection
12018  * @static
12019  * 
12020  * @cfg {String} url  The default URL to be used for requests to the server. (defaults to undefined)
12021  * @cfg {Object} extraParams  An object containing properties which are used as extra parameters to each request made by this object. (defaults to undefined)
12022  * @cfg {Object} defaultHeaders  An object containing request headers which are added to each request made by this object. (defaults to undefined)
12023  * @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)
12024  * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
12025  * @cfg {Boolean} autoAbort (Optional) Whether a new request should abort any pending requests. (defaults to false)
12026  * @cfg {Boolean} disableCaching (Optional)   True to add a unique cache-buster param to GET requests. (defaults to true)
12027  */
12028 Roo.Ajax = new Roo.data.Connection({
12029     // fix up the docs
12030     /**
12031      * @scope Roo.Ajax
12032      * @type {Boolear} 
12033      */
12034     autoAbort : false,
12035
12036     /**
12037      * Serialize the passed form into a url encoded string
12038      * @scope Roo.Ajax
12039      * @param {String/HTMLElement} form
12040      * @return {String}
12041      */
12042     serializeForm : function(form){
12043         return Roo.lib.Ajax.serializeForm(form);
12044     }
12045 });/*
12046  * Based on:
12047  * Ext JS Library 1.1.1
12048  * Copyright(c) 2006-2007, Ext JS, LLC.
12049  *
12050  * Originally Released Under LGPL - original licence link has changed is not relivant.
12051  *
12052  * Fork - LGPL
12053  * <script type="text/javascript">
12054  */
12055
12056  
12057 /**
12058  * @class Roo.UpdateManager
12059  * @extends Roo.util.Observable
12060  * Provides AJAX-style update for Element object.<br><br>
12061  * Usage:<br>
12062  * <pre><code>
12063  * // Get it from a Roo.Element object
12064  * var el = Roo.get("foo");
12065  * var mgr = el.getUpdateManager();
12066  * mgr.update("http://myserver.com/index.php", "param1=1&amp;param2=2");
12067  * ...
12068  * mgr.formUpdate("myFormId", "http://myserver.com/index.php");
12069  * <br>
12070  * // or directly (returns the same UpdateManager instance)
12071  * var mgr = new Roo.UpdateManager("myElementId");
12072  * mgr.startAutoRefresh(60, "http://myserver.com/index.php");
12073  * mgr.on("update", myFcnNeedsToKnow);
12074  * <br>
12075    // short handed call directly from the element object
12076    Roo.get("foo").load({
12077         url: "bar.php",
12078         scripts:true,
12079         params: "for=bar",
12080         text: "Loading Foo..."
12081    });
12082  * </code></pre>
12083  * @constructor
12084  * Create new UpdateManager directly.
12085  * @param {String/HTMLElement/Roo.Element} el The element to update
12086  * @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).
12087  */
12088 Roo.UpdateManager = function(el, forceNew){
12089     el = Roo.get(el);
12090     if(!forceNew && el.updateManager){
12091         return el.updateManager;
12092     }
12093     /**
12094      * The Element object
12095      * @type Roo.Element
12096      */
12097     this.el = el;
12098     /**
12099      * Cached url to use for refreshes. Overwritten every time update() is called unless "discardUrl" param is set to true.
12100      * @type String
12101      */
12102     this.defaultUrl = null;
12103
12104     this.addEvents({
12105         /**
12106          * @event beforeupdate
12107          * Fired before an update is made, return false from your handler and the update is cancelled.
12108          * @param {Roo.Element} el
12109          * @param {String/Object/Function} url
12110          * @param {String/Object} params
12111          */
12112         "beforeupdate": true,
12113         /**
12114          * @event update
12115          * Fired after successful update is made.
12116          * @param {Roo.Element} el
12117          * @param {Object} oResponseObject The response Object
12118          */
12119         "update": true,
12120         /**
12121          * @event failure
12122          * Fired on update failure.
12123          * @param {Roo.Element} el
12124          * @param {Object} oResponseObject The response Object
12125          */
12126         "failure": true
12127     });
12128     var d = Roo.UpdateManager.defaults;
12129     /**
12130      * Blank page URL to use with SSL file uploads (Defaults to Roo.UpdateManager.defaults.sslBlankUrl or "about:blank").
12131      * @type String
12132      */
12133     this.sslBlankUrl = d.sslBlankUrl;
12134     /**
12135      * Whether to append unique parameter on get request to disable caching (Defaults to Roo.UpdateManager.defaults.disableCaching or false).
12136      * @type Boolean
12137      */
12138     this.disableCaching = d.disableCaching;
12139     /**
12140      * Text for loading indicator (Defaults to Roo.UpdateManager.defaults.indicatorText or '&lt;div class="loading-indicator"&gt;Loading...&lt;/div&gt;').
12141      * @type String
12142      */
12143     this.indicatorText = d.indicatorText;
12144     /**
12145      * Whether to show indicatorText when loading (Defaults to Roo.UpdateManager.defaults.showLoadIndicator or true).
12146      * @type String
12147      */
12148     this.showLoadIndicator = d.showLoadIndicator;
12149     /**
12150      * Timeout for requests or form posts in seconds (Defaults to Roo.UpdateManager.defaults.timeout or 30 seconds).
12151      * @type Number
12152      */
12153     this.timeout = d.timeout;
12154
12155     /**
12156      * True to process scripts in the output (Defaults to Roo.UpdateManager.defaults.loadScripts (false)).
12157      * @type Boolean
12158      */
12159     this.loadScripts = d.loadScripts;
12160
12161     /**
12162      * Transaction object of current executing transaction
12163      */
12164     this.transaction = null;
12165
12166     /**
12167      * @private
12168      */
12169     this.autoRefreshProcId = null;
12170     /**
12171      * Delegate for refresh() prebound to "this", use myUpdater.refreshDelegate.createCallback(arg1, arg2) to bind arguments
12172      * @type Function
12173      */
12174     this.refreshDelegate = this.refresh.createDelegate(this);
12175     /**
12176      * Delegate for update() prebound to "this", use myUpdater.updateDelegate.createCallback(arg1, arg2) to bind arguments
12177      * @type Function
12178      */
12179     this.updateDelegate = this.update.createDelegate(this);
12180     /**
12181      * Delegate for formUpdate() prebound to "this", use myUpdater.formUpdateDelegate.createCallback(arg1, arg2) to bind arguments
12182      * @type Function
12183      */
12184     this.formUpdateDelegate = this.formUpdate.createDelegate(this);
12185     /**
12186      * @private
12187      */
12188     this.successDelegate = this.processSuccess.createDelegate(this);
12189     /**
12190      * @private
12191      */
12192     this.failureDelegate = this.processFailure.createDelegate(this);
12193
12194     if(!this.renderer){
12195      /**
12196       * The renderer for this UpdateManager. Defaults to {@link Roo.UpdateManager.BasicRenderer}.
12197       */
12198     this.renderer = new Roo.UpdateManager.BasicRenderer();
12199     }
12200     
12201     Roo.UpdateManager.superclass.constructor.call(this);
12202 };
12203
12204 Roo.extend(Roo.UpdateManager, Roo.util.Observable, {
12205     /**
12206      * Get the Element this UpdateManager is bound to
12207      * @return {Roo.Element} The element
12208      */
12209     getEl : function(){
12210         return this.el;
12211     },
12212     /**
12213      * Performs an async request, updating this element with the response. If params are specified it uses POST, otherwise it uses GET.
12214      * @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:
12215 <pre><code>
12216 um.update({<br/>
12217     url: "your-url.php",<br/>
12218     params: {param1: "foo", param2: "bar"}, // or a URL encoded string<br/>
12219     callback: yourFunction,<br/>
12220     scope: yourObject, //(optional scope)  <br/>
12221     discardUrl: false, <br/>
12222     nocache: false,<br/>
12223     text: "Loading...",<br/>
12224     timeout: 30,<br/>
12225     scripts: false<br/>
12226 });
12227 </code></pre>
12228      * The only required property is url. The optional properties nocache, text and scripts
12229      * are shorthand for disableCaching, indicatorText and loadScripts and are used to set their associated property on this UpdateManager instance.
12230      * @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}
12231      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess, oResponse)
12232      * @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.
12233      */
12234     update : function(url, params, callback, discardUrl){
12235         if(this.fireEvent("beforeupdate", this.el, url, params) !== false){
12236             var method = this.method,
12237                 cfg;
12238             if(typeof url == "object"){ // must be config object
12239                 cfg = url;
12240                 url = cfg.url;
12241                 params = params || cfg.params;
12242                 callback = callback || cfg.callback;
12243                 discardUrl = discardUrl || cfg.discardUrl;
12244                 if(callback && cfg.scope){
12245                     callback = callback.createDelegate(cfg.scope);
12246                 }
12247                 if(typeof cfg.method != "undefined"){method = cfg.method;};
12248                 if(typeof cfg.nocache != "undefined"){this.disableCaching = cfg.nocache;};
12249                 if(typeof cfg.text != "undefined"){this.indicatorText = '<div class="loading-indicator">'+cfg.text+"</div>";};
12250                 if(typeof cfg.scripts != "undefined"){this.loadScripts = cfg.scripts;};
12251                 if(typeof cfg.timeout != "undefined"){this.timeout = cfg.timeout;};
12252             }
12253             this.showLoading();
12254             if(!discardUrl){
12255                 this.defaultUrl = url;
12256             }
12257             if(typeof url == "function"){
12258                 url = url.call(this);
12259             }
12260
12261             method = method || (params ? "POST" : "GET");
12262             if(method == "GET"){
12263                 url = this.prepareUrl(url);
12264             }
12265
12266             var o = Roo.apply(cfg ||{}, {
12267                 url : url,
12268                 params: params,
12269                 success: this.successDelegate,
12270                 failure: this.failureDelegate,
12271                 callback: undefined,
12272                 timeout: (this.timeout*1000),
12273                 argument: {"url": url, "form": null, "callback": callback, "params": params}
12274             });
12275             Roo.log("updated manager called with timeout of " + o.timeout);
12276             this.transaction = Roo.Ajax.request(o);
12277         }
12278     },
12279
12280     /**
12281      * 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.
12282      * Uses this.sslBlankUrl for SSL file uploads to prevent IE security warning.
12283      * @param {String/HTMLElement} form The form Id or form element
12284      * @param {String} url (optional) The url to pass the form to. If omitted the action attribute on the form will be used.
12285      * @param {Boolean} reset (optional) Whether to try to reset the form after the update
12286      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess, oResponse)
12287      */
12288     formUpdate : function(form, url, reset, callback){
12289         if(this.fireEvent("beforeupdate", this.el, form, url) !== false){
12290             if(typeof url == "function"){
12291                 url = url.call(this);
12292             }
12293             form = Roo.getDom(form);
12294             this.transaction = Roo.Ajax.request({
12295                 form: form,
12296                 url:url,
12297                 success: this.successDelegate,
12298                 failure: this.failureDelegate,
12299                 timeout: (this.timeout*1000),
12300                 argument: {"url": url, "form": form, "callback": callback, "reset": reset}
12301             });
12302             this.showLoading.defer(1, this);
12303         }
12304     },
12305
12306     /**
12307      * Refresh the element with the last used url or defaultUrl. If there is no url, it returns immediately
12308      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess)
12309      */
12310     refresh : function(callback){
12311         if(this.defaultUrl == null){
12312             return;
12313         }
12314         this.update(this.defaultUrl, null, callback, true);
12315     },
12316
12317     /**
12318      * Set this element to auto refresh.
12319      * @param {Number} interval How often to update (in seconds).
12320      * @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)
12321      * @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}
12322      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess)
12323      * @param {Boolean} refreshNow (optional) Whether to execute the refresh now, or wait the interval
12324      */
12325     startAutoRefresh : function(interval, url, params, callback, refreshNow){
12326         if(refreshNow){
12327             this.update(url || this.defaultUrl, params, callback, true);
12328         }
12329         if(this.autoRefreshProcId){
12330             clearInterval(this.autoRefreshProcId);
12331         }
12332         this.autoRefreshProcId = setInterval(this.update.createDelegate(this, [url || this.defaultUrl, params, callback, true]), interval*1000);
12333     },
12334
12335     /**
12336      * Stop auto refresh on this element.
12337      */
12338      stopAutoRefresh : function(){
12339         if(this.autoRefreshProcId){
12340             clearInterval(this.autoRefreshProcId);
12341             delete this.autoRefreshProcId;
12342         }
12343     },
12344
12345     isAutoRefreshing : function(){
12346        return this.autoRefreshProcId ? true : false;
12347     },
12348     /**
12349      * Called to update the element to "Loading" state. Override to perform custom action.
12350      */
12351     showLoading : function(){
12352         if(this.showLoadIndicator){
12353             this.el.update(this.indicatorText);
12354         }
12355     },
12356
12357     /**
12358      * Adds unique parameter to query string if disableCaching = true
12359      * @private
12360      */
12361     prepareUrl : function(url){
12362         if(this.disableCaching){
12363             var append = "_dc=" + (new Date().getTime());
12364             if(url.indexOf("?") !== -1){
12365                 url += "&" + append;
12366             }else{
12367                 url += "?" + append;
12368             }
12369         }
12370         return url;
12371     },
12372
12373     /**
12374      * @private
12375      */
12376     processSuccess : function(response){
12377         this.transaction = null;
12378         if(response.argument.form && response.argument.reset){
12379             try{ // put in try/catch since some older FF releases had problems with this
12380                 response.argument.form.reset();
12381             }catch(e){}
12382         }
12383         if(this.loadScripts){
12384             this.renderer.render(this.el, response, this,
12385                 this.updateComplete.createDelegate(this, [response]));
12386         }else{
12387             this.renderer.render(this.el, response, this);
12388             this.updateComplete(response);
12389         }
12390     },
12391
12392     updateComplete : function(response){
12393         this.fireEvent("update", this.el, response);
12394         if(typeof response.argument.callback == "function"){
12395             response.argument.callback(this.el, true, response);
12396         }
12397     },
12398
12399     /**
12400      * @private
12401      */
12402     processFailure : function(response){
12403         this.transaction = null;
12404         this.fireEvent("failure", this.el, response);
12405         if(typeof response.argument.callback == "function"){
12406             response.argument.callback(this.el, false, response);
12407         }
12408     },
12409
12410     /**
12411      * Set the content renderer for this UpdateManager. See {@link Roo.UpdateManager.BasicRenderer#render} for more details.
12412      * @param {Object} renderer The object implementing the render() method
12413      */
12414     setRenderer : function(renderer){
12415         this.renderer = renderer;
12416     },
12417
12418     getRenderer : function(){
12419        return this.renderer;
12420     },
12421
12422     /**
12423      * Set the defaultUrl used for updates
12424      * @param {String/Function} defaultUrl The url or a function to call to get the url
12425      */
12426     setDefaultUrl : function(defaultUrl){
12427         this.defaultUrl = defaultUrl;
12428     },
12429
12430     /**
12431      * Aborts the executing transaction
12432      */
12433     abort : function(){
12434         if(this.transaction){
12435             Roo.Ajax.abort(this.transaction);
12436         }
12437     },
12438
12439     /**
12440      * Returns true if an update is in progress
12441      * @return {Boolean}
12442      */
12443     isUpdating : function(){
12444         if(this.transaction){
12445             return Roo.Ajax.isLoading(this.transaction);
12446         }
12447         return false;
12448     }
12449 });
12450
12451 /**
12452  * @class Roo.UpdateManager.defaults
12453  * @static (not really - but it helps the doc tool)
12454  * The defaults collection enables customizing the default properties of UpdateManager
12455  */
12456    Roo.UpdateManager.defaults = {
12457        /**
12458          * Timeout for requests or form posts in seconds (Defaults 30 seconds).
12459          * @type Number
12460          */
12461          timeout : 30,
12462
12463          /**
12464          * True to process scripts by default (Defaults to false).
12465          * @type Boolean
12466          */
12467         loadScripts : false,
12468
12469         /**
12470         * Blank page URL to use with SSL file uploads (Defaults to "javascript:false").
12471         * @type String
12472         */
12473         sslBlankUrl : (Roo.SSL_SECURE_URL || "javascript:false"),
12474         /**
12475          * Whether to append unique parameter on get request to disable caching (Defaults to false).
12476          * @type Boolean
12477          */
12478         disableCaching : false,
12479         /**
12480          * Whether to show indicatorText when loading (Defaults to true).
12481          * @type Boolean
12482          */
12483         showLoadIndicator : true,
12484         /**
12485          * Text for loading indicator (Defaults to '&lt;div class="loading-indicator"&gt;Loading...&lt;/div&gt;').
12486          * @type String
12487          */
12488         indicatorText : '<div class="loading-indicator">Loading...</div>'
12489    };
12490
12491 /**
12492  * Static convenience method. This method is deprecated in favor of el.load({url:'foo.php', ...}).
12493  *Usage:
12494  * <pre><code>Roo.UpdateManager.updateElement("my-div", "stuff.php");</code></pre>
12495  * @param {String/HTMLElement/Roo.Element} el The element to update
12496  * @param {String} url The url
12497  * @param {String/Object} params (optional) Url encoded param string or an object of name/value pairs
12498  * @param {Object} options (optional) A config object with any of the UpdateManager properties you want to set - for example: {disableCaching:true, indicatorText: "Loading data..."}
12499  * @static
12500  * @deprecated
12501  * @member Roo.UpdateManager
12502  */
12503 Roo.UpdateManager.updateElement = function(el, url, params, options){
12504     var um = Roo.get(el, true).getUpdateManager();
12505     Roo.apply(um, options);
12506     um.update(url, params, options ? options.callback : null);
12507 };
12508 // alias for backwards compat
12509 Roo.UpdateManager.update = Roo.UpdateManager.updateElement;
12510 /**
12511  * @class Roo.UpdateManager.BasicRenderer
12512  * Default Content renderer. Updates the elements innerHTML with the responseText.
12513  */
12514 Roo.UpdateManager.BasicRenderer = function(){};
12515
12516 Roo.UpdateManager.BasicRenderer.prototype = {
12517     /**
12518      * This is called when the transaction is completed and it's time to update the element - The BasicRenderer
12519      * updates the elements innerHTML with the responseText - To perform a custom render (i.e. XML or JSON processing),
12520      * create an object with a "render(el, response)" method and pass it to setRenderer on the UpdateManager.
12521      * @param {Roo.Element} el The element being rendered
12522      * @param {Object} response The YUI Connect response object
12523      * @param {UpdateManager} updateManager The calling update manager
12524      * @param {Function} callback A callback that will need to be called if loadScripts is true on the UpdateManager
12525      */
12526      render : function(el, response, updateManager, callback){
12527         el.update(response.responseText, updateManager.loadScripts, callback);
12528     }
12529 };
12530 /*
12531  * Based on:
12532  * Roo JS
12533  * (c)) Alan Knowles
12534  * Licence : LGPL
12535  */
12536
12537
12538 /**
12539  * @class Roo.DomTemplate
12540  * @extends Roo.Template
12541  * An effort at a dom based template engine..
12542  *
12543  * Similar to XTemplate, except it uses dom parsing to create the template..
12544  *
12545  * Supported features:
12546  *
12547  *  Tags:
12548
12549 <pre><code>
12550       {a_variable} - output encoded.
12551       {a_variable.format:("Y-m-d")} - call a method on the variable
12552       {a_variable:raw} - unencoded output
12553       {a_variable:toFixed(1,2)} - Roo.util.Format."toFixed"
12554       {a_variable:this.method_on_template(...)} - call a method on the template object.
12555  
12556 </code></pre>
12557  *  The tpl tag:
12558 <pre><code>
12559         &lt;div roo-for="a_variable or condition.."&gt;&lt;/div&gt;
12560         &lt;div roo-if="a_variable or condition"&gt;&lt;/div&gt;
12561         &lt;div roo-exec="some javascript"&gt;&lt;/div&gt;
12562         &lt;div roo-name="named_template"&gt;&lt;/div&gt; 
12563   
12564 </code></pre>
12565  *      
12566  */
12567 Roo.DomTemplate = function()
12568 {
12569      Roo.DomTemplate.superclass.constructor.apply(this, arguments);
12570      if (this.html) {
12571         this.compile();
12572      }
12573 };
12574
12575
12576 Roo.extend(Roo.DomTemplate, Roo.Template, {
12577     /**
12578      * id counter for sub templates.
12579      */
12580     id : 0,
12581     /**
12582      * flag to indicate if dom parser is inside a pre,
12583      * it will strip whitespace if not.
12584      */
12585     inPre : false,
12586     
12587     /**
12588      * The various sub templates
12589      */
12590     tpls : false,
12591     
12592     
12593     
12594     /**
12595      *
12596      * basic tag replacing syntax
12597      * WORD:WORD()
12598      *
12599      * // you can fake an object call by doing this
12600      *  x.t:(test,tesT) 
12601      * 
12602      */
12603     re : /(\{|\%7B)([\w-\.]+)(?:\:([\w\.]*)(?:\(([^)]*?)?\))?)?(\}|\%7D)/g,
12604     //re : /\{([\w-\.]+)(?:\:([\w\.]*)(?:\((.*?)?\))?)?\}/g,
12605     
12606     iterChild : function (node, method) {
12607         
12608         var oldPre = this.inPre;
12609         if (node.tagName == 'PRE') {
12610             this.inPre = true;
12611         }
12612         for( var i = 0; i < node.childNodes.length; i++) {
12613             method.call(this, node.childNodes[i]);
12614         }
12615         this.inPre = oldPre;
12616     },
12617     
12618     
12619     
12620     /**
12621      * compile the template
12622      *
12623      * This is not recursive, so I'm not sure how nested templates are really going to be handled..
12624      *
12625      */
12626     compile: function()
12627     {
12628         var s = this.html;
12629         
12630         // covert the html into DOM...
12631         var doc = false;
12632         var div =false;
12633         try {
12634             doc = document.implementation.createHTMLDocument("");
12635             doc.documentElement.innerHTML =   this.html  ;
12636             div = doc.documentElement;
12637         } catch (e) {
12638             // old IE... - nasty -- it causes all sorts of issues.. with
12639             // images getting pulled from server..
12640             div = document.createElement('div');
12641             div.innerHTML = this.html;
12642         }
12643         //doc.documentElement.innerHTML = htmlBody
12644          
12645         
12646         
12647         this.tpls = [];
12648         var _t = this;
12649         this.iterChild(div, function(n) {_t.compileNode(n, true); });
12650         
12651         var tpls = this.tpls;
12652         
12653         // create a top level template from the snippet..
12654         
12655         //Roo.log(div.innerHTML);
12656         
12657         var tpl = {
12658             uid : 'master',
12659             id : this.id++,
12660             attr : false,
12661             value : false,
12662             body : div.innerHTML,
12663             
12664             forCall : false,
12665             execCall : false,
12666             dom : div,
12667             isTop : true
12668             
12669         };
12670         tpls.unshift(tpl);
12671         
12672         
12673         // compile them...
12674         this.tpls = [];
12675         Roo.each(tpls, function(tp){
12676             this.compileTpl(tp);
12677             this.tpls[tp.id] = tp;
12678         }, this);
12679         
12680         this.master = tpls[0];
12681         return this;
12682         
12683         
12684     },
12685     
12686     compileNode : function(node, istop) {
12687         // test for
12688         //Roo.log(node);
12689         
12690         
12691         // skip anything not a tag..
12692         if (node.nodeType != 1) {
12693             if (node.nodeType == 3 && !this.inPre) {
12694                 // reduce white space..
12695                 node.nodeValue = node.nodeValue.replace(/\s+/g, ' '); 
12696                 
12697             }
12698             return;
12699         }
12700         
12701         var tpl = {
12702             uid : false,
12703             id : false,
12704             attr : false,
12705             value : false,
12706             body : '',
12707             
12708             forCall : false,
12709             execCall : false,
12710             dom : false,
12711             isTop : istop
12712             
12713             
12714         };
12715         
12716         
12717         switch(true) {
12718             case (node.hasAttribute('roo-for')): tpl.attr = 'for'; break;
12719             case (node.hasAttribute('roo-if')): tpl.attr = 'if'; break;
12720             case (node.hasAttribute('roo-name')): tpl.attr = 'name'; break;
12721             case (node.hasAttribute('roo-exec')): tpl.attr = 'exec'; break;
12722             // no default..
12723         }
12724         
12725         
12726         if (!tpl.attr) {
12727             // just itterate children..
12728             this.iterChild(node,this.compileNode);
12729             return;
12730         }
12731         tpl.uid = this.id++;
12732         tpl.value = node.getAttribute('roo-' +  tpl.attr);
12733         node.removeAttribute('roo-'+ tpl.attr);
12734         if (tpl.attr != 'name') {
12735             var placeholder = document.createTextNode('{domtpl' + tpl.uid + '}');
12736             node.parentNode.replaceChild(placeholder,  node);
12737         } else {
12738             
12739             var placeholder =  document.createElement('span');
12740             placeholder.className = 'roo-tpl-' + tpl.value;
12741             node.parentNode.replaceChild(placeholder,  node);
12742         }
12743         
12744         // parent now sees '{domtplXXXX}
12745         this.iterChild(node,this.compileNode);
12746         
12747         // we should now have node body...
12748         var div = document.createElement('div');
12749         div.appendChild(node);
12750         tpl.dom = node;
12751         // this has the unfortunate side effect of converting tagged attributes
12752         // eg. href="{...}" into %7C...%7D
12753         // this has been fixed by searching for those combo's although it's a bit hacky..
12754         
12755         
12756         tpl.body = div.innerHTML;
12757         
12758         
12759          
12760         tpl.id = tpl.uid;
12761         switch(tpl.attr) {
12762             case 'for' :
12763                 switch (tpl.value) {
12764                     case '.':  tpl.forCall = new Function('values', 'parent', 'with(values){ return values; }'); break;
12765                     case '..': tpl.forCall= new Function('values', 'parent', 'with(values){ return parent; }'); break;
12766                     default:   tpl.forCall= new Function('values', 'parent', 'with(values){ return '+tpl.value+'; }');
12767                 }
12768                 break;
12769             
12770             case 'exec':
12771                 tpl.execCall = new Function('values', 'parent', 'with(values){ '+(Roo.util.Format.htmlDecode(tpl.value))+'; }');
12772                 break;
12773             
12774             case 'if':     
12775                 tpl.ifCall = new Function('values', 'parent', 'with(values){ return '+(Roo.util.Format.htmlDecode(tpl.value))+'; }');
12776                 break;
12777             
12778             case 'name':
12779                 tpl.id  = tpl.value; // replace non characters???
12780                 break;
12781             
12782         }
12783         
12784         
12785         this.tpls.push(tpl);
12786         
12787         
12788         
12789     },
12790     
12791     
12792     
12793     
12794     /**
12795      * Compile a segment of the template into a 'sub-template'
12796      *
12797      * 
12798      * 
12799      *
12800      */
12801     compileTpl : function(tpl)
12802     {
12803         var fm = Roo.util.Format;
12804         var useF = this.disableFormats !== true;
12805         
12806         var sep = Roo.isGecko ? "+\n" : ",\n";
12807         
12808         var undef = function(str) {
12809             Roo.debug && Roo.log("Property not found :"  + str);
12810             return '';
12811         };
12812           
12813         //Roo.log(tpl.body);
12814         
12815         
12816         
12817         var fn = function(m, lbrace, name, format, args)
12818         {
12819             //Roo.log("ARGS");
12820             //Roo.log(arguments);
12821             args = args ? args.replace(/\\'/g,"'") : args;
12822             //["{TEST:(a,b,c)}", "TEST", "", "a,b,c", 0, "{TEST:(a,b,c)}"]
12823             if (typeof(format) == 'undefined') {
12824                 format =  'htmlEncode'; 
12825             }
12826             if (format == 'raw' ) {
12827                 format = false;
12828             }
12829             
12830             if(name.substr(0, 6) == 'domtpl'){
12831                 return "'"+ sep +'this.applySubTemplate('+name.substr(6)+', values, parent)'+sep+"'";
12832             }
12833             
12834             // build an array of options to determine if value is undefined..
12835             
12836             // basically get 'xxxx.yyyy' then do
12837             // (typeof(xxxx) == 'undefined' || typeof(xxx.yyyy) == 'undefined') ?
12838             //    (function () { Roo.log("Property not found"); return ''; })() :
12839             //    ......
12840             
12841             var udef_ar = [];
12842             var lookfor = '';
12843             Roo.each(name.split('.'), function(st) {
12844                 lookfor += (lookfor.length ? '.': '') + st;
12845                 udef_ar.push(  "(typeof(" + lookfor + ") == 'undefined')"  );
12846             });
12847             
12848             var udef_st = '((' + udef_ar.join(" || ") +") ? undef('" + name + "') : "; // .. needs )
12849             
12850             
12851             if(format && useF){
12852                 
12853                 args = args ? ',' + args : "";
12854                  
12855                 if(format.substr(0, 5) != "this."){
12856                     format = "fm." + format + '(';
12857                 }else{
12858                     format = 'this.call("'+ format.substr(5) + '", ';
12859                     args = ", values";
12860                 }
12861                 
12862                 return "'"+ sep +   udef_st   +    format + name + args + "))"+sep+"'";
12863             }
12864              
12865             if (args && args.length) {
12866                 // called with xxyx.yuu:(test,test)
12867                 // change to ()
12868                 return "'"+ sep + udef_st  + name + '(' +  args + "))"+sep+"'";
12869             }
12870             // raw.. - :raw modifier..
12871             return "'"+ sep + udef_st  + name + ")"+sep+"'";
12872             
12873         };
12874         var body;
12875         // branched to use + in gecko and [].join() in others
12876         if(Roo.isGecko){
12877             body = "tpl.compiled = function(values, parent){  with(values) { return '" +
12878                    tpl.body.replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn) +
12879                     "';};};";
12880         }else{
12881             body = ["tpl.compiled = function(values, parent){  with (values) { return ['"];
12882             body.push(tpl.body.replace(/(\r\n|\n)/g,
12883                             '\\n').replace(/'/g, "\\'").replace(this.re, fn));
12884             body.push("'].join('');};};");
12885             body = body.join('');
12886         }
12887         
12888         Roo.debug && Roo.log(body.replace(/\\n/,'\n'));
12889        
12890         /** eval:var:tpl eval:var:fm eval:var:useF eval:var:undef  */
12891         eval(body);
12892         
12893         return this;
12894     },
12895      
12896     /**
12897      * same as applyTemplate, except it's done to one of the subTemplates
12898      * when using named templates, you can do:
12899      *
12900      * var str = pl.applySubTemplate('your-name', values);
12901      *
12902      * 
12903      * @param {Number} id of the template
12904      * @param {Object} values to apply to template
12905      * @param {Object} parent (normaly the instance of this object)
12906      */
12907     applySubTemplate : function(id, values, parent)
12908     {
12909         
12910         
12911         var t = this.tpls[id];
12912         
12913         
12914         try { 
12915             if(t.ifCall && !t.ifCall.call(this, values, parent)){
12916                 Roo.debug && Roo.log('if call on ' + t.value + ' return false');
12917                 return '';
12918             }
12919         } catch(e) {
12920             Roo.log('Xtemplate.applySubTemplate('+ id+ '): Exception thrown on roo-if="' + t.value + '" - ' + e.toString());
12921             Roo.log(values);
12922           
12923             return '';
12924         }
12925         try { 
12926             
12927             if(t.execCall && t.execCall.call(this, values, parent)){
12928                 return '';
12929             }
12930         } catch(e) {
12931             Roo.log('Xtemplate.applySubTemplate('+ id+ '): Exception thrown on roo-for="' + t.value + '" - ' + e.toString());
12932             Roo.log(values);
12933             return '';
12934         }
12935         
12936         try {
12937             var vs = t.forCall ? t.forCall.call(this, values, parent) : values;
12938             parent = t.target ? values : parent;
12939             if(t.forCall && vs instanceof Array){
12940                 var buf = [];
12941                 for(var i = 0, len = vs.length; i < len; i++){
12942                     try {
12943                         buf[buf.length] = t.compiled.call(this, vs[i], parent);
12944                     } catch (e) {
12945                         Roo.log('Xtemplate.applySubTemplate('+ id+ '): Exception thrown on body="' + t.value + '" - ' + e.toString());
12946                         Roo.log(e.body);
12947                         //Roo.log(t.compiled);
12948                         Roo.log(vs[i]);
12949                     }   
12950                 }
12951                 return buf.join('');
12952             }
12953         } catch (e) {
12954             Roo.log('Xtemplate.applySubTemplate('+ id+ '): Exception thrown on roo-for="' + t.value + '" - ' + e.toString());
12955             Roo.log(values);
12956             return '';
12957         }
12958         try {
12959             return t.compiled.call(this, vs, parent);
12960         } catch (e) {
12961             Roo.log('Xtemplate.applySubTemplate('+ id+ '): Exception thrown on body="' + t.value + '" - ' + e.toString());
12962             Roo.log(e.body);
12963             //Roo.log(t.compiled);
12964             Roo.log(values);
12965             return '';
12966         }
12967     },
12968
12969    
12970
12971     applyTemplate : function(values){
12972         return this.master.compiled.call(this, values, {});
12973         //var s = this.subs;
12974     },
12975
12976     apply : function(){
12977         return this.applyTemplate.apply(this, arguments);
12978     }
12979
12980  });
12981
12982 Roo.DomTemplate.from = function(el){
12983     el = Roo.getDom(el);
12984     return new Roo.Domtemplate(el.value || el.innerHTML);
12985 };/*
12986  * Based on:
12987  * Ext JS Library 1.1.1
12988  * Copyright(c) 2006-2007, Ext JS, LLC.
12989  *
12990  * Originally Released Under LGPL - original licence link has changed is not relivant.
12991  *
12992  * Fork - LGPL
12993  * <script type="text/javascript">
12994  */
12995
12996 /**
12997  * @class Roo.util.DelayedTask
12998  * Provides a convenient method of performing setTimeout where a new
12999  * timeout cancels the old timeout. An example would be performing validation on a keypress.
13000  * You can use this class to buffer
13001  * the keypress events for a certain number of milliseconds, and perform only if they stop
13002  * for that amount of time.
13003  * @constructor The parameters to this constructor serve as defaults and are not required.
13004  * @param {Function} fn (optional) The default function to timeout
13005  * @param {Object} scope (optional) The default scope of that timeout
13006  * @param {Array} args (optional) The default Array of arguments
13007  */
13008 Roo.util.DelayedTask = function(fn, scope, args){
13009     var id = null, d, t;
13010
13011     var call = function(){
13012         var now = new Date().getTime();
13013         if(now - t >= d){
13014             clearInterval(id);
13015             id = null;
13016             fn.apply(scope, args || []);
13017         }
13018     };
13019     /**
13020      * Cancels any pending timeout and queues a new one
13021      * @param {Number} delay The milliseconds to delay
13022      * @param {Function} newFn (optional) Overrides function passed to constructor
13023      * @param {Object} newScope (optional) Overrides scope passed to constructor
13024      * @param {Array} newArgs (optional) Overrides args passed to constructor
13025      */
13026     this.delay = function(delay, newFn, newScope, newArgs){
13027         if(id && delay != d){
13028             this.cancel();
13029         }
13030         d = delay;
13031         t = new Date().getTime();
13032         fn = newFn || fn;
13033         scope = newScope || scope;
13034         args = newArgs || args;
13035         if(!id){
13036             id = setInterval(call, d);
13037         }
13038     };
13039
13040     /**
13041      * Cancel the last queued timeout
13042      */
13043     this.cancel = function(){
13044         if(id){
13045             clearInterval(id);
13046             id = null;
13047         }
13048     };
13049 };/*
13050  * Based on:
13051  * Ext JS Library 1.1.1
13052  * Copyright(c) 2006-2007, Ext JS, LLC.
13053  *
13054  * Originally Released Under LGPL - original licence link has changed is not relivant.
13055  *
13056  * Fork - LGPL
13057  * <script type="text/javascript">
13058  */
13059  
13060  
13061 Roo.util.TaskRunner = function(interval){
13062     interval = interval || 10;
13063     var tasks = [], removeQueue = [];
13064     var id = 0;
13065     var running = false;
13066
13067     var stopThread = function(){
13068         running = false;
13069         clearInterval(id);
13070         id = 0;
13071     };
13072
13073     var startThread = function(){
13074         if(!running){
13075             running = true;
13076             id = setInterval(runTasks, interval);
13077         }
13078     };
13079
13080     var removeTask = function(task){
13081         removeQueue.push(task);
13082         if(task.onStop){
13083             task.onStop();
13084         }
13085     };
13086
13087     var runTasks = function(){
13088         if(removeQueue.length > 0){
13089             for(var i = 0, len = removeQueue.length; i < len; i++){
13090                 tasks.remove(removeQueue[i]);
13091             }
13092             removeQueue = [];
13093             if(tasks.length < 1){
13094                 stopThread();
13095                 return;
13096             }
13097         }
13098         var now = new Date().getTime();
13099         for(var i = 0, len = tasks.length; i < len; ++i){
13100             var t = tasks[i];
13101             var itime = now - t.taskRunTime;
13102             if(t.interval <= itime){
13103                 var rt = t.run.apply(t.scope || t, t.args || [++t.taskRunCount]);
13104                 t.taskRunTime = now;
13105                 if(rt === false || t.taskRunCount === t.repeat){
13106                     removeTask(t);
13107                     return;
13108                 }
13109             }
13110             if(t.duration && t.duration <= (now - t.taskStartTime)){
13111                 removeTask(t);
13112             }
13113         }
13114     };
13115
13116     /**
13117      * Queues a new task.
13118      * @param {Object} task
13119      */
13120     this.start = function(task){
13121         tasks.push(task);
13122         task.taskStartTime = new Date().getTime();
13123         task.taskRunTime = 0;
13124         task.taskRunCount = 0;
13125         startThread();
13126         return task;
13127     };
13128
13129     this.stop = function(task){
13130         removeTask(task);
13131         return task;
13132     };
13133
13134     this.stopAll = function(){
13135         stopThread();
13136         for(var i = 0, len = tasks.length; i < len; i++){
13137             if(tasks[i].onStop){
13138                 tasks[i].onStop();
13139             }
13140         }
13141         tasks = [];
13142         removeQueue = [];
13143     };
13144 };
13145
13146 Roo.TaskMgr = new Roo.util.TaskRunner();/*
13147  * Based on:
13148  * Ext JS Library 1.1.1
13149  * Copyright(c) 2006-2007, Ext JS, LLC.
13150  *
13151  * Originally Released Under LGPL - original licence link has changed is not relivant.
13152  *
13153  * Fork - LGPL
13154  * <script type="text/javascript">
13155  */
13156
13157  
13158 /**
13159  * @class Roo.util.MixedCollection
13160  * @extends Roo.util.Observable
13161  * A Collection class that maintains both numeric indexes and keys and exposes events.
13162  * @constructor
13163  * @param {Boolean} allowFunctions True if the addAll function should add function references to the
13164  * collection (defaults to false)
13165  * @param {Function} keyFn A function that can accept an item of the type(s) stored in this MixedCollection
13166  * and return the key value for that item.  This is used when available to look up the key on items that
13167  * were passed without an explicit key parameter to a MixedCollection method.  Passing this parameter is
13168  * equivalent to providing an implementation for the {@link #getKey} method.
13169  */
13170 Roo.util.MixedCollection = function(allowFunctions, keyFn){
13171     this.items = [];
13172     this.map = {};
13173     this.keys = [];
13174     this.length = 0;
13175     this.addEvents({
13176         /**
13177          * @event clear
13178          * Fires when the collection is cleared.
13179          */
13180         "clear" : true,
13181         /**
13182          * @event add
13183          * Fires when an item is added to the collection.
13184          * @param {Number} index The index at which the item was added.
13185          * @param {Object} o The item added.
13186          * @param {String} key The key associated with the added item.
13187          */
13188         "add" : true,
13189         /**
13190          * @event replace
13191          * Fires when an item is replaced in the collection.
13192          * @param {String} key he key associated with the new added.
13193          * @param {Object} old The item being replaced.
13194          * @param {Object} new The new item.
13195          */
13196         "replace" : true,
13197         /**
13198          * @event remove
13199          * Fires when an item is removed from the collection.
13200          * @param {Object} o The item being removed.
13201          * @param {String} key (optional) The key associated with the removed item.
13202          */
13203         "remove" : true,
13204         "sort" : true
13205     });
13206     this.allowFunctions = allowFunctions === true;
13207     if(keyFn){
13208         this.getKey = keyFn;
13209     }
13210     Roo.util.MixedCollection.superclass.constructor.call(this);
13211 };
13212
13213 Roo.extend(Roo.util.MixedCollection, Roo.util.Observable, {
13214     allowFunctions : false,
13215     
13216 /**
13217  * Adds an item to the collection.
13218  * @param {String} key The key to associate with the item
13219  * @param {Object} o The item to add.
13220  * @return {Object} The item added.
13221  */
13222     add : function(key, o){
13223         if(arguments.length == 1){
13224             o = arguments[0];
13225             key = this.getKey(o);
13226         }
13227         if(typeof key == "undefined" || key === null){
13228             this.length++;
13229             this.items.push(o);
13230             this.keys.push(null);
13231         }else{
13232             var old = this.map[key];
13233             if(old){
13234                 return this.replace(key, o);
13235             }
13236             this.length++;
13237             this.items.push(o);
13238             this.map[key] = o;
13239             this.keys.push(key);
13240         }
13241         this.fireEvent("add", this.length-1, o, key);
13242         return o;
13243     },
13244        
13245 /**
13246   * MixedCollection has a generic way to fetch keys if you implement getKey.
13247 <pre><code>
13248 // normal way
13249 var mc = new Roo.util.MixedCollection();
13250 mc.add(someEl.dom.id, someEl);
13251 mc.add(otherEl.dom.id, otherEl);
13252 //and so on
13253
13254 // using getKey
13255 var mc = new Roo.util.MixedCollection();
13256 mc.getKey = function(el){
13257    return el.dom.id;
13258 };
13259 mc.add(someEl);
13260 mc.add(otherEl);
13261
13262 // or via the constructor
13263 var mc = new Roo.util.MixedCollection(false, function(el){
13264    return el.dom.id;
13265 });
13266 mc.add(someEl);
13267 mc.add(otherEl);
13268 </code></pre>
13269  * @param o {Object} The item for which to find the key.
13270  * @return {Object} The key for the passed item.
13271  */
13272     getKey : function(o){
13273          return o.id; 
13274     },
13275    
13276 /**
13277  * Replaces an item in the collection.
13278  * @param {String} key The key associated with the item to replace, or the item to replace.
13279  * @param o {Object} o (optional) If the first parameter passed was a key, the item to associate with that key.
13280  * @return {Object}  The new item.
13281  */
13282     replace : function(key, o){
13283         if(arguments.length == 1){
13284             o = arguments[0];
13285             key = this.getKey(o);
13286         }
13287         var old = this.item(key);
13288         if(typeof key == "undefined" || key === null || typeof old == "undefined"){
13289              return this.add(key, o);
13290         }
13291         var index = this.indexOfKey(key);
13292         this.items[index] = o;
13293         this.map[key] = o;
13294         this.fireEvent("replace", key, old, o);
13295         return o;
13296     },
13297    
13298 /**
13299  * Adds all elements of an Array or an Object to the collection.
13300  * @param {Object/Array} objs An Object containing properties which will be added to the collection, or
13301  * an Array of values, each of which are added to the collection.
13302  */
13303     addAll : function(objs){
13304         if(arguments.length > 1 || objs instanceof Array){
13305             var args = arguments.length > 1 ? arguments : objs;
13306             for(var i = 0, len = args.length; i < len; i++){
13307                 this.add(args[i]);
13308             }
13309         }else{
13310             for(var key in objs){
13311                 if(this.allowFunctions || typeof objs[key] != "function"){
13312                     this.add(key, objs[key]);
13313                 }
13314             }
13315         }
13316     },
13317    
13318 /**
13319  * Executes the specified function once for every item in the collection, passing each
13320  * item as the first and only parameter. returning false from the function will stop the iteration.
13321  * @param {Function} fn The function to execute for each item.
13322  * @param {Object} scope (optional) The scope in which to execute the function.
13323  */
13324     each : function(fn, scope){
13325         var items = [].concat(this.items); // each safe for removal
13326         for(var i = 0, len = items.length; i < len; i++){
13327             if(fn.call(scope || items[i], items[i], i, len) === false){
13328                 break;
13329             }
13330         }
13331     },
13332    
13333 /**
13334  * Executes the specified function once for every key in the collection, passing each
13335  * key, and its associated item as the first two parameters.
13336  * @param {Function} fn The function to execute for each item.
13337  * @param {Object} scope (optional) The scope in which to execute the function.
13338  */
13339     eachKey : function(fn, scope){
13340         for(var i = 0, len = this.keys.length; i < len; i++){
13341             fn.call(scope || window, this.keys[i], this.items[i], i, len);
13342         }
13343     },
13344    
13345 /**
13346  * Returns the first item in the collection which elicits a true return value from the
13347  * passed selection function.
13348  * @param {Function} fn The selection function to execute for each item.
13349  * @param {Object} scope (optional) The scope in which to execute the function.
13350  * @return {Object} The first item in the collection which returned true from the selection function.
13351  */
13352     find : function(fn, scope){
13353         for(var i = 0, len = this.items.length; i < len; i++){
13354             if(fn.call(scope || window, this.items[i], this.keys[i])){
13355                 return this.items[i];
13356             }
13357         }
13358         return null;
13359     },
13360    
13361 /**
13362  * Inserts an item at the specified index in the collection.
13363  * @param {Number} index The index to insert the item at.
13364  * @param {String} key The key to associate with the new item, or the item itself.
13365  * @param {Object} o  (optional) If the second parameter was a key, the new item.
13366  * @return {Object} The item inserted.
13367  */
13368     insert : function(index, key, o){
13369         if(arguments.length == 2){
13370             o = arguments[1];
13371             key = this.getKey(o);
13372         }
13373         if(index >= this.length){
13374             return this.add(key, o);
13375         }
13376         this.length++;
13377         this.items.splice(index, 0, o);
13378         if(typeof key != "undefined" && key != null){
13379             this.map[key] = o;
13380         }
13381         this.keys.splice(index, 0, key);
13382         this.fireEvent("add", index, o, key);
13383         return o;
13384     },
13385    
13386 /**
13387  * Removed an item from the collection.
13388  * @param {Object} o The item to remove.
13389  * @return {Object} The item removed.
13390  */
13391     remove : function(o){
13392         return this.removeAt(this.indexOf(o));
13393     },
13394    
13395 /**
13396  * Remove an item from a specified index in the collection.
13397  * @param {Number} index The index within the collection of the item to remove.
13398  */
13399     removeAt : function(index){
13400         if(index < this.length && index >= 0){
13401             this.length--;
13402             var o = this.items[index];
13403             this.items.splice(index, 1);
13404             var key = this.keys[index];
13405             if(typeof key != "undefined"){
13406                 delete this.map[key];
13407             }
13408             this.keys.splice(index, 1);
13409             this.fireEvent("remove", o, key);
13410         }
13411     },
13412    
13413 /**
13414  * Removed an item associated with the passed key fom the collection.
13415  * @param {String} key The key of the item to remove.
13416  */
13417     removeKey : function(key){
13418         return this.removeAt(this.indexOfKey(key));
13419     },
13420    
13421 /**
13422  * Returns the number of items in the collection.
13423  * @return {Number} the number of items in the collection.
13424  */
13425     getCount : function(){
13426         return this.length; 
13427     },
13428    
13429 /**
13430  * Returns index within the collection of the passed Object.
13431  * @param {Object} o The item to find the index of.
13432  * @return {Number} index of the item.
13433  */
13434     indexOf : function(o){
13435         if(!this.items.indexOf){
13436             for(var i = 0, len = this.items.length; i < len; i++){
13437                 if(this.items[i] == o) {
13438                     return i;
13439                 }
13440             }
13441             return -1;
13442         }else{
13443             return this.items.indexOf(o);
13444         }
13445     },
13446    
13447 /**
13448  * Returns index within the collection of the passed key.
13449  * @param {String} key The key to find the index of.
13450  * @return {Number} index of the key.
13451  */
13452     indexOfKey : function(key){
13453         if(!this.keys.indexOf){
13454             for(var i = 0, len = this.keys.length; i < len; i++){
13455                 if(this.keys[i] == key) {
13456                     return i;
13457                 }
13458             }
13459             return -1;
13460         }else{
13461             return this.keys.indexOf(key);
13462         }
13463     },
13464    
13465 /**
13466  * Returns the item associated with the passed key OR index. Key has priority over index.
13467  * @param {String/Number} key The key or index of the item.
13468  * @return {Object} The item associated with the passed key.
13469  */
13470     item : function(key){
13471         if (key === 'length') {
13472             return null;
13473         }
13474         var item = typeof this.map[key] != "undefined" ? this.map[key] : this.items[key];
13475         return typeof item != 'function' || this.allowFunctions ? item : null; // for prototype!
13476     },
13477     
13478 /**
13479  * Returns the item at the specified index.
13480  * @param {Number} index The index of the item.
13481  * @return {Object}
13482  */
13483     itemAt : function(index){
13484         return this.items[index];
13485     },
13486     
13487 /**
13488  * Returns the item associated with the passed key.
13489  * @param {String/Number} key The key of the item.
13490  * @return {Object} The item associated with the passed key.
13491  */
13492     key : function(key){
13493         return this.map[key];
13494     },
13495    
13496 /**
13497  * Returns true if the collection contains the passed Object as an item.
13498  * @param {Object} o  The Object to look for in the collection.
13499  * @return {Boolean} True if the collection contains the Object as an item.
13500  */
13501     contains : function(o){
13502         return this.indexOf(o) != -1;
13503     },
13504    
13505 /**
13506  * Returns true if the collection contains the passed Object as a key.
13507  * @param {String} key The key to look for in the collection.
13508  * @return {Boolean} True if the collection contains the Object as a key.
13509  */
13510     containsKey : function(key){
13511         return typeof this.map[key] != "undefined";
13512     },
13513    
13514 /**
13515  * Removes all items from the collection.
13516  */
13517     clear : function(){
13518         this.length = 0;
13519         this.items = [];
13520         this.keys = [];
13521         this.map = {};
13522         this.fireEvent("clear");
13523     },
13524    
13525 /**
13526  * Returns the first item in the collection.
13527  * @return {Object} the first item in the collection..
13528  */
13529     first : function(){
13530         return this.items[0]; 
13531     },
13532    
13533 /**
13534  * Returns the last item in the collection.
13535  * @return {Object} the last item in the collection..
13536  */
13537     last : function(){
13538         return this.items[this.length-1];   
13539     },
13540     
13541     _sort : function(property, dir, fn){
13542         var dsc = String(dir).toUpperCase() == "DESC" ? -1 : 1;
13543         fn = fn || function(a, b){
13544             return a-b;
13545         };
13546         var c = [], k = this.keys, items = this.items;
13547         for(var i = 0, len = items.length; i < len; i++){
13548             c[c.length] = {key: k[i], value: items[i], index: i};
13549         }
13550         c.sort(function(a, b){
13551             var v = fn(a[property], b[property]) * dsc;
13552             if(v == 0){
13553                 v = (a.index < b.index ? -1 : 1);
13554             }
13555             return v;
13556         });
13557         for(var i = 0, len = c.length; i < len; i++){
13558             items[i] = c[i].value;
13559             k[i] = c[i].key;
13560         }
13561         this.fireEvent("sort", this);
13562     },
13563     
13564     /**
13565      * Sorts this collection with the passed comparison function
13566      * @param {String} direction (optional) "ASC" or "DESC"
13567      * @param {Function} fn (optional) comparison function
13568      */
13569     sort : function(dir, fn){
13570         this._sort("value", dir, fn);
13571     },
13572     
13573     /**
13574      * Sorts this collection by keys
13575      * @param {String} direction (optional) "ASC" or "DESC"
13576      * @param {Function} fn (optional) a comparison function (defaults to case insensitive string)
13577      */
13578     keySort : function(dir, fn){
13579         this._sort("key", dir, fn || function(a, b){
13580             return String(a).toUpperCase()-String(b).toUpperCase();
13581         });
13582     },
13583     
13584     /**
13585      * Returns a range of items in this collection
13586      * @param {Number} startIndex (optional) defaults to 0
13587      * @param {Number} endIndex (optional) default to the last item
13588      * @return {Array} An array of items
13589      */
13590     getRange : function(start, end){
13591         var items = this.items;
13592         if(items.length < 1){
13593             return [];
13594         }
13595         start = start || 0;
13596         end = Math.min(typeof end == "undefined" ? this.length-1 : end, this.length-1);
13597         var r = [];
13598         if(start <= end){
13599             for(var i = start; i <= end; i++) {
13600                     r[r.length] = items[i];
13601             }
13602         }else{
13603             for(var i = start; i >= end; i--) {
13604                     r[r.length] = items[i];
13605             }
13606         }
13607         return r;
13608     },
13609         
13610     /**
13611      * Filter the <i>objects</i> in this collection by a specific property. 
13612      * Returns a new collection that has been filtered.
13613      * @param {String} property A property on your objects
13614      * @param {String/RegExp} value Either string that the property values 
13615      * should start with or a RegExp to test against the property
13616      * @return {MixedCollection} The new filtered collection
13617      */
13618     filter : function(property, value){
13619         if(!value.exec){ // not a regex
13620             value = String(value);
13621             if(value.length == 0){
13622                 return this.clone();
13623             }
13624             value = new RegExp("^" + Roo.escapeRe(value), "i");
13625         }
13626         return this.filterBy(function(o){
13627             return o && value.test(o[property]);
13628         });
13629         },
13630     
13631     /**
13632      * Filter by a function. * Returns a new collection that has been filtered.
13633      * The passed function will be called with each 
13634      * object in the collection. If the function returns true, the value is included 
13635      * otherwise it is filtered.
13636      * @param {Function} fn The function to be called, it will receive the args o (the object), k (the key)
13637      * @param {Object} scope (optional) The scope of the function (defaults to this) 
13638      * @return {MixedCollection} The new filtered collection
13639      */
13640     filterBy : function(fn, scope){
13641         var r = new Roo.util.MixedCollection();
13642         r.getKey = this.getKey;
13643         var k = this.keys, it = this.items;
13644         for(var i = 0, len = it.length; i < len; i++){
13645             if(fn.call(scope||this, it[i], k[i])){
13646                                 r.add(k[i], it[i]);
13647                         }
13648         }
13649         return r;
13650     },
13651     
13652     /**
13653      * Creates a duplicate of this collection
13654      * @return {MixedCollection}
13655      */
13656     clone : function(){
13657         var r = new Roo.util.MixedCollection();
13658         var k = this.keys, it = this.items;
13659         for(var i = 0, len = it.length; i < len; i++){
13660             r.add(k[i], it[i]);
13661         }
13662         r.getKey = this.getKey;
13663         return r;
13664     }
13665 });
13666 /**
13667  * Returns the item associated with the passed key or index.
13668  * @method
13669  * @param {String/Number} key The key or index of the item.
13670  * @return {Object} The item associated with the passed key.
13671  */
13672 Roo.util.MixedCollection.prototype.get = Roo.util.MixedCollection.prototype.item;/*
13673  * Based on:
13674  * Ext JS Library 1.1.1
13675  * Copyright(c) 2006-2007, Ext JS, LLC.
13676  *
13677  * Originally Released Under LGPL - original licence link has changed is not relivant.
13678  *
13679  * Fork - LGPL
13680  * <script type="text/javascript">
13681  */
13682 /**
13683  * @class Roo.util.JSON
13684  * Modified version of Douglas Crockford"s json.js that doesn"t
13685  * mess with the Object prototype 
13686  * http://www.json.org/js.html
13687  * @singleton
13688  */
13689 Roo.util.JSON = new (function(){
13690     var useHasOwn = {}.hasOwnProperty ? true : false;
13691     
13692     // crashes Safari in some instances
13693     //var validRE = /^("(\\.|[^"\\\n\r])*?"|[,:{}\[\]0-9.\-+Eaeflnr-u \n\r\t])+?$/;
13694     
13695     var pad = function(n) {
13696         return n < 10 ? "0" + n : n;
13697     };
13698     
13699     var m = {
13700         "\b": '\\b',
13701         "\t": '\\t',
13702         "\n": '\\n',
13703         "\f": '\\f',
13704         "\r": '\\r',
13705         '"' : '\\"',
13706         "\\": '\\\\'
13707     };
13708
13709     var encodeString = function(s){
13710         if (/["\\\x00-\x1f]/.test(s)) {
13711             return '"' + s.replace(/([\x00-\x1f\\"])/g, function(a, b) {
13712                 var c = m[b];
13713                 if(c){
13714                     return c;
13715                 }
13716                 c = b.charCodeAt();
13717                 return "\\u00" +
13718                     Math.floor(c / 16).toString(16) +
13719                     (c % 16).toString(16);
13720             }) + '"';
13721         }
13722         return '"' + s + '"';
13723     };
13724     
13725     var encodeArray = function(o){
13726         var a = ["["], b, i, l = o.length, v;
13727             for (i = 0; i < l; i += 1) {
13728                 v = o[i];
13729                 switch (typeof v) {
13730                     case "undefined":
13731                     case "function":
13732                     case "unknown":
13733                         break;
13734                     default:
13735                         if (b) {
13736                             a.push(',');
13737                         }
13738                         a.push(v === null ? "null" : Roo.util.JSON.encode(v));
13739                         b = true;
13740                 }
13741             }
13742             a.push("]");
13743             return a.join("");
13744     };
13745     
13746     var encodeDate = function(o){
13747         return '"' + o.getFullYear() + "-" +
13748                 pad(o.getMonth() + 1) + "-" +
13749                 pad(o.getDate()) + "T" +
13750                 pad(o.getHours()) + ":" +
13751                 pad(o.getMinutes()) + ":" +
13752                 pad(o.getSeconds()) + '"';
13753     };
13754     
13755     /**
13756      * Encodes an Object, Array or other value
13757      * @param {Mixed} o The variable to encode
13758      * @return {String} The JSON string
13759      */
13760     this.encode = function(o)
13761     {
13762         // should this be extended to fully wrap stringify..
13763         
13764         if(typeof o == "undefined" || o === null){
13765             return "null";
13766         }else if(o instanceof Array){
13767             return encodeArray(o);
13768         }else if(o instanceof Date){
13769             return encodeDate(o);
13770         }else if(typeof o == "string"){
13771             return encodeString(o);
13772         }else if(typeof o == "number"){
13773             return isFinite(o) ? String(o) : "null";
13774         }else if(typeof o == "boolean"){
13775             return String(o);
13776         }else {
13777             var a = ["{"], b, i, v;
13778             for (i in o) {
13779                 if(!useHasOwn || o.hasOwnProperty(i)) {
13780                     v = o[i];
13781                     switch (typeof v) {
13782                     case "undefined":
13783                     case "function":
13784                     case "unknown":
13785                         break;
13786                     default:
13787                         if(b){
13788                             a.push(',');
13789                         }
13790                         a.push(this.encode(i), ":",
13791                                 v === null ? "null" : this.encode(v));
13792                         b = true;
13793                     }
13794                 }
13795             }
13796             a.push("}");
13797             return a.join("");
13798         }
13799     };
13800     
13801     /**
13802      * Decodes (parses) a JSON string to an object. If the JSON is invalid, this function throws a SyntaxError.
13803      * @param {String} json The JSON string
13804      * @return {Object} The resulting object
13805      */
13806     this.decode = function(json){
13807         
13808         return  /** eval:var:json */ eval("(" + json + ')');
13809     };
13810 })();
13811 /** 
13812  * Shorthand for {@link Roo.util.JSON#encode}
13813  * @member Roo encode 
13814  * @method */
13815 Roo.encode = typeof(JSON) != 'undefined' && JSON.stringify ? JSON.stringify : Roo.util.JSON.encode;
13816 /** 
13817  * Shorthand for {@link Roo.util.JSON#decode}
13818  * @member Roo decode 
13819  * @method */
13820 Roo.decode = typeof(JSON) != 'undefined' && JSON.parse ? JSON.parse : Roo.util.JSON.decode;
13821 /*
13822  * Based on:
13823  * Ext JS Library 1.1.1
13824  * Copyright(c) 2006-2007, Ext JS, LLC.
13825  *
13826  * Originally Released Under LGPL - original licence link has changed is not relivant.
13827  *
13828  * Fork - LGPL
13829  * <script type="text/javascript">
13830  */
13831  
13832 /**
13833  * @class Roo.util.Format
13834  * Reusable data formatting functions
13835  * @singleton
13836  */
13837 Roo.util.Format = function(){
13838     var trimRe = /^\s+|\s+$/g;
13839     return {
13840         /**
13841          * Truncate a string and add an ellipsis ('...') to the end if it exceeds the specified length
13842          * @param {String} value The string to truncate
13843          * @param {Number} length The maximum length to allow before truncating
13844          * @return {String} The converted text
13845          */
13846         ellipsis : function(value, len){
13847             if(value && value.length > len){
13848                 return value.substr(0, len-3)+"...";
13849             }
13850             return value;
13851         },
13852
13853         /**
13854          * Checks a reference and converts it to empty string if it is undefined
13855          * @param {Mixed} value Reference to check
13856          * @return {Mixed} Empty string if converted, otherwise the original value
13857          */
13858         undef : function(value){
13859             return typeof value != "undefined" ? value : "";
13860         },
13861
13862         /**
13863          * Convert certain characters (&, <, >, and ') to their HTML character equivalents for literal display in web pages.
13864          * @param {String} value The string to encode
13865          * @return {String} The encoded text
13866          */
13867         htmlEncode : function(value){
13868             return !value ? value : String(value).replace(/&/g, "&amp;").replace(/>/g, "&gt;").replace(/</g, "&lt;").replace(/"/g, "&quot;");
13869         },
13870
13871         /**
13872          * Convert certain characters (&, <, >, and ') from their HTML character equivalents.
13873          * @param {String} value The string to decode
13874          * @return {String} The decoded text
13875          */
13876         htmlDecode : function(value){
13877             return !value ? value : String(value).replace(/&amp;/g, "&").replace(/&gt;/g, ">").replace(/&lt;/g, "<").replace(/&quot;/g, '"');
13878         },
13879
13880         /**
13881          * Trims any whitespace from either side of a string
13882          * @param {String} value The text to trim
13883          * @return {String} The trimmed text
13884          */
13885         trim : function(value){
13886             return String(value).replace(trimRe, "");
13887         },
13888
13889         /**
13890          * Returns a substring from within an original string
13891          * @param {String} value The original text
13892          * @param {Number} start The start index of the substring
13893          * @param {Number} length The length of the substring
13894          * @return {String} The substring
13895          */
13896         substr : function(value, start, length){
13897             return String(value).substr(start, length);
13898         },
13899
13900         /**
13901          * Converts a string to all lower case letters
13902          * @param {String} value The text to convert
13903          * @return {String} The converted text
13904          */
13905         lowercase : function(value){
13906             return String(value).toLowerCase();
13907         },
13908
13909         /**
13910          * Converts a string to all upper case letters
13911          * @param {String} value The text to convert
13912          * @return {String} The converted text
13913          */
13914         uppercase : function(value){
13915             return String(value).toUpperCase();
13916         },
13917
13918         /**
13919          * Converts the first character only of a string to upper case
13920          * @param {String} value The text to convert
13921          * @return {String} The converted text
13922          */
13923         capitalize : function(value){
13924             return !value ? value : value.charAt(0).toUpperCase() + value.substr(1).toLowerCase();
13925         },
13926
13927         // private
13928         call : function(value, fn){
13929             if(arguments.length > 2){
13930                 var args = Array.prototype.slice.call(arguments, 2);
13931                 args.unshift(value);
13932                  
13933                 return /** eval:var:value */  eval(fn).apply(window, args);
13934             }else{
13935                 /** eval:var:value */
13936                 return /** eval:var:value */ eval(fn).call(window, value);
13937             }
13938         },
13939
13940        
13941         /**
13942          * safer version of Math.toFixed..??/
13943          * @param {Number/String} value The numeric value to format
13944          * @param {Number/String} value Decimal places 
13945          * @return {String} The formatted currency string
13946          */
13947         toFixed : function(v, n)
13948         {
13949             // why not use to fixed - precision is buggered???
13950             if (!n) {
13951                 return Math.round(v-0);
13952             }
13953             var fact = Math.pow(10,n+1);
13954             v = (Math.round((v-0)*fact))/fact;
13955             var z = (''+fact).substring(2);
13956             if (v == Math.floor(v)) {
13957                 return Math.floor(v) + '.' + z;
13958             }
13959             
13960             // now just padd decimals..
13961             var ps = String(v).split('.');
13962             var fd = (ps[1] + z);
13963             var r = fd.substring(0,n); 
13964             var rm = fd.substring(n); 
13965             if (rm < 5) {
13966                 return ps[0] + '.' + r;
13967             }
13968             r*=1; // turn it into a number;
13969             r++;
13970             if (String(r).length != n) {
13971                 ps[0]*=1;
13972                 ps[0]++;
13973                 r = String(r).substring(1); // chop the end off.
13974             }
13975             
13976             return ps[0] + '.' + r;
13977              
13978         },
13979         
13980         /**
13981          * Format a number as US currency
13982          * @param {Number/String} value The numeric value to format
13983          * @return {String} The formatted currency string
13984          */
13985         usMoney : function(v){
13986             return '$' + Roo.util.Format.number(v);
13987         },
13988         
13989         /**
13990          * Format a number
13991          * eventually this should probably emulate php's number_format
13992          * @param {Number/String} value The numeric value to format
13993          * @param {Number} decimals number of decimal places
13994          * @param {String} delimiter for thousands (default comma)
13995          * @return {String} The formatted currency string
13996          */
13997         number : function(v, decimals, thousandsDelimiter)
13998         {
13999             // multiply and round.
14000             decimals = typeof(decimals) == 'undefined' ? 2 : decimals;
14001             thousandsDelimiter = typeof(thousandsDelimiter) == 'undefined' ? ',' : thousandsDelimiter;
14002             
14003             var mul = Math.pow(10, decimals);
14004             var zero = String(mul).substring(1);
14005             v = (Math.round((v-0)*mul))/mul;
14006             
14007             // if it's '0' number.. then
14008             
14009             //v = (v == Math.floor(v)) ? v + "." + zero : ((v*10 == Math.floor(v*10)) ? v + "0" : v);
14010             v = String(v);
14011             var ps = v.split('.');
14012             var whole = ps[0];
14013             
14014             var r = /(\d+)(\d{3})/;
14015             // add comma's
14016             
14017             if(thousandsDelimiter.length != 0) {
14018                 whole = whole.replace(/\B(?=(\d{3})+(?!\d))/g, thousandsDelimiter );
14019             } 
14020             
14021             var sub = ps[1] ?
14022                     // has decimals..
14023                     (decimals ?  ('.'+ ps[1] + zero.substring(ps[1].length)) : '') :
14024                     // does not have decimals
14025                     (decimals ? ('.' + zero) : '');
14026             
14027             
14028             return whole + sub ;
14029         },
14030         
14031         /**
14032          * Parse a value into a formatted date using the specified format pattern.
14033          * @param {Mixed} value The value to format
14034          * @param {String} format (optional) Any valid date format string (defaults to 'm/d/Y')
14035          * @return {String} The formatted date string
14036          */
14037         date : function(v, format){
14038             if(!v){
14039                 return "";
14040             }
14041             if(!(v instanceof Date)){
14042                 v = new Date(Date.parse(v));
14043             }
14044             return v.dateFormat(format || Roo.util.Format.defaults.date);
14045         },
14046
14047         /**
14048          * Returns a date rendering function that can be reused to apply a date format multiple times efficiently
14049          * @param {String} format Any valid date format string
14050          * @return {Function} The date formatting function
14051          */
14052         dateRenderer : function(format){
14053             return function(v){
14054                 return Roo.util.Format.date(v, format);  
14055             };
14056         },
14057
14058         // private
14059         stripTagsRE : /<\/?[^>]+>/gi,
14060         
14061         /**
14062          * Strips all HTML tags
14063          * @param {Mixed} value The text from which to strip tags
14064          * @return {String} The stripped text
14065          */
14066         stripTags : function(v){
14067             return !v ? v : String(v).replace(this.stripTagsRE, "");
14068         },
14069         
14070         /**
14071          * Size in Mb,Gb etc.
14072          * @param {Number} value The number to be formated
14073          * @param {number} decimals how many decimal places
14074          * @return {String} the formated string
14075          */
14076         size : function(value, decimals)
14077         {
14078             var sizes = ['b', 'k', 'M', 'G', 'T'];
14079             if (value == 0) {
14080                 return 0;
14081             }
14082             var i = parseInt(Math.floor(Math.log(value) / Math.log(1024)));
14083             return Roo.util.Format.number(value/ Math.pow(1024, i) ,decimals)   + sizes[i];
14084         }
14085         
14086         
14087         
14088     };
14089 }();
14090 Roo.util.Format.defaults = {
14091     date : 'd/M/Y'
14092 };/*
14093  * Based on:
14094  * Ext JS Library 1.1.1
14095  * Copyright(c) 2006-2007, Ext JS, LLC.
14096  *
14097  * Originally Released Under LGPL - original licence link has changed is not relivant.
14098  *
14099  * Fork - LGPL
14100  * <script type="text/javascript">
14101  */
14102
14103
14104  
14105
14106 /**
14107  * @class Roo.MasterTemplate
14108  * @extends Roo.Template
14109  * Provides a template that can have child templates. The syntax is:
14110 <pre><code>
14111 var t = new Roo.MasterTemplate(
14112         '&lt;select name="{name}"&gt;',
14113                 '&lt;tpl name="options"&gt;&lt;option value="{value:trim}"&gt;{text:ellipsis(10)}&lt;/option&gt;&lt;/tpl&gt;',
14114         '&lt;/select&gt;'
14115 );
14116 t.add('options', {value: 'foo', text: 'bar'});
14117 // or you can add multiple child elements in one shot
14118 t.addAll('options', [
14119     {value: 'foo', text: 'bar'},
14120     {value: 'foo2', text: 'bar2'},
14121     {value: 'foo3', text: 'bar3'}
14122 ]);
14123 // then append, applying the master template values
14124 t.append('my-form', {name: 'my-select'});
14125 </code></pre>
14126 * A name attribute for the child template is not required if you have only one child
14127 * template or you want to refer to them by index.
14128  */
14129 Roo.MasterTemplate = function(){
14130     Roo.MasterTemplate.superclass.constructor.apply(this, arguments);
14131     this.originalHtml = this.html;
14132     var st = {};
14133     var m, re = this.subTemplateRe;
14134     re.lastIndex = 0;
14135     var subIndex = 0;
14136     while(m = re.exec(this.html)){
14137         var name = m[1], content = m[2];
14138         st[subIndex] = {
14139             name: name,
14140             index: subIndex,
14141             buffer: [],
14142             tpl : new Roo.Template(content)
14143         };
14144         if(name){
14145             st[name] = st[subIndex];
14146         }
14147         st[subIndex].tpl.compile();
14148         st[subIndex].tpl.call = this.call.createDelegate(this);
14149         subIndex++;
14150     }
14151     this.subCount = subIndex;
14152     this.subs = st;
14153 };
14154 Roo.extend(Roo.MasterTemplate, Roo.Template, {
14155     /**
14156     * The regular expression used to match sub templates
14157     * @type RegExp
14158     * @property
14159     */
14160     subTemplateRe : /<tpl(?:\sname="([\w-]+)")?>((?:.|\n)*?)<\/tpl>/gi,
14161
14162     /**
14163      * Applies the passed values to a child template.
14164      * @param {String/Number} name (optional) The name or index of the child template
14165      * @param {Array/Object} values The values to be applied to the template
14166      * @return {MasterTemplate} this
14167      */
14168      add : function(name, values){
14169         if(arguments.length == 1){
14170             values = arguments[0];
14171             name = 0;
14172         }
14173         var s = this.subs[name];
14174         s.buffer[s.buffer.length] = s.tpl.apply(values);
14175         return this;
14176     },
14177
14178     /**
14179      * Applies all the passed values to a child template.
14180      * @param {String/Number} name (optional) The name or index of the child template
14181      * @param {Array} values The values to be applied to the template, this should be an array of objects.
14182      * @param {Boolean} reset (optional) True to reset the template first
14183      * @return {MasterTemplate} this
14184      */
14185     fill : function(name, values, reset){
14186         var a = arguments;
14187         if(a.length == 1 || (a.length == 2 && typeof a[1] == "boolean")){
14188             values = a[0];
14189             name = 0;
14190             reset = a[1];
14191         }
14192         if(reset){
14193             this.reset();
14194         }
14195         for(var i = 0, len = values.length; i < len; i++){
14196             this.add(name, values[i]);
14197         }
14198         return this;
14199     },
14200
14201     /**
14202      * Resets the template for reuse
14203      * @return {MasterTemplate} this
14204      */
14205      reset : function(){
14206         var s = this.subs;
14207         for(var i = 0; i < this.subCount; i++){
14208             s[i].buffer = [];
14209         }
14210         return this;
14211     },
14212
14213     applyTemplate : function(values){
14214         var s = this.subs;
14215         var replaceIndex = -1;
14216         this.html = this.originalHtml.replace(this.subTemplateRe, function(m, name){
14217             return s[++replaceIndex].buffer.join("");
14218         });
14219         return Roo.MasterTemplate.superclass.applyTemplate.call(this, values);
14220     },
14221
14222     apply : function(){
14223         return this.applyTemplate.apply(this, arguments);
14224     },
14225
14226     compile : function(){return this;}
14227 });
14228
14229 /**
14230  * Alias for fill().
14231  * @method
14232  */
14233 Roo.MasterTemplate.prototype.addAll = Roo.MasterTemplate.prototype.fill;
14234  /**
14235  * Creates a template from the passed element's value (display:none textarea, preferred) or innerHTML. e.g.
14236  * var tpl = Roo.MasterTemplate.from('element-id');
14237  * @param {String/HTMLElement} el
14238  * @param {Object} config
14239  * @static
14240  */
14241 Roo.MasterTemplate.from = function(el, config){
14242     el = Roo.getDom(el);
14243     return new Roo.MasterTemplate(el.value || el.innerHTML, config || '');
14244 };/*
14245  * Based on:
14246  * Ext JS Library 1.1.1
14247  * Copyright(c) 2006-2007, Ext JS, LLC.
14248  *
14249  * Originally Released Under LGPL - original licence link has changed is not relivant.
14250  *
14251  * Fork - LGPL
14252  * <script type="text/javascript">
14253  */
14254
14255  
14256 /**
14257  * @class Roo.util.CSS
14258  * Utility class for manipulating CSS rules
14259  * @singleton
14260  */
14261 Roo.util.CSS = function(){
14262         var rules = null;
14263         var doc = document;
14264
14265     var camelRe = /(-[a-z])/gi;
14266     var camelFn = function(m, a){ return a.charAt(1).toUpperCase(); };
14267
14268    return {
14269    /**
14270     * Very simple dynamic creation of stylesheets from a text blob of rules.  The text will wrapped in a style
14271     * tag and appended to the HEAD of the document.
14272     * @param {String|Object} cssText The text containing the css rules
14273     * @param {String} id An id to add to the stylesheet for later removal
14274     * @return {StyleSheet}
14275     */
14276     createStyleSheet : function(cssText, id){
14277         var ss;
14278         var head = doc.getElementsByTagName("head")[0];
14279         var nrules = doc.createElement("style");
14280         nrules.setAttribute("type", "text/css");
14281         if(id){
14282             nrules.setAttribute("id", id);
14283         }
14284         if (typeof(cssText) != 'string') {
14285             // support object maps..
14286             // not sure if this a good idea.. 
14287             // perhaps it should be merged with the general css handling
14288             // and handle js style props.
14289             var cssTextNew = [];
14290             for(var n in cssText) {
14291                 var citems = [];
14292                 for(var k in cssText[n]) {
14293                     citems.push( k + ' : ' +cssText[n][k] + ';' );
14294                 }
14295                 cssTextNew.push( n + ' { ' + citems.join(' ') + '} ');
14296                 
14297             }
14298             cssText = cssTextNew.join("\n");
14299             
14300         }
14301        
14302        
14303        if(Roo.isIE){
14304            head.appendChild(nrules);
14305            ss = nrules.styleSheet;
14306            ss.cssText = cssText;
14307        }else{
14308            try{
14309                 nrules.appendChild(doc.createTextNode(cssText));
14310            }catch(e){
14311                nrules.cssText = cssText; 
14312            }
14313            head.appendChild(nrules);
14314            ss = nrules.styleSheet ? nrules.styleSheet : (nrules.sheet || doc.styleSheets[doc.styleSheets.length-1]);
14315        }
14316        this.cacheStyleSheet(ss);
14317        return ss;
14318    },
14319
14320    /**
14321     * Removes a style or link tag by id
14322     * @param {String} id The id of the tag
14323     */
14324    removeStyleSheet : function(id){
14325        var existing = doc.getElementById(id);
14326        if(existing){
14327            existing.parentNode.removeChild(existing);
14328        }
14329    },
14330
14331    /**
14332     * Dynamically swaps an existing stylesheet reference for a new one
14333     * @param {String} id The id of an existing link tag to remove
14334     * @param {String} url The href of the new stylesheet to include
14335     */
14336    swapStyleSheet : function(id, url){
14337        this.removeStyleSheet(id);
14338        var ss = doc.createElement("link");
14339        ss.setAttribute("rel", "stylesheet");
14340        ss.setAttribute("type", "text/css");
14341        ss.setAttribute("id", id);
14342        ss.setAttribute("href", url);
14343        doc.getElementsByTagName("head")[0].appendChild(ss);
14344    },
14345    
14346    /**
14347     * Refresh the rule cache if you have dynamically added stylesheets
14348     * @return {Object} An object (hash) of rules indexed by selector
14349     */
14350    refreshCache : function(){
14351        return this.getRules(true);
14352    },
14353
14354    // private
14355    cacheStyleSheet : function(stylesheet){
14356        if(!rules){
14357            rules = {};
14358        }
14359        try{// try catch for cross domain access issue
14360            var ssRules = stylesheet.cssRules || stylesheet.rules;
14361            for(var j = ssRules.length-1; j >= 0; --j){
14362                rules[ssRules[j].selectorText] = ssRules[j];
14363            }
14364        }catch(e){}
14365    },
14366    
14367    /**
14368     * Gets all css rules for the document
14369     * @param {Boolean} refreshCache true to refresh the internal cache
14370     * @return {Object} An object (hash) of rules indexed by selector
14371     */
14372    getRules : function(refreshCache){
14373                 if(rules == null || refreshCache){
14374                         rules = {};
14375                         var ds = doc.styleSheets;
14376                         for(var i =0, len = ds.length; i < len; i++){
14377                             try{
14378                         this.cacheStyleSheet(ds[i]);
14379                     }catch(e){} 
14380                 }
14381                 }
14382                 return rules;
14383         },
14384         
14385         /**
14386     * Gets an an individual CSS rule by selector(s)
14387     * @param {String/Array} selector The CSS selector or an array of selectors to try. The first selector that is found is returned.
14388     * @param {Boolean} refreshCache true to refresh the internal cache if you have recently updated any rules or added styles dynamically
14389     * @return {CSSRule} The CSS rule or null if one is not found
14390     */
14391    getRule : function(selector, refreshCache){
14392                 var rs = this.getRules(refreshCache);
14393                 if(!(selector instanceof Array)){
14394                     return rs[selector];
14395                 }
14396                 for(var i = 0; i < selector.length; i++){
14397                         if(rs[selector[i]]){
14398                                 return rs[selector[i]];
14399                         }
14400                 }
14401                 return null;
14402         },
14403         
14404         
14405         /**
14406     * Updates a rule property
14407     * @param {String/Array} selector If it's an array it tries each selector until it finds one. Stops immediately once one is found.
14408     * @param {String} property The css property
14409     * @param {String} value The new value for the property
14410     * @return {Boolean} true If a rule was found and updated
14411     */
14412    updateRule : function(selector, property, value){
14413                 if(!(selector instanceof Array)){
14414                         var rule = this.getRule(selector);
14415                         if(rule){
14416                                 rule.style[property.replace(camelRe, camelFn)] = value;
14417                                 return true;
14418                         }
14419                 }else{
14420                         for(var i = 0; i < selector.length; i++){
14421                                 if(this.updateRule(selector[i], property, value)){
14422                                         return true;
14423                                 }
14424                         }
14425                 }
14426                 return false;
14427         }
14428    };   
14429 }();/*
14430  * Based on:
14431  * Ext JS Library 1.1.1
14432  * Copyright(c) 2006-2007, Ext JS, LLC.
14433  *
14434  * Originally Released Under LGPL - original licence link has changed is not relivant.
14435  *
14436  * Fork - LGPL
14437  * <script type="text/javascript">
14438  */
14439
14440  
14441
14442 /**
14443  * @class Roo.util.ClickRepeater
14444  * @extends Roo.util.Observable
14445  * 
14446  * A wrapper class which can be applied to any element. Fires a "click" event while the
14447  * mouse is pressed. The interval between firings may be specified in the config but
14448  * defaults to 10 milliseconds.
14449  * 
14450  * Optionally, a CSS class may be applied to the element during the time it is pressed.
14451  * 
14452  * @cfg {String/HTMLElement/Element} el The element to act as a button.
14453  * @cfg {Number} delay The initial delay before the repeating event begins firing.
14454  * Similar to an autorepeat key delay.
14455  * @cfg {Number} interval The interval between firings of the "click" event. Default 10 ms.
14456  * @cfg {String} pressClass A CSS class name to be applied to the element while pressed.
14457  * @cfg {Boolean} accelerate True if autorepeating should start slowly and accelerate.
14458  *           "interval" and "delay" are ignored. "immediate" is honored.
14459  * @cfg {Boolean} preventDefault True to prevent the default click event
14460  * @cfg {Boolean} stopDefault True to stop the default click event
14461  * 
14462  * @history
14463  *     2007-02-02 jvs Original code contributed by Nige "Animal" White
14464  *     2007-02-02 jvs Renamed to ClickRepeater
14465  *   2007-02-03 jvs Modifications for FF Mac and Safari 
14466  *
14467  *  @constructor
14468  * @param {String/HTMLElement/Element} el The element to listen on
14469  * @param {Object} config
14470  **/
14471 Roo.util.ClickRepeater = function(el, config)
14472 {
14473     this.el = Roo.get(el);
14474     this.el.unselectable();
14475
14476     Roo.apply(this, config);
14477
14478     this.addEvents({
14479     /**
14480      * @event mousedown
14481      * Fires when the mouse button is depressed.
14482      * @param {Roo.util.ClickRepeater} this
14483      */
14484         "mousedown" : true,
14485     /**
14486      * @event click
14487      * Fires on a specified interval during the time the element is pressed.
14488      * @param {Roo.util.ClickRepeater} this
14489      */
14490         "click" : true,
14491     /**
14492      * @event mouseup
14493      * Fires when the mouse key is released.
14494      * @param {Roo.util.ClickRepeater} this
14495      */
14496         "mouseup" : true
14497     });
14498
14499     this.el.on("mousedown", this.handleMouseDown, this);
14500     if(this.preventDefault || this.stopDefault){
14501         this.el.on("click", function(e){
14502             if(this.preventDefault){
14503                 e.preventDefault();
14504             }
14505             if(this.stopDefault){
14506                 e.stopEvent();
14507             }
14508         }, this);
14509     }
14510
14511     // allow inline handler
14512     if(this.handler){
14513         this.on("click", this.handler,  this.scope || this);
14514     }
14515
14516     Roo.util.ClickRepeater.superclass.constructor.call(this);
14517 };
14518
14519 Roo.extend(Roo.util.ClickRepeater, Roo.util.Observable, {
14520     interval : 20,
14521     delay: 250,
14522     preventDefault : true,
14523     stopDefault : false,
14524     timer : 0,
14525
14526     // private
14527     handleMouseDown : function(){
14528         clearTimeout(this.timer);
14529         this.el.blur();
14530         if(this.pressClass){
14531             this.el.addClass(this.pressClass);
14532         }
14533         this.mousedownTime = new Date();
14534
14535         Roo.get(document).on("mouseup", this.handleMouseUp, this);
14536         this.el.on("mouseout", this.handleMouseOut, this);
14537
14538         this.fireEvent("mousedown", this);
14539         this.fireEvent("click", this);
14540         
14541         this.timer = this.click.defer(this.delay || this.interval, this);
14542     },
14543
14544     // private
14545     click : function(){
14546         this.fireEvent("click", this);
14547         this.timer = this.click.defer(this.getInterval(), this);
14548     },
14549
14550     // private
14551     getInterval: function(){
14552         if(!this.accelerate){
14553             return this.interval;
14554         }
14555         var pressTime = this.mousedownTime.getElapsed();
14556         if(pressTime < 500){
14557             return 400;
14558         }else if(pressTime < 1700){
14559             return 320;
14560         }else if(pressTime < 2600){
14561             return 250;
14562         }else if(pressTime < 3500){
14563             return 180;
14564         }else if(pressTime < 4400){
14565             return 140;
14566         }else if(pressTime < 5300){
14567             return 80;
14568         }else if(pressTime < 6200){
14569             return 50;
14570         }else{
14571             return 10;
14572         }
14573     },
14574
14575     // private
14576     handleMouseOut : function(){
14577         clearTimeout(this.timer);
14578         if(this.pressClass){
14579             this.el.removeClass(this.pressClass);
14580         }
14581         this.el.on("mouseover", this.handleMouseReturn, this);
14582     },
14583
14584     // private
14585     handleMouseReturn : function(){
14586         this.el.un("mouseover", this.handleMouseReturn);
14587         if(this.pressClass){
14588             this.el.addClass(this.pressClass);
14589         }
14590         this.click();
14591     },
14592
14593     // private
14594     handleMouseUp : function(){
14595         clearTimeout(this.timer);
14596         this.el.un("mouseover", this.handleMouseReturn);
14597         this.el.un("mouseout", this.handleMouseOut);
14598         Roo.get(document).un("mouseup", this.handleMouseUp);
14599         this.el.removeClass(this.pressClass);
14600         this.fireEvent("mouseup", this);
14601     }
14602 });/**
14603  * @class Roo.util.Clipboard
14604  * @static
14605  * 
14606  * Clipboard UTILS
14607  * 
14608  **/
14609 Roo.util.Clipboard = {
14610     /**
14611      * Writes a string to the clipboard - using the Clipboard API if https, otherwise using text area.
14612      * @param {String} text to copy to clipboard
14613      */
14614     write : function(text) {
14615         // navigator clipboard api needs a secure context (https)
14616         if (navigator.clipboard && window.isSecureContext) {
14617             // navigator clipboard api method'
14618             navigator.clipboard.writeText(text);
14619             return ;
14620         } 
14621         // text area method
14622         var ta = document.createElement("textarea");
14623         ta.value = text;
14624         // make the textarea out of viewport
14625         ta.style.position = "fixed";
14626         ta.style.left = "-999999px";
14627         ta.style.top = "-999999px";
14628         document.body.appendChild(ta);
14629         ta.focus();
14630         ta.select();
14631         document.execCommand('copy');
14632         (function() {
14633             ta.remove();
14634         }).defer(100);
14635         
14636     }
14637         
14638 }
14639     /*
14640  * Based on:
14641  * Ext JS Library 1.1.1
14642  * Copyright(c) 2006-2007, Ext JS, LLC.
14643  *
14644  * Originally Released Under LGPL - original licence link has changed is not relivant.
14645  *
14646  * Fork - LGPL
14647  * <script type="text/javascript">
14648  */
14649
14650  
14651 /**
14652  * @class Roo.KeyNav
14653  * <p>Provides a convenient wrapper for normalized keyboard navigation.  KeyNav allows you to bind
14654  * navigation keys to function calls that will get called when the keys are pressed, providing an easy
14655  * way to implement custom navigation schemes for any UI component.</p>
14656  * <p>The following are all of the possible keys that can be implemented: enter, left, right, up, down, tab, esc,
14657  * pageUp, pageDown, del, home, end.  Usage:</p>
14658  <pre><code>
14659 var nav = new Roo.KeyNav("my-element", {
14660     "left" : function(e){
14661         this.moveLeft(e.ctrlKey);
14662     },
14663     "right" : function(e){
14664         this.moveRight(e.ctrlKey);
14665     },
14666     "enter" : function(e){
14667         this.save();
14668     },
14669     scope : this
14670 });
14671 </code></pre>
14672  * @constructor
14673  * @param {String/HTMLElement/Roo.Element} el The element to bind to
14674  * @param {Object} config The config
14675  */
14676 Roo.KeyNav = function(el, config){
14677     this.el = Roo.get(el);
14678     Roo.apply(this, config);
14679     if(!this.disabled){
14680         this.disabled = true;
14681         this.enable();
14682     }
14683 };
14684
14685 Roo.KeyNav.prototype = {
14686     /**
14687      * @cfg {Boolean} disabled
14688      * True to disable this KeyNav instance (defaults to false)
14689      */
14690     disabled : false,
14691     /**
14692      * @cfg {String} defaultEventAction
14693      * The method to call on the {@link Roo.EventObject} after this KeyNav intercepts a key.  Valid values are
14694      * {@link Roo.EventObject#stopEvent}, {@link Roo.EventObject#preventDefault} and
14695      * {@link Roo.EventObject#stopPropagation} (defaults to 'stopEvent')
14696      */
14697     defaultEventAction: "stopEvent",
14698     /**
14699      * @cfg {Boolean} forceKeyDown
14700      * Handle the keydown event instead of keypress (defaults to false).  KeyNav automatically does this for IE since
14701      * IE does not propagate special keys on keypress, but setting this to true will force other browsers to also
14702      * handle keydown instead of keypress.
14703      */
14704     forceKeyDown : false,
14705
14706     // private
14707     prepareEvent : function(e){
14708         var k = e.getKey();
14709         var h = this.keyToHandler[k];
14710         //if(h && this[h]){
14711         //    e.stopPropagation();
14712         //}
14713         if(Roo.isSafari && h && k >= 37 && k <= 40){
14714             e.stopEvent();
14715         }
14716     },
14717
14718     // private
14719     relay : function(e){
14720         var k = e.getKey();
14721         var h = this.keyToHandler[k];
14722         if(h && this[h]){
14723             if(this.doRelay(e, this[h], h) !== true){
14724                 e[this.defaultEventAction]();
14725             }
14726         }
14727     },
14728
14729     // private
14730     doRelay : function(e, h, hname){
14731         return h.call(this.scope || this, e);
14732     },
14733
14734     // possible handlers
14735     enter : false,
14736     left : false,
14737     right : false,
14738     up : false,
14739     down : false,
14740     tab : false,
14741     esc : false,
14742     pageUp : false,
14743     pageDown : false,
14744     del : false,
14745     home : false,
14746     end : false,
14747
14748     // quick lookup hash
14749     keyToHandler : {
14750         37 : "left",
14751         39 : "right",
14752         38 : "up",
14753         40 : "down",
14754         33 : "pageUp",
14755         34 : "pageDown",
14756         46 : "del",
14757         36 : "home",
14758         35 : "end",
14759         13 : "enter",
14760         27 : "esc",
14761         9  : "tab"
14762     },
14763
14764         /**
14765          * Enable this KeyNav
14766          */
14767         enable: function(){
14768                 if(this.disabled){
14769             // ie won't do special keys on keypress, no one else will repeat keys with keydown
14770             // the EventObject will normalize Safari automatically
14771             if(this.forceKeyDown || Roo.isIE || Roo.isAir){
14772                 this.el.on("keydown", this.relay,  this);
14773             }else{
14774                 this.el.on("keydown", this.prepareEvent,  this);
14775                 this.el.on("keypress", this.relay,  this);
14776             }
14777                     this.disabled = false;
14778                 }
14779         },
14780
14781         /**
14782          * Disable this KeyNav
14783          */
14784         disable: function(){
14785                 if(!this.disabled){
14786                     if(this.forceKeyDown || Roo.isIE || Roo.isAir){
14787                 this.el.un("keydown", this.relay);
14788             }else{
14789                 this.el.un("keydown", this.prepareEvent);
14790                 this.el.un("keypress", this.relay);
14791             }
14792                     this.disabled = true;
14793                 }
14794         }
14795 };/*
14796  * Based on:
14797  * Ext JS Library 1.1.1
14798  * Copyright(c) 2006-2007, Ext JS, LLC.
14799  *
14800  * Originally Released Under LGPL - original licence link has changed is not relivant.
14801  *
14802  * Fork - LGPL
14803  * <script type="text/javascript">
14804  */
14805
14806  
14807 /**
14808  * @class Roo.KeyMap
14809  * Handles mapping keys to actions for an element. One key map can be used for multiple actions.
14810  * The constructor accepts the same config object as defined by {@link #addBinding}.
14811  * If you bind a callback function to a KeyMap, anytime the KeyMap handles an expected key
14812  * combination it will call the function with this signature (if the match is a multi-key
14813  * combination the callback will still be called only once): (String key, Roo.EventObject e)
14814  * A KeyMap can also handle a string representation of keys.<br />
14815  * Usage:
14816  <pre><code>
14817 // map one key by key code
14818 var map = new Roo.KeyMap("my-element", {
14819     key: 13, // or Roo.EventObject.ENTER
14820     fn: myHandler,
14821     scope: myObject
14822 });
14823
14824 // map multiple keys to one action by string
14825 var map = new Roo.KeyMap("my-element", {
14826     key: "a\r\n\t",
14827     fn: myHandler,
14828     scope: myObject
14829 });
14830
14831 // map multiple keys to multiple actions by strings and array of codes
14832 var map = new Roo.KeyMap("my-element", [
14833     {
14834         key: [10,13],
14835         fn: function(){ alert("Return was pressed"); }
14836     }, {
14837         key: "abc",
14838         fn: function(){ alert('a, b or c was pressed'); }
14839     }, {
14840         key: "\t",
14841         ctrl:true,
14842         shift:true,
14843         fn: function(){ alert('Control + shift + tab was pressed.'); }
14844     }
14845 ]);
14846 </code></pre>
14847  * <b>Note: A KeyMap starts enabled</b>
14848  * @constructor
14849  * @param {String/HTMLElement/Roo.Element} el The element to bind to
14850  * @param {Object} config The config (see {@link #addBinding})
14851  * @param {String} eventName (optional) The event to bind to (defaults to "keydown")
14852  */
14853 Roo.KeyMap = function(el, config, eventName){
14854     this.el  = Roo.get(el);
14855     this.eventName = eventName || "keydown";
14856     this.bindings = [];
14857     if(config){
14858         this.addBinding(config);
14859     }
14860     this.enable();
14861 };
14862
14863 Roo.KeyMap.prototype = {
14864     /**
14865      * True to stop the event from bubbling and prevent the default browser action if the
14866      * key was handled by the KeyMap (defaults to false)
14867      * @type Boolean
14868      */
14869     stopEvent : false,
14870
14871     /**
14872      * Add a new binding to this KeyMap. The following config object properties are supported:
14873      * <pre>
14874 Property    Type             Description
14875 ----------  ---------------  ----------------------------------------------------------------------
14876 key         String/Array     A single keycode or an array of keycodes to handle
14877 shift       Boolean          True to handle key only when shift is pressed (defaults to false)
14878 ctrl        Boolean          True to handle key only when ctrl is pressed (defaults to false)
14879 alt         Boolean          True to handle key only when alt is pressed (defaults to false)
14880 fn          Function         The function to call when KeyMap finds the expected key combination
14881 scope       Object           The scope of the callback function
14882 </pre>
14883      *
14884      * Usage:
14885      * <pre><code>
14886 // Create a KeyMap
14887 var map = new Roo.KeyMap(document, {
14888     key: Roo.EventObject.ENTER,
14889     fn: handleKey,
14890     scope: this
14891 });
14892
14893 //Add a new binding to the existing KeyMap later
14894 map.addBinding({
14895     key: 'abc',
14896     shift: true,
14897     fn: handleKey,
14898     scope: this
14899 });
14900 </code></pre>
14901      * @param {Object/Array} config A single KeyMap config or an array of configs
14902      */
14903         addBinding : function(config){
14904         if(config instanceof Array){
14905             for(var i = 0, len = config.length; i < len; i++){
14906                 this.addBinding(config[i]);
14907             }
14908             return;
14909         }
14910         var keyCode = config.key,
14911             shift = config.shift, 
14912             ctrl = config.ctrl, 
14913             alt = config.alt,
14914             fn = config.fn,
14915             scope = config.scope;
14916         if(typeof keyCode == "string"){
14917             var ks = [];
14918             var keyString = keyCode.toUpperCase();
14919             for(var j = 0, len = keyString.length; j < len; j++){
14920                 ks.push(keyString.charCodeAt(j));
14921             }
14922             keyCode = ks;
14923         }
14924         var keyArray = keyCode instanceof Array;
14925         var handler = function(e){
14926             if((!shift || e.shiftKey) && (!ctrl || e.ctrlKey) &&  (!alt || e.altKey)){
14927                 var k = e.getKey();
14928                 if(keyArray){
14929                     for(var i = 0, len = keyCode.length; i < len; i++){
14930                         if(keyCode[i] == k){
14931                           if(this.stopEvent){
14932                               e.stopEvent();
14933                           }
14934                           fn.call(scope || window, k, e);
14935                           return;
14936                         }
14937                     }
14938                 }else{
14939                     if(k == keyCode){
14940                         if(this.stopEvent){
14941                            e.stopEvent();
14942                         }
14943                         fn.call(scope || window, k, e);
14944                     }
14945                 }
14946             }
14947         };
14948         this.bindings.push(handler);  
14949         },
14950
14951     /**
14952      * Shorthand for adding a single key listener
14953      * @param {Number/Array/Object} key Either the numeric key code, array of key codes or an object with the
14954      * following options:
14955      * {key: (number or array), shift: (true/false), ctrl: (true/false), alt: (true/false)}
14956      * @param {Function} fn The function to call
14957      * @param {Object} scope (optional) The scope of the function
14958      */
14959     on : function(key, fn, scope){
14960         var keyCode, shift, ctrl, alt;
14961         if(typeof key == "object" && !(key instanceof Array)){
14962             keyCode = key.key;
14963             shift = key.shift;
14964             ctrl = key.ctrl;
14965             alt = key.alt;
14966         }else{
14967             keyCode = key;
14968         }
14969         this.addBinding({
14970             key: keyCode,
14971             shift: shift,
14972             ctrl: ctrl,
14973             alt: alt,
14974             fn: fn,
14975             scope: scope
14976         })
14977     },
14978
14979     // private
14980     handleKeyDown : function(e){
14981             if(this.enabled){ //just in case
14982             var b = this.bindings;
14983             for(var i = 0, len = b.length; i < len; i++){
14984                 b[i].call(this, e);
14985             }
14986             }
14987         },
14988         
14989         /**
14990          * Returns true if this KeyMap is enabled
14991          * @return {Boolean} 
14992          */
14993         isEnabled : function(){
14994             return this.enabled;  
14995         },
14996         
14997         /**
14998          * Enables this KeyMap
14999          */
15000         enable: function(){
15001                 if(!this.enabled){
15002                     this.el.on(this.eventName, this.handleKeyDown, this);
15003                     this.enabled = true;
15004                 }
15005         },
15006
15007         /**
15008          * Disable this KeyMap
15009          */
15010         disable: function(){
15011                 if(this.enabled){
15012                     this.el.removeListener(this.eventName, this.handleKeyDown, this);
15013                     this.enabled = false;
15014                 }
15015         }
15016 };/*
15017  * Based on:
15018  * Ext JS Library 1.1.1
15019  * Copyright(c) 2006-2007, Ext JS, LLC.
15020  *
15021  * Originally Released Under LGPL - original licence link has changed is not relivant.
15022  *
15023  * Fork - LGPL
15024  * <script type="text/javascript">
15025  */
15026
15027  
15028 /**
15029  * @class Roo.util.TextMetrics
15030  * Provides precise pixel measurements for blocks of text so that you can determine exactly how high and
15031  * wide, in pixels, a given block of text will be.
15032  * @singleton
15033  */
15034 Roo.util.TextMetrics = function(){
15035     var shared;
15036     return {
15037         /**
15038          * Measures the size of the specified text
15039          * @param {String/HTMLElement} el The element, dom node or id from which to copy existing CSS styles
15040          * that can affect the size of the rendered text
15041          * @param {String} text The text to measure
15042          * @param {Number} fixedWidth (optional) If the text will be multiline, you have to set a fixed width
15043          * in order to accurately measure the text height
15044          * @return {Object} An object containing the text's size {width: (width), height: (height)}
15045          */
15046         measure : function(el, text, fixedWidth){
15047             if(!shared){
15048                 shared = Roo.util.TextMetrics.Instance(el, fixedWidth);
15049             }
15050             shared.bind(el);
15051             shared.setFixedWidth(fixedWidth || 'auto');
15052             return shared.getSize(text);
15053         },
15054
15055         /**
15056          * Return a unique TextMetrics instance that can be bound directly to an element and reused.  This reduces
15057          * the overhead of multiple calls to initialize the style properties on each measurement.
15058          * @param {String/HTMLElement} el The element, dom node or id that the instance will be bound to
15059          * @param {Number} fixedWidth (optional) If the text will be multiline, you have to set a fixed width
15060          * in order to accurately measure the text height
15061          * @return {Roo.util.TextMetrics.Instance} instance The new instance
15062          */
15063         createInstance : function(el, fixedWidth){
15064             return Roo.util.TextMetrics.Instance(el, fixedWidth);
15065         }
15066     };
15067 }();
15068
15069  
15070
15071 Roo.util.TextMetrics.Instance = function(bindTo, fixedWidth){
15072     var ml = new Roo.Element(document.createElement('div'));
15073     document.body.appendChild(ml.dom);
15074     ml.position('absolute');
15075     ml.setLeftTop(-1000, -1000);
15076     ml.hide();
15077
15078     if(fixedWidth){
15079         ml.setWidth(fixedWidth);
15080     }
15081      
15082     var instance = {
15083         /**
15084          * Returns the size of the specified text based on the internal element's style and width properties
15085          * @memberOf Roo.util.TextMetrics.Instance#
15086          * @param {String} text The text to measure
15087          * @return {Object} An object containing the text's size {width: (width), height: (height)}
15088          */
15089         getSize : function(text){
15090             ml.update(text);
15091             var s = ml.getSize();
15092             ml.update('');
15093             return s;
15094         },
15095
15096         /**
15097          * Binds this TextMetrics instance to an element from which to copy existing CSS styles
15098          * that can affect the size of the rendered text
15099          * @memberOf Roo.util.TextMetrics.Instance#
15100          * @param {String/HTMLElement} el The element, dom node or id
15101          */
15102         bind : function(el){
15103             ml.setStyle(
15104                 Roo.fly(el).getStyles('font-size','font-style', 'font-weight', 'font-family','line-height')
15105             );
15106         },
15107
15108         /**
15109          * Sets a fixed width on the internal measurement element.  If the text will be multiline, you have
15110          * to set a fixed width in order to accurately measure the text height.
15111          * @memberOf Roo.util.TextMetrics.Instance#
15112          * @param {Number} width The width to set on the element
15113          */
15114         setFixedWidth : function(width){
15115             ml.setWidth(width);
15116         },
15117
15118         /**
15119          * Returns the measured width of the specified text
15120          * @memberOf Roo.util.TextMetrics.Instance#
15121          * @param {String} text The text to measure
15122          * @return {Number} width The width in pixels
15123          */
15124         getWidth : function(text){
15125             ml.dom.style.width = 'auto';
15126             return this.getSize(text).width;
15127         },
15128
15129         /**
15130          * Returns the measured height of the specified text.  For multiline text, be sure to call
15131          * {@link #setFixedWidth} if necessary.
15132          * @memberOf Roo.util.TextMetrics.Instance#
15133          * @param {String} text The text to measure
15134          * @return {Number} height The height in pixels
15135          */
15136         getHeight : function(text){
15137             return this.getSize(text).height;
15138         }
15139     };
15140
15141     instance.bind(bindTo);
15142
15143     return instance;
15144 };
15145
15146 // backwards compat
15147 Roo.Element.measureText = Roo.util.TextMetrics.measure;/*
15148  * Based on:
15149  * Ext JS Library 1.1.1
15150  * Copyright(c) 2006-2007, Ext JS, LLC.
15151  *
15152  * Originally Released Under LGPL - original licence link has changed is not relivant.
15153  *
15154  * Fork - LGPL
15155  * <script type="text/javascript">
15156  */
15157
15158 /**
15159  * @class Roo.state.Provider
15160  * Abstract base class for state provider implementations. This class provides methods
15161  * for encoding and decoding <b>typed</b> variables including dates and defines the 
15162  * Provider interface.
15163  */
15164 Roo.state.Provider = function(){
15165     /**
15166      * @event statechange
15167      * Fires when a state change occurs.
15168      * @param {Provider} this This state provider
15169      * @param {String} key The state key which was changed
15170      * @param {String} value The encoded value for the state
15171      */
15172     this.addEvents({
15173         "statechange": true
15174     });
15175     this.state = {};
15176     Roo.state.Provider.superclass.constructor.call(this);
15177 };
15178 Roo.extend(Roo.state.Provider, Roo.util.Observable, {
15179     /**
15180      * Returns the current value for a key
15181      * @param {String} name The key name
15182      * @param {Mixed} defaultValue A default value to return if the key's value is not found
15183      * @return {Mixed} The state data
15184      */
15185     get : function(name, defaultValue){
15186         return typeof this.state[name] == "undefined" ?
15187             defaultValue : this.state[name];
15188     },
15189     
15190     /**
15191      * Clears a value from the state
15192      * @param {String} name The key name
15193      */
15194     clear : function(name){
15195         delete this.state[name];
15196         this.fireEvent("statechange", this, name, null);
15197     },
15198     
15199     /**
15200      * Sets the value for a key
15201      * @param {String} name The key name
15202      * @param {Mixed} value The value to set
15203      */
15204     set : function(name, value){
15205         this.state[name] = value;
15206         this.fireEvent("statechange", this, name, value);
15207     },
15208     
15209     /**
15210      * Decodes a string previously encoded with {@link #encodeValue}.
15211      * @param {String} value The value to decode
15212      * @return {Mixed} The decoded value
15213      */
15214     decodeValue : function(cookie){
15215         var re = /^(a|n|d|b|s|o)\:(.*)$/;
15216         var matches = re.exec(unescape(cookie));
15217         if(!matches || !matches[1]) {
15218             return; // non state cookie
15219         }
15220         var type = matches[1];
15221         var v = matches[2];
15222         switch(type){
15223             case "n":
15224                 return parseFloat(v);
15225             case "d":
15226                 return new Date(Date.parse(v));
15227             case "b":
15228                 return (v == "1");
15229             case "a":
15230                 var all = [];
15231                 var values = v.split("^");
15232                 for(var i = 0, len = values.length; i < len; i++){
15233                     all.push(this.decodeValue(values[i]));
15234                 }
15235                 return all;
15236            case "o":
15237                 var all = {};
15238                 var values = v.split("^");
15239                 for(var i = 0, len = values.length; i < len; i++){
15240                     var kv = values[i].split("=");
15241                     all[kv[0]] = this.decodeValue(kv[1]);
15242                 }
15243                 return all;
15244            default:
15245                 return v;
15246         }
15247     },
15248     
15249     /**
15250      * Encodes a value including type information.  Decode with {@link #decodeValue}.
15251      * @param {Mixed} value The value to encode
15252      * @return {String} The encoded value
15253      */
15254     encodeValue : function(v){
15255         var enc;
15256         if(typeof v == "number"){
15257             enc = "n:" + v;
15258         }else if(typeof v == "boolean"){
15259             enc = "b:" + (v ? "1" : "0");
15260         }else if(v instanceof Date){
15261             enc = "d:" + v.toGMTString();
15262         }else if(v instanceof Array){
15263             var flat = "";
15264             for(var i = 0, len = v.length; i < len; i++){
15265                 flat += this.encodeValue(v[i]);
15266                 if(i != len-1) {
15267                     flat += "^";
15268                 }
15269             }
15270             enc = "a:" + flat;
15271         }else if(typeof v == "object"){
15272             var flat = "";
15273             for(var key in v){
15274                 if(typeof v[key] != "function"){
15275                     flat += key + "=" + this.encodeValue(v[key]) + "^";
15276                 }
15277             }
15278             enc = "o:" + flat.substring(0, flat.length-1);
15279         }else{
15280             enc = "s:" + v;
15281         }
15282         return escape(enc);        
15283     }
15284 });
15285
15286 /*
15287  * Based on:
15288  * Ext JS Library 1.1.1
15289  * Copyright(c) 2006-2007, Ext JS, LLC.
15290  *
15291  * Originally Released Under LGPL - original licence link has changed is not relivant.
15292  *
15293  * Fork - LGPL
15294  * <script type="text/javascript">
15295  */
15296 /**
15297  * @class Roo.state.Manager
15298  * This is the global state manager. By default all components that are "state aware" check this class
15299  * for state information if you don't pass them a custom state provider. In order for this class
15300  * to be useful, it must be initialized with a provider when your application initializes.
15301  <pre><code>
15302 // in your initialization function
15303 init : function(){
15304    Roo.state.Manager.setProvider(new Roo.state.CookieProvider());
15305    ...
15306    // supposed you have a {@link Roo.BorderLayout}
15307    var layout = new Roo.BorderLayout(...);
15308    layout.restoreState();
15309    // or a {Roo.BasicDialog}
15310    var dialog = new Roo.BasicDialog(...);
15311    dialog.restoreState();
15312  </code></pre>
15313  * @singleton
15314  */
15315 Roo.state.Manager = function(){
15316     var provider = new Roo.state.Provider();
15317     
15318     return {
15319         /**
15320          * Configures the default state provider for your application
15321          * @param {Provider} stateProvider The state provider to set
15322          */
15323         setProvider : function(stateProvider){
15324             provider = stateProvider;
15325         },
15326         
15327         /**
15328          * Returns the current value for a key
15329          * @param {String} name The key name
15330          * @param {Mixed} defaultValue The default value to return if the key lookup does not match
15331          * @return {Mixed} The state data
15332          */
15333         get : function(key, defaultValue){
15334             return provider.get(key, defaultValue);
15335         },
15336         
15337         /**
15338          * Sets the value for a key
15339          * @param {String} name The key name
15340          * @param {Mixed} value The state data
15341          */
15342          set : function(key, value){
15343             provider.set(key, value);
15344         },
15345         
15346         /**
15347          * Clears a value from the state
15348          * @param {String} name The key name
15349          */
15350         clear : function(key){
15351             provider.clear(key);
15352         },
15353         
15354         /**
15355          * Gets the currently configured state provider
15356          * @return {Provider} The state provider
15357          */
15358         getProvider : function(){
15359             return provider;
15360         }
15361     };
15362 }();
15363 /*
15364  * Based on:
15365  * Ext JS Library 1.1.1
15366  * Copyright(c) 2006-2007, Ext JS, LLC.
15367  *
15368  * Originally Released Under LGPL - original licence link has changed is not relivant.
15369  *
15370  * Fork - LGPL
15371  * <script type="text/javascript">
15372  */
15373 /**
15374  * @class Roo.state.CookieProvider
15375  * @extends Roo.state.Provider
15376  * The default Provider implementation which saves state via cookies.
15377  * <br />Usage:
15378  <pre><code>
15379    var cp = new Roo.state.CookieProvider({
15380        path: "/cgi-bin/",
15381        expires: new Date(new Date().getTime()+(1000*60*60*24*30)); //30 days
15382        domain: "roojs.com"
15383    })
15384    Roo.state.Manager.setProvider(cp);
15385  </code></pre>
15386  * @cfg {String} path The path for which the cookie is active (defaults to root '/' which makes it active for all pages in the site)
15387  * @cfg {Date} expires The cookie expiration date (defaults to 7 days from now)
15388  * @cfg {String} domain The domain to save the cookie for.  Note that you cannot specify a different domain than
15389  * your page is on, but you can specify a sub-domain, or simply the domain itself like 'roojs.com' to include
15390  * all sub-domains if you need to access cookies across different sub-domains (defaults to null which uses the same
15391  * domain the page is running on including the 'www' like 'www.roojs.com')
15392  * @cfg {Boolean} secure True if the site is using SSL (defaults to false)
15393  * @constructor
15394  * Create a new CookieProvider
15395  * @param {Object} config The configuration object
15396  */
15397 Roo.state.CookieProvider = function(config){
15398     Roo.state.CookieProvider.superclass.constructor.call(this);
15399     this.path = "/";
15400     this.expires = new Date(new Date().getTime()+(1000*60*60*24*7)); //7 days
15401     this.domain = null;
15402     this.secure = false;
15403     Roo.apply(this, config);
15404     this.state = this.readCookies();
15405 };
15406
15407 Roo.extend(Roo.state.CookieProvider, Roo.state.Provider, {
15408     // private
15409     set : function(name, value){
15410         if(typeof value == "undefined" || value === null){
15411             this.clear(name);
15412             return;
15413         }
15414         this.setCookie(name, value);
15415         Roo.state.CookieProvider.superclass.set.call(this, name, value);
15416     },
15417
15418     // private
15419     clear : function(name){
15420         this.clearCookie(name);
15421         Roo.state.CookieProvider.superclass.clear.call(this, name);
15422     },
15423
15424     // private
15425     readCookies : function(){
15426         var cookies = {};
15427         var c = document.cookie + ";";
15428         var re = /\s?(.*?)=(.*?);/g;
15429         var matches;
15430         while((matches = re.exec(c)) != null){
15431             var name = matches[1];
15432             var value = matches[2];
15433             if(name && name.substring(0,3) == "ys-"){
15434                 cookies[name.substr(3)] = this.decodeValue(value);
15435             }
15436         }
15437         return cookies;
15438     },
15439
15440     // private
15441     setCookie : function(name, value){
15442         document.cookie = "ys-"+ name + "=" + this.encodeValue(value) +
15443            ((this.expires == null) ? "" : ("; expires=" + this.expires.toGMTString())) +
15444            ((this.path == null) ? "" : ("; path=" + this.path)) +
15445            ((this.domain == null) ? "" : ("; domain=" + this.domain)) +
15446            ((this.secure == true) ? "; secure" : "");
15447     },
15448
15449     // private
15450     clearCookie : function(name){
15451         document.cookie = "ys-" + name + "=null; expires=Thu, 01-Jan-70 00:00:01 GMT" +
15452            ((this.path == null) ? "" : ("; path=" + this.path)) +
15453            ((this.domain == null) ? "" : ("; domain=" + this.domain)) +
15454            ((this.secure == true) ? "; secure" : "");
15455     }
15456 });/*
15457  * Based on:
15458  * Ext JS Library 1.1.1
15459  * Copyright(c) 2006-2007, Ext JS, LLC.
15460  *
15461  * Originally Released Under LGPL - original licence link has changed is not relivant.
15462  *
15463  * Fork - LGPL
15464  * <script type="text/javascript">
15465  */
15466  
15467
15468 /**
15469  * @class Roo.ComponentMgr
15470  * Provides a common registry of all components on a page so that they can be easily accessed by component id (see {@link Roo.getCmp}).
15471  * @singleton
15472  */
15473 Roo.ComponentMgr = function(){
15474     var all = new Roo.util.MixedCollection();
15475
15476     return {
15477         /**
15478          * Registers a component.
15479          * @param {Roo.Component} c The component
15480          */
15481         register : function(c){
15482             all.add(c);
15483         },
15484
15485         /**
15486          * Unregisters a component.
15487          * @param {Roo.Component} c The component
15488          */
15489         unregister : function(c){
15490             all.remove(c);
15491         },
15492
15493         /**
15494          * Returns a component by id
15495          * @param {String} id The component id
15496          */
15497         get : function(id){
15498             return all.get(id);
15499         },
15500
15501         /**
15502          * Registers a function that will be called when a specified component is added to ComponentMgr
15503          * @param {String} id The component id
15504          * @param {Funtction} fn The callback function
15505          * @param {Object} scope The scope of the callback
15506          */
15507         onAvailable : function(id, fn, scope){
15508             all.on("add", function(index, o){
15509                 if(o.id == id){
15510                     fn.call(scope || o, o);
15511                     all.un("add", fn, scope);
15512                 }
15513             });
15514         }
15515     };
15516 }();/*
15517  * Based on:
15518  * Ext JS Library 1.1.1
15519  * Copyright(c) 2006-2007, Ext JS, LLC.
15520  *
15521  * Originally Released Under LGPL - original licence link has changed is not relivant.
15522  *
15523  * Fork - LGPL
15524  * <script type="text/javascript">
15525  */
15526  
15527 /**
15528  * @class Roo.Component
15529  * @extends Roo.util.Observable
15530  * Base class for all major Roo components.  All subclasses of Component can automatically participate in the standard
15531  * Roo component lifecycle of creation, rendering and destruction.  They also have automatic support for basic hide/show
15532  * and enable/disable behavior.  Component allows any subclass to be lazy-rendered into any {@link Roo.Container} and
15533  * to be automatically registered with the {@link Roo.ComponentMgr} so that it can be referenced at any time via {@link Roo.getCmp}.
15534  * All visual components (widgets) that require rendering into a layout should subclass Component.
15535  * @constructor
15536  * @param {Roo.Element/String/Object} config The configuration options.  If an element is passed, it is set as the internal
15537  * 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
15538  * and is used as the component id.  Otherwise, it is assumed to be a standard config object and is applied to the component.
15539  */
15540 Roo.Component = function(config){
15541     config = config || {};
15542     if(config.tagName || config.dom || typeof config == "string"){ // element object
15543         config = {el: config, id: config.id || config};
15544     }
15545     this.initialConfig = config;
15546
15547     Roo.apply(this, config);
15548     this.addEvents({
15549         /**
15550          * @event disable
15551          * Fires after the component is disabled.
15552              * @param {Roo.Component} this
15553              */
15554         disable : true,
15555         /**
15556          * @event enable
15557          * Fires after the component is enabled.
15558              * @param {Roo.Component} this
15559              */
15560         enable : true,
15561         /**
15562          * @event beforeshow
15563          * Fires before the component is shown.  Return false to stop the show.
15564              * @param {Roo.Component} this
15565              */
15566         beforeshow : true,
15567         /**
15568          * @event show
15569          * Fires after the component is shown.
15570              * @param {Roo.Component} this
15571              */
15572         show : true,
15573         /**
15574          * @event beforehide
15575          * Fires before the component is hidden. Return false to stop the hide.
15576              * @param {Roo.Component} this
15577              */
15578         beforehide : true,
15579         /**
15580          * @event hide
15581          * Fires after the component is hidden.
15582              * @param {Roo.Component} this
15583              */
15584         hide : true,
15585         /**
15586          * @event beforerender
15587          * Fires before the component is rendered. Return false to stop the render.
15588              * @param {Roo.Component} this
15589              */
15590         beforerender : true,
15591         /**
15592          * @event render
15593          * Fires after the component is rendered.
15594              * @param {Roo.Component} this
15595              */
15596         render : true,
15597         /**
15598          * @event beforedestroy
15599          * Fires before the component is destroyed. Return false to stop the destroy.
15600              * @param {Roo.Component} this
15601              */
15602         beforedestroy : true,
15603         /**
15604          * @event destroy
15605          * Fires after the component is destroyed.
15606              * @param {Roo.Component} this
15607              */
15608         destroy : true
15609     });
15610     if(!this.id){
15611         this.id = "roo-comp-" + (++Roo.Component.AUTO_ID);
15612     }
15613     Roo.ComponentMgr.register(this);
15614     Roo.Component.superclass.constructor.call(this);
15615     this.initComponent();
15616     if(this.renderTo){ // not supported by all components yet. use at your own risk!
15617         this.render(this.renderTo);
15618         delete this.renderTo;
15619     }
15620 };
15621
15622 /** @private */
15623 Roo.Component.AUTO_ID = 1000;
15624
15625 Roo.extend(Roo.Component, Roo.util.Observable, {
15626     /**
15627      * @scope Roo.Component.prototype
15628      * @type {Boolean}
15629      * true if this component is hidden. Read-only.
15630      */
15631     hidden : false,
15632     /**
15633      * @type {Boolean}
15634      * true if this component is disabled. Read-only.
15635      */
15636     disabled : false,
15637     /**
15638      * @type {Boolean}
15639      * true if this component has been rendered. Read-only.
15640      */
15641     rendered : false,
15642     
15643     /** @cfg {String} disableClass
15644      * CSS class added to the component when it is disabled (defaults to "x-item-disabled").
15645      */
15646     disabledClass : "x-item-disabled",
15647         /** @cfg {Boolean} allowDomMove
15648          * Whether the component can move the Dom node when rendering (defaults to true).
15649          */
15650     allowDomMove : true,
15651     /** @cfg {String} hideMode (display|visibility)
15652      * How this component should hidden. Supported values are
15653      * "visibility" (css visibility), "offsets" (negative offset position) and
15654      * "display" (css display) - defaults to "display".
15655      */
15656     hideMode: 'display',
15657
15658     /** @private */
15659     ctype : "Roo.Component",
15660
15661     /**
15662      * @cfg {String} actionMode 
15663      * which property holds the element that used for  hide() / show() / disable() / enable()
15664      * default is 'el' for forms you probably want to set this to fieldEl 
15665      */
15666     actionMode : "el",
15667
15668     /** @private */
15669     getActionEl : function(){
15670         return this[this.actionMode];
15671     },
15672
15673     initComponent : Roo.emptyFn,
15674     /**
15675      * If this is a lazy rendering component, render it to its container element.
15676      * @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.
15677      */
15678     render : function(container, position){
15679         
15680         if(this.rendered){
15681             return this;
15682         }
15683         
15684         if(this.fireEvent("beforerender", this) === false){
15685             return false;
15686         }
15687         
15688         if(!container && this.el){
15689             this.el = Roo.get(this.el);
15690             container = this.el.dom.parentNode;
15691             this.allowDomMove = false;
15692         }
15693         this.container = Roo.get(container);
15694         this.rendered = true;
15695         if(position !== undefined){
15696             if(typeof position == 'number'){
15697                 position = this.container.dom.childNodes[position];
15698             }else{
15699                 position = Roo.getDom(position);
15700             }
15701         }
15702         this.onRender(this.container, position || null);
15703         if(this.cls){
15704             this.el.addClass(this.cls);
15705             delete this.cls;
15706         }
15707         if(this.style){
15708             this.el.applyStyles(this.style);
15709             delete this.style;
15710         }
15711         this.fireEvent("render", this);
15712         this.afterRender(this.container);
15713         if(this.hidden){
15714             this.hide();
15715         }
15716         if(this.disabled){
15717             this.disable();
15718         }
15719
15720         return this;
15721         
15722     },
15723
15724     /** @private */
15725     // default function is not really useful
15726     onRender : function(ct, position){
15727         if(this.el){
15728             this.el = Roo.get(this.el);
15729             if(this.allowDomMove !== false){
15730                 ct.dom.insertBefore(this.el.dom, position);
15731             }
15732         }
15733     },
15734
15735     /** @private */
15736     getAutoCreate : function(){
15737         var cfg = typeof this.autoCreate == "object" ?
15738                       this.autoCreate : Roo.apply({}, this.defaultAutoCreate);
15739         if(this.id && !cfg.id){
15740             cfg.id = this.id;
15741         }
15742         return cfg;
15743     },
15744
15745     /** @private */
15746     afterRender : Roo.emptyFn,
15747
15748     /**
15749      * Destroys this component by purging any event listeners, removing the component's element from the DOM,
15750      * removing the component from its {@link Roo.Container} (if applicable) and unregistering it from {@link Roo.ComponentMgr}.
15751      */
15752     destroy : function(){
15753         if(this.fireEvent("beforedestroy", this) !== false){
15754             this.purgeListeners();
15755             this.beforeDestroy();
15756             if(this.rendered){
15757                 this.el.removeAllListeners();
15758                 this.el.remove();
15759                 if(this.actionMode == "container"){
15760                     this.container.remove();
15761                 }
15762             }
15763             this.onDestroy();
15764             Roo.ComponentMgr.unregister(this);
15765             this.fireEvent("destroy", this);
15766         }
15767     },
15768
15769         /** @private */
15770     beforeDestroy : function(){
15771
15772     },
15773
15774         /** @private */
15775         onDestroy : function(){
15776
15777     },
15778
15779     /**
15780      * Returns the underlying {@link Roo.Element}.
15781      * @return {Roo.Element} The element
15782      */
15783     getEl : function(){
15784         return this.el;
15785     },
15786
15787     /**
15788      * Returns the id of this component.
15789      * @return {String}
15790      */
15791     getId : function(){
15792         return this.id;
15793     },
15794
15795     /**
15796      * Try to focus this component.
15797      * @param {Boolean} selectText True to also select the text in this component (if applicable)
15798      * @return {Roo.Component} this
15799      */
15800     focus : function(selectText){
15801         if(this.rendered){
15802             this.el.focus();
15803             if(selectText === true){
15804                 this.el.dom.select();
15805             }
15806         }
15807         return this;
15808     },
15809
15810     /** @private */
15811     blur : function(){
15812         if(this.rendered){
15813             this.el.blur();
15814         }
15815         return this;
15816     },
15817
15818     /**
15819      * Disable this component.
15820      * @return {Roo.Component} this
15821      */
15822     disable : function(){
15823         if(this.rendered){
15824             this.onDisable();
15825         }
15826         this.disabled = true;
15827         this.fireEvent("disable", this);
15828         return this;
15829     },
15830
15831         // private
15832     onDisable : function(){
15833         this.getActionEl().addClass(this.disabledClass);
15834         this.el.dom.disabled = true;
15835     },
15836
15837     /**
15838      * Enable this component.
15839      * @return {Roo.Component} this
15840      */
15841     enable : function(){
15842         if(this.rendered){
15843             this.onEnable();
15844         }
15845         this.disabled = false;
15846         this.fireEvent("enable", this);
15847         return this;
15848     },
15849
15850         // private
15851     onEnable : function(){
15852         this.getActionEl().removeClass(this.disabledClass);
15853         this.el.dom.disabled = false;
15854     },
15855
15856     /**
15857      * Convenience function for setting disabled/enabled by boolean.
15858      * @param {Boolean} disabled
15859      */
15860     setDisabled : function(disabled){
15861         this[disabled ? "disable" : "enable"]();
15862     },
15863
15864     /**
15865      * Show this component.
15866      * @return {Roo.Component} this
15867      */
15868     show: function(){
15869         if(this.fireEvent("beforeshow", this) !== false){
15870             this.hidden = false;
15871             if(this.rendered){
15872                 this.onShow();
15873             }
15874             this.fireEvent("show", this);
15875         }
15876         return this;
15877     },
15878
15879     // private
15880     onShow : function(){
15881         var ae = this.getActionEl();
15882         if(this.hideMode == 'visibility'){
15883             ae.dom.style.visibility = "visible";
15884         }else if(this.hideMode == 'offsets'){
15885             ae.removeClass('x-hidden');
15886         }else{
15887             ae.dom.style.display = "";
15888         }
15889     },
15890
15891     /**
15892      * Hide this component.
15893      * @return {Roo.Component} this
15894      */
15895     hide: function(){
15896         if(this.fireEvent("beforehide", this) !== false){
15897             this.hidden = true;
15898             if(this.rendered){
15899                 this.onHide();
15900             }
15901             this.fireEvent("hide", this);
15902         }
15903         return this;
15904     },
15905
15906     // private
15907     onHide : function(){
15908         var ae = this.getActionEl();
15909         if(this.hideMode == 'visibility'){
15910             ae.dom.style.visibility = "hidden";
15911         }else if(this.hideMode == 'offsets'){
15912             ae.addClass('x-hidden');
15913         }else{
15914             ae.dom.style.display = "none";
15915         }
15916     },
15917
15918     /**
15919      * Convenience function to hide or show this component by boolean.
15920      * @param {Boolean} visible True to show, false to hide
15921      * @return {Roo.Component} this
15922      */
15923     setVisible: function(visible){
15924         if(visible) {
15925             this.show();
15926         }else{
15927             this.hide();
15928         }
15929         return this;
15930     },
15931
15932     /**
15933      * Returns true if this component is visible.
15934      */
15935     isVisible : function(){
15936         return this.getActionEl().isVisible();
15937     },
15938
15939     cloneConfig : function(overrides){
15940         overrides = overrides || {};
15941         var id = overrides.id || Roo.id();
15942         var cfg = Roo.applyIf(overrides, this.initialConfig);
15943         cfg.id = id; // prevent dup id
15944         return new this.constructor(cfg);
15945     }
15946 });/*
15947  * Based on:
15948  * Ext JS Library 1.1.1
15949  * Copyright(c) 2006-2007, Ext JS, LLC.
15950  *
15951  * Originally Released Under LGPL - original licence link has changed is not relivant.
15952  *
15953  * Fork - LGPL
15954  * <script type="text/javascript">
15955  */
15956
15957 /**
15958  * @class Roo.BoxComponent
15959  * @extends Roo.Component
15960  * Base class for any visual {@link Roo.Component} that uses a box container.  BoxComponent provides automatic box
15961  * model adjustments for sizing and positioning and will work correctly withnin the Component rendering model.  All
15962  * container classes should subclass BoxComponent so that they will work consistently when nested within other Roo
15963  * layout containers.
15964  * @constructor
15965  * @param {Roo.Element/String/Object} config The configuration options.
15966  */
15967 Roo.BoxComponent = function(config){
15968     Roo.Component.call(this, config);
15969     this.addEvents({
15970         /**
15971          * @event resize
15972          * Fires after the component is resized.
15973              * @param {Roo.Component} this
15974              * @param {Number} adjWidth The box-adjusted width that was set
15975              * @param {Number} adjHeight The box-adjusted height that was set
15976              * @param {Number} rawWidth The width that was originally specified
15977              * @param {Number} rawHeight The height that was originally specified
15978              */
15979         resize : true,
15980         /**
15981          * @event move
15982          * Fires after the component is moved.
15983              * @param {Roo.Component} this
15984              * @param {Number} x The new x position
15985              * @param {Number} y The new y position
15986              */
15987         move : true
15988     });
15989 };
15990
15991 Roo.extend(Roo.BoxComponent, Roo.Component, {
15992     // private, set in afterRender to signify that the component has been rendered
15993     boxReady : false,
15994     // private, used to defer height settings to subclasses
15995     deferHeight: false,
15996     /** @cfg {Number} width
15997      * width (optional) size of component
15998      */
15999      /** @cfg {Number} height
16000      * height (optional) size of component
16001      */
16002      
16003     /**
16004      * Sets the width and height of the component.  This method fires the resize event.  This method can accept
16005      * either width and height as separate numeric arguments, or you can pass a size object like {width:10, height:20}.
16006      * @param {Number/Object} width The new width to set, or a size object in the format {width, height}
16007      * @param {Number} height The new height to set (not required if a size object is passed as the first arg)
16008      * @return {Roo.BoxComponent} this
16009      */
16010     setSize : function(w, h){
16011         // support for standard size objects
16012         if(typeof w == 'object'){
16013             h = w.height;
16014             w = w.width;
16015         }
16016         // not rendered
16017         if(!this.boxReady){
16018             this.width = w;
16019             this.height = h;
16020             return this;
16021         }
16022
16023         // prevent recalcs when not needed
16024         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
16025             return this;
16026         }
16027         this.lastSize = {width: w, height: h};
16028
16029         var adj = this.adjustSize(w, h);
16030         var aw = adj.width, ah = adj.height;
16031         if(aw !== undefined || ah !== undefined){ // this code is nasty but performs better with floaters
16032             var rz = this.getResizeEl();
16033             if(!this.deferHeight && aw !== undefined && ah !== undefined){
16034                 rz.setSize(aw, ah);
16035             }else if(!this.deferHeight && ah !== undefined){
16036                 rz.setHeight(ah);
16037             }else if(aw !== undefined){
16038                 rz.setWidth(aw);
16039             }
16040             this.onResize(aw, ah, w, h);
16041             this.fireEvent('resize', this, aw, ah, w, h);
16042         }
16043         return this;
16044     },
16045
16046     /**
16047      * Gets the current size of the component's underlying element.
16048      * @return {Object} An object containing the element's size {width: (element width), height: (element height)}
16049      */
16050     getSize : function(){
16051         return this.el.getSize();
16052     },
16053
16054     /**
16055      * Gets the current XY position of the component's underlying element.
16056      * @param {Boolean} local (optional) If true the element's left and top are returned instead of page XY (defaults to false)
16057      * @return {Array} The XY position of the element (e.g., [100, 200])
16058      */
16059     getPosition : function(local){
16060         if(local === true){
16061             return [this.el.getLeft(true), this.el.getTop(true)];
16062         }
16063         return this.xy || this.el.getXY();
16064     },
16065
16066     /**
16067      * Gets the current box measurements of the component's underlying element.
16068      * @param {Boolean} local (optional) If true the element's left and top are returned instead of page XY (defaults to false)
16069      * @returns {Object} box An object in the format {x, y, width, height}
16070      */
16071     getBox : function(local){
16072         var s = this.el.getSize();
16073         if(local){
16074             s.x = this.el.getLeft(true);
16075             s.y = this.el.getTop(true);
16076         }else{
16077             var xy = this.xy || this.el.getXY();
16078             s.x = xy[0];
16079             s.y = xy[1];
16080         }
16081         return s;
16082     },
16083
16084     /**
16085      * Sets the current box measurements of the component's underlying element.
16086      * @param {Object} box An object in the format {x, y, width, height}
16087      * @returns {Roo.BoxComponent} this
16088      */
16089     updateBox : function(box){
16090         this.setSize(box.width, box.height);
16091         this.setPagePosition(box.x, box.y);
16092         return this;
16093     },
16094
16095     // protected
16096     getResizeEl : function(){
16097         return this.resizeEl || this.el;
16098     },
16099
16100     // protected
16101     getPositionEl : function(){
16102         return this.positionEl || this.el;
16103     },
16104
16105     /**
16106      * Sets the left and top of the component.  To set the page XY position instead, use {@link #setPagePosition}.
16107      * This method fires the move event.
16108      * @param {Number} left The new left
16109      * @param {Number} top The new top
16110      * @returns {Roo.BoxComponent} this
16111      */
16112     setPosition : function(x, y){
16113         this.x = x;
16114         this.y = y;
16115         if(!this.boxReady){
16116             return this;
16117         }
16118         var adj = this.adjustPosition(x, y);
16119         var ax = adj.x, ay = adj.y;
16120
16121         var el = this.getPositionEl();
16122         if(ax !== undefined || ay !== undefined){
16123             if(ax !== undefined && ay !== undefined){
16124                 el.setLeftTop(ax, ay);
16125             }else if(ax !== undefined){
16126                 el.setLeft(ax);
16127             }else if(ay !== undefined){
16128                 el.setTop(ay);
16129             }
16130             this.onPosition(ax, ay);
16131             this.fireEvent('move', this, ax, ay);
16132         }
16133         return this;
16134     },
16135
16136     /**
16137      * Sets the page XY position of the component.  To set the left and top instead, use {@link #setPosition}.
16138      * This method fires the move event.
16139      * @param {Number} x The new x position
16140      * @param {Number} y The new y position
16141      * @returns {Roo.BoxComponent} this
16142      */
16143     setPagePosition : function(x, y){
16144         this.pageX = x;
16145         this.pageY = y;
16146         if(!this.boxReady){
16147             return;
16148         }
16149         if(x === undefined || y === undefined){ // cannot translate undefined points
16150             return;
16151         }
16152         var p = this.el.translatePoints(x, y);
16153         this.setPosition(p.left, p.top);
16154         return this;
16155     },
16156
16157     // private
16158     onRender : function(ct, position){
16159         Roo.BoxComponent.superclass.onRender.call(this, ct, position);
16160         if(this.resizeEl){
16161             this.resizeEl = Roo.get(this.resizeEl);
16162         }
16163         if(this.positionEl){
16164             this.positionEl = Roo.get(this.positionEl);
16165         }
16166     },
16167
16168     // private
16169     afterRender : function(){
16170         Roo.BoxComponent.superclass.afterRender.call(this);
16171         this.boxReady = true;
16172         this.setSize(this.width, this.height);
16173         if(this.x || this.y){
16174             this.setPosition(this.x, this.y);
16175         }
16176         if(this.pageX || this.pageY){
16177             this.setPagePosition(this.pageX, this.pageY);
16178         }
16179     },
16180
16181     /**
16182      * Force the component's size to recalculate based on the underlying element's current height and width.
16183      * @returns {Roo.BoxComponent} this
16184      */
16185     syncSize : function(){
16186         delete this.lastSize;
16187         this.setSize(this.el.getWidth(), this.el.getHeight());
16188         return this;
16189     },
16190
16191     /**
16192      * Called after the component is resized, this method is empty by default but can be implemented by any
16193      * subclass that needs to perform custom logic after a resize occurs.
16194      * @param {Number} adjWidth The box-adjusted width that was set
16195      * @param {Number} adjHeight The box-adjusted height that was set
16196      * @param {Number} rawWidth The width that was originally specified
16197      * @param {Number} rawHeight The height that was originally specified
16198      */
16199     onResize : function(adjWidth, adjHeight, rawWidth, rawHeight){
16200
16201     },
16202
16203     /**
16204      * Called after the component is moved, this method is empty by default but can be implemented by any
16205      * subclass that needs to perform custom logic after a move occurs.
16206      * @param {Number} x The new x position
16207      * @param {Number} y The new y position
16208      */
16209     onPosition : function(x, y){
16210
16211     },
16212
16213     // private
16214     adjustSize : function(w, h){
16215         if(this.autoWidth){
16216             w = 'auto';
16217         }
16218         if(this.autoHeight){
16219             h = 'auto';
16220         }
16221         return {width : w, height: h};
16222     },
16223
16224     // private
16225     adjustPosition : function(x, y){
16226         return {x : x, y: y};
16227     }
16228 });/*
16229  * Based on:
16230  * Ext JS Library 1.1.1
16231  * Copyright(c) 2006-2007, Ext JS, LLC.
16232  *
16233  * Originally Released Under LGPL - original licence link has changed is not relivant.
16234  *
16235  * Fork - LGPL
16236  * <script type="text/javascript">
16237  */
16238  (function(){ 
16239 /**
16240  * @class Roo.Layer
16241  * @extends Roo.Element
16242  * An extended {@link Roo.Element} object that supports a shadow and shim, constrain to viewport and
16243  * automatic maintaining of shadow/shim positions.
16244  * @cfg {Boolean} shim False to disable the iframe shim in browsers which need one (defaults to true)
16245  * @cfg {String/Boolean} shadow True to create a shadow element with default class "x-layer-shadow", or
16246  * you can pass a string with a CSS class name. False turns off the shadow.
16247  * @cfg {Object} dh DomHelper object config to create element with (defaults to {tag: "div", cls: "x-layer"}).
16248  * @cfg {Boolean} constrain False to disable constrain to viewport (defaults to true)
16249  * @cfg {String} cls CSS class to add to the element
16250  * @cfg {Number} zindex Starting z-index (defaults to 11000)
16251  * @cfg {Number} shadowOffset Number of pixels to offset the shadow (defaults to 3)
16252  * @constructor
16253  * @param {Object} config An object with config options.
16254  * @param {String/HTMLElement} existingEl (optional) Uses an existing DOM element. If the element is not found it creates it.
16255  */
16256
16257 Roo.Layer = function(config, existingEl){
16258     config = config || {};
16259     var dh = Roo.DomHelper;
16260     var cp = config.parentEl, pel = cp ? Roo.getDom(cp) : document.body;
16261     if(existingEl){
16262         this.dom = Roo.getDom(existingEl);
16263     }
16264     if(!this.dom){
16265         var o = config.dh || {tag: "div", cls: "x-layer"};
16266         this.dom = dh.append(pel, o);
16267     }
16268     if(config.cls){
16269         this.addClass(config.cls);
16270     }
16271     this.constrain = config.constrain !== false;
16272     this.visibilityMode = Roo.Element.VISIBILITY;
16273     if(config.id){
16274         this.id = this.dom.id = config.id;
16275     }else{
16276         this.id = Roo.id(this.dom);
16277     }
16278     this.zindex = config.zindex || this.getZIndex();
16279     this.position("absolute", this.zindex);
16280     if(config.shadow){
16281         this.shadowOffset = config.shadowOffset || 4;
16282         this.shadow = new Roo.Shadow({
16283             offset : this.shadowOffset,
16284             mode : config.shadow
16285         });
16286     }else{
16287         this.shadowOffset = 0;
16288     }
16289     this.useShim = config.shim !== false && Roo.useShims;
16290     this.useDisplay = config.useDisplay;
16291     this.hide();
16292 };
16293
16294 var supr = Roo.Element.prototype;
16295
16296 // shims are shared among layer to keep from having 100 iframes
16297 var shims = [];
16298
16299 Roo.extend(Roo.Layer, Roo.Element, {
16300
16301     getZIndex : function(){
16302         return this.zindex || parseInt(this.getStyle("z-index"), 10) || 11000;
16303     },
16304
16305     getShim : function(){
16306         if(!this.useShim){
16307             return null;
16308         }
16309         if(this.shim){
16310             return this.shim;
16311         }
16312         var shim = shims.shift();
16313         if(!shim){
16314             shim = this.createShim();
16315             shim.enableDisplayMode('block');
16316             shim.dom.style.display = 'none';
16317             shim.dom.style.visibility = 'visible';
16318         }
16319         var pn = this.dom.parentNode;
16320         if(shim.dom.parentNode != pn){
16321             pn.insertBefore(shim.dom, this.dom);
16322         }
16323         shim.setStyle('z-index', this.getZIndex()-2);
16324         this.shim = shim;
16325         return shim;
16326     },
16327
16328     hideShim : function(){
16329         if(this.shim){
16330             this.shim.setDisplayed(false);
16331             shims.push(this.shim);
16332             delete this.shim;
16333         }
16334     },
16335
16336     disableShadow : function(){
16337         if(this.shadow){
16338             this.shadowDisabled = true;
16339             this.shadow.hide();
16340             this.lastShadowOffset = this.shadowOffset;
16341             this.shadowOffset = 0;
16342         }
16343     },
16344
16345     enableShadow : function(show){
16346         if(this.shadow){
16347             this.shadowDisabled = false;
16348             this.shadowOffset = this.lastShadowOffset;
16349             delete this.lastShadowOffset;
16350             if(show){
16351                 this.sync(true);
16352             }
16353         }
16354     },
16355
16356     // private
16357     // this code can execute repeatedly in milliseconds (i.e. during a drag) so
16358     // code size was sacrificed for effeciency (e.g. no getBox/setBox, no XY calls)
16359     sync : function(doShow){
16360         var sw = this.shadow;
16361         if(!this.updating && this.isVisible() && (sw || this.useShim)){
16362             var sh = this.getShim();
16363
16364             var w = this.getWidth(),
16365                 h = this.getHeight();
16366
16367             var l = this.getLeft(true),
16368                 t = this.getTop(true);
16369
16370             if(sw && !this.shadowDisabled){
16371                 if(doShow && !sw.isVisible()){
16372                     sw.show(this);
16373                 }else{
16374                     sw.realign(l, t, w, h);
16375                 }
16376                 if(sh){
16377                     if(doShow){
16378                        sh.show();
16379                     }
16380                     // fit the shim behind the shadow, so it is shimmed too
16381                     var a = sw.adjusts, s = sh.dom.style;
16382                     s.left = (Math.min(l, l+a.l))+"px";
16383                     s.top = (Math.min(t, t+a.t))+"px";
16384                     s.width = (w+a.w)+"px";
16385                     s.height = (h+a.h)+"px";
16386                 }
16387             }else if(sh){
16388                 if(doShow){
16389                    sh.show();
16390                 }
16391                 sh.setSize(w, h);
16392                 sh.setLeftTop(l, t);
16393             }
16394             
16395         }
16396     },
16397
16398     // private
16399     destroy : function(){
16400         this.hideShim();
16401         if(this.shadow){
16402             this.shadow.hide();
16403         }
16404         this.removeAllListeners();
16405         var pn = this.dom.parentNode;
16406         if(pn){
16407             pn.removeChild(this.dom);
16408         }
16409         Roo.Element.uncache(this.id);
16410     },
16411
16412     remove : function(){
16413         this.destroy();
16414     },
16415
16416     // private
16417     beginUpdate : function(){
16418         this.updating = true;
16419     },
16420
16421     // private
16422     endUpdate : function(){
16423         this.updating = false;
16424         this.sync(true);
16425     },
16426
16427     // private
16428     hideUnders : function(negOffset){
16429         if(this.shadow){
16430             this.shadow.hide();
16431         }
16432         this.hideShim();
16433     },
16434
16435     // private
16436     constrainXY : function(){
16437         if(this.constrain){
16438             var vw = Roo.lib.Dom.getViewWidth(),
16439                 vh = Roo.lib.Dom.getViewHeight();
16440             var s = Roo.get(document).getScroll();
16441
16442             var xy = this.getXY();
16443             var x = xy[0], y = xy[1];   
16444             var w = this.dom.offsetWidth+this.shadowOffset, h = this.dom.offsetHeight+this.shadowOffset;
16445             // only move it if it needs it
16446             var moved = false;
16447             // first validate right/bottom
16448             if((x + w) > vw+s.left){
16449                 x = vw - w - this.shadowOffset;
16450                 moved = true;
16451             }
16452             if((y + h) > vh+s.top){
16453                 y = vh - h - this.shadowOffset;
16454                 moved = true;
16455             }
16456             // then make sure top/left isn't negative
16457             if(x < s.left){
16458                 x = s.left;
16459                 moved = true;
16460             }
16461             if(y < s.top){
16462                 y = s.top;
16463                 moved = true;
16464             }
16465             if(moved){
16466                 if(this.avoidY){
16467                     var ay = this.avoidY;
16468                     if(y <= ay && (y+h) >= ay){
16469                         y = ay-h-5;   
16470                     }
16471                 }
16472                 xy = [x, y];
16473                 this.storeXY(xy);
16474                 supr.setXY.call(this, xy);
16475                 this.sync();
16476             }
16477         }
16478     },
16479
16480     isVisible : function(){
16481         return this.visible;    
16482     },
16483
16484     // private
16485     showAction : function(){
16486         this.visible = true; // track visibility to prevent getStyle calls
16487         if(this.useDisplay === true){
16488             this.setDisplayed("");
16489         }else if(this.lastXY){
16490             supr.setXY.call(this, this.lastXY);
16491         }else if(this.lastLT){
16492             supr.setLeftTop.call(this, this.lastLT[0], this.lastLT[1]);
16493         }
16494     },
16495
16496     // private
16497     hideAction : function(){
16498         this.visible = false;
16499         if(this.useDisplay === true){
16500             this.setDisplayed(false);
16501         }else{
16502             this.setLeftTop(-10000,-10000);
16503         }
16504     },
16505
16506     // overridden Element method
16507     setVisible : function(v, a, d, c, e){
16508         if(v){
16509             this.showAction();
16510         }
16511         if(a && v){
16512             var cb = function(){
16513                 this.sync(true);
16514                 if(c){
16515                     c();
16516                 }
16517             }.createDelegate(this);
16518             supr.setVisible.call(this, true, true, d, cb, e);
16519         }else{
16520             if(!v){
16521                 this.hideUnders(true);
16522             }
16523             var cb = c;
16524             if(a){
16525                 cb = function(){
16526                     this.hideAction();
16527                     if(c){
16528                         c();
16529                     }
16530                 }.createDelegate(this);
16531             }
16532             supr.setVisible.call(this, v, a, d, cb, e);
16533             if(v){
16534                 this.sync(true);
16535             }else if(!a){
16536                 this.hideAction();
16537             }
16538         }
16539     },
16540
16541     storeXY : function(xy){
16542         delete this.lastLT;
16543         this.lastXY = xy;
16544     },
16545
16546     storeLeftTop : function(left, top){
16547         delete this.lastXY;
16548         this.lastLT = [left, top];
16549     },
16550
16551     // private
16552     beforeFx : function(){
16553         this.beforeAction();
16554         return Roo.Layer.superclass.beforeFx.apply(this, arguments);
16555     },
16556
16557     // private
16558     afterFx : function(){
16559         Roo.Layer.superclass.afterFx.apply(this, arguments);
16560         this.sync(this.isVisible());
16561     },
16562
16563     // private
16564     beforeAction : function(){
16565         if(!this.updating && this.shadow){
16566             this.shadow.hide();
16567         }
16568     },
16569
16570     // overridden Element method
16571     setLeft : function(left){
16572         this.storeLeftTop(left, this.getTop(true));
16573         supr.setLeft.apply(this, arguments);
16574         this.sync();
16575     },
16576
16577     setTop : function(top){
16578         this.storeLeftTop(this.getLeft(true), top);
16579         supr.setTop.apply(this, arguments);
16580         this.sync();
16581     },
16582
16583     setLeftTop : function(left, top){
16584         this.storeLeftTop(left, top);
16585         supr.setLeftTop.apply(this, arguments);
16586         this.sync();
16587     },
16588
16589     setXY : function(xy, a, d, c, e){
16590         this.fixDisplay();
16591         this.beforeAction();
16592         this.storeXY(xy);
16593         var cb = this.createCB(c);
16594         supr.setXY.call(this, xy, a, d, cb, e);
16595         if(!a){
16596             cb();
16597         }
16598     },
16599
16600     // private
16601     createCB : function(c){
16602         var el = this;
16603         return function(){
16604             el.constrainXY();
16605             el.sync(true);
16606             if(c){
16607                 c();
16608             }
16609         };
16610     },
16611
16612     // overridden Element method
16613     setX : function(x, a, d, c, e){
16614         this.setXY([x, this.getY()], a, d, c, e);
16615     },
16616
16617     // overridden Element method
16618     setY : function(y, a, d, c, e){
16619         this.setXY([this.getX(), y], a, d, c, e);
16620     },
16621
16622     // overridden Element method
16623     setSize : function(w, h, a, d, c, e){
16624         this.beforeAction();
16625         var cb = this.createCB(c);
16626         supr.setSize.call(this, w, h, a, d, cb, e);
16627         if(!a){
16628             cb();
16629         }
16630     },
16631
16632     // overridden Element method
16633     setWidth : function(w, a, d, c, e){
16634         this.beforeAction();
16635         var cb = this.createCB(c);
16636         supr.setWidth.call(this, w, a, d, cb, e);
16637         if(!a){
16638             cb();
16639         }
16640     },
16641
16642     // overridden Element method
16643     setHeight : function(h, a, d, c, e){
16644         this.beforeAction();
16645         var cb = this.createCB(c);
16646         supr.setHeight.call(this, h, a, d, cb, e);
16647         if(!a){
16648             cb();
16649         }
16650     },
16651
16652     // overridden Element method
16653     setBounds : function(x, y, w, h, a, d, c, e){
16654         this.beforeAction();
16655         var cb = this.createCB(c);
16656         if(!a){
16657             this.storeXY([x, y]);
16658             supr.setXY.call(this, [x, y]);
16659             supr.setSize.call(this, w, h, a, d, cb, e);
16660             cb();
16661         }else{
16662             supr.setBounds.call(this, x, y, w, h, a, d, cb, e);
16663         }
16664         return this;
16665     },
16666     
16667     /**
16668      * Sets the z-index of this layer and adjusts any shadow and shim z-indexes. The layer z-index is automatically
16669      * incremented by two more than the value passed in so that it always shows above any shadow or shim (the shadow
16670      * element, if any, will be assigned z-index + 1, and the shim element, if any, will be assigned the unmodified z-index).
16671      * @param {Number} zindex The new z-index to set
16672      * @return {this} The Layer
16673      */
16674     setZIndex : function(zindex){
16675         this.zindex = zindex;
16676         this.setStyle("z-index", zindex + 2);
16677         if(this.shadow){
16678             this.shadow.setZIndex(zindex + 1);
16679         }
16680         if(this.shim){
16681             this.shim.setStyle("z-index", zindex);
16682         }
16683     }
16684 });
16685 })();/*
16686  * Original code for Roojs - LGPL
16687  * <script type="text/javascript">
16688  */
16689  
16690 /**
16691  * @class Roo.XComponent
16692  * A delayed Element creator...
16693  * Or a way to group chunks of interface together.
16694  * technically this is a wrapper around a tree of Roo elements (which defines a 'module'),
16695  *  used in conjunction with XComponent.build() it will create an instance of each element,
16696  *  then call addxtype() to build the User interface.
16697  * 
16698  * Mypart.xyx = new Roo.XComponent({
16699
16700     parent : 'Mypart.xyz', // empty == document.element.!!
16701     order : '001',
16702     name : 'xxxx'
16703     region : 'xxxx'
16704     disabled : function() {} 
16705      
16706     tree : function() { // return an tree of xtype declared components
16707         var MODULE = this;
16708         return 
16709         {
16710             xtype : 'NestedLayoutPanel',
16711             // technicall
16712         }
16713      ]
16714  *})
16715  *
16716  *
16717  * It can be used to build a big heiracy, with parent etc.
16718  * or you can just use this to render a single compoent to a dom element
16719  * MYPART.render(Roo.Element | String(id) | dom_element )
16720  *
16721  *
16722  * Usage patterns.
16723  *
16724  * Classic Roo
16725  *
16726  * Roo is designed primarily as a single page application, so the UI build for a standard interface will
16727  * expect a single 'TOP' level module normally indicated by the 'parent' of the XComponent definition being defined as false.
16728  *
16729  * Each sub module is expected to have a parent pointing to the class name of it's parent module.
16730  *
16731  * When the top level is false, a 'Roo.BorderLayout' is created and the element is flagged as 'topModule'
16732  * - if mulitple topModules exist, the last one is defined as the top module.
16733  *
16734  * Embeded Roo
16735  * 
16736  * When the top level or multiple modules are to embedded into a existing HTML page,
16737  * the parent element can container '#id' of the element where the module will be drawn.
16738  *
16739  * Bootstrap Roo
16740  *
16741  * Unlike classic Roo, the bootstrap tends not to be used as a single page.
16742  * it relies more on a include mechanism, where sub modules are included into an outer page.
16743  * This is normally managed by the builder tools using Roo.apply( options, Included.Sub.Module )
16744  * 
16745  * Bootstrap Roo Included elements
16746  *
16747  * Our builder application needs the ability to preview these sub compoennts. They will normally have parent=false set,
16748  * hence confusing the component builder as it thinks there are multiple top level elements. 
16749  *
16750  * String Over-ride & Translations
16751  *
16752  * Our builder application writes all the strings as _strings and _named_strings. This is to enable the translation of elements,
16753  * and also the 'overlaying of string values - needed when different versions of the same application with different text content
16754  * are needed. @see Roo.XComponent.overlayString  
16755  * 
16756  * 
16757  * 
16758  * @extends Roo.util.Observable
16759  * @constructor
16760  * @param cfg {Object} configuration of component
16761  * 
16762  */
16763 Roo.XComponent = function(cfg) {
16764     Roo.apply(this, cfg);
16765     this.addEvents({ 
16766         /**
16767              * @event built
16768              * Fires when this the componnt is built
16769              * @param {Roo.XComponent} c the component
16770              */
16771         'built' : true
16772         
16773     });
16774     this.region = this.region || 'center'; // default..
16775     Roo.XComponent.register(this);
16776     this.modules = false;
16777     this.el = false; // where the layout goes..
16778     
16779     
16780 }
16781 Roo.extend(Roo.XComponent, Roo.util.Observable, {
16782     /**
16783      * @property el
16784      * The created element (with Roo.factory())
16785      * @type {Roo.Layout}
16786      */
16787     el  : false,
16788     
16789     /**
16790      * @property el
16791      * for BC  - use el in new code
16792      * @type {Roo.Layout}
16793      */
16794     panel : false,
16795     
16796     /**
16797      * @property layout
16798      * for BC  - use el in new code
16799      * @type {Roo.Layout}
16800      */
16801     layout : false,
16802     
16803      /**
16804      * @cfg {Function|boolean} disabled
16805      * If this module is disabled by some rule, return true from the funtion
16806      */
16807     disabled : false,
16808     
16809     /**
16810      * @cfg {String} parent 
16811      * Name of parent element which it get xtype added to..
16812      */
16813     parent: false,
16814     
16815     /**
16816      * @cfg {String} order
16817      * Used to set the order in which elements are created (usefull for multiple tabs)
16818      */
16819     
16820     order : false,
16821     /**
16822      * @cfg {String} name
16823      * String to display while loading.
16824      */
16825     name : false,
16826     /**
16827      * @cfg {String} region
16828      * Region to render component to (defaults to center)
16829      */
16830     region : 'center',
16831     
16832     /**
16833      * @cfg {Array} items
16834      * A single item array - the first element is the root of the tree..
16835      * It's done this way to stay compatible with the Xtype system...
16836      */
16837     items : false,
16838     
16839     /**
16840      * @property _tree
16841      * The method that retuns the tree of parts that make up this compoennt 
16842      * @type {function}
16843      */
16844     _tree  : false,
16845     
16846      /**
16847      * render
16848      * render element to dom or tree
16849      * @param {Roo.Element|String|DomElement} optional render to if parent is not set.
16850      */
16851     
16852     render : function(el)
16853     {
16854         
16855         el = el || false;
16856         var hp = this.parent ? 1 : 0;
16857         Roo.debug &&  Roo.log(this);
16858         
16859         var tree = this._tree ? this._tree() : this.tree();
16860
16861         
16862         if (!el && typeof(this.parent) == 'string' && this.parent.substring(0,1) == '#') {
16863             // if parent is a '#.....' string, then let's use that..
16864             var ename = this.parent.substr(1);
16865             this.parent = false;
16866             Roo.debug && Roo.log(ename);
16867             switch (ename) {
16868                 case 'bootstrap-body':
16869                     if (typeof(tree.el) != 'undefined' && tree.el == document.body)  {
16870                         // this is the BorderLayout standard?
16871                        this.parent = { el : true };
16872                        break;
16873                     }
16874                     if (["Nest", "Content", "Grid", "Tree"].indexOf(tree.xtype)  > -1)  {
16875                         // need to insert stuff...
16876                         this.parent =  {
16877                              el : new Roo.bootstrap.layout.Border({
16878                                  el : document.body, 
16879                      
16880                                  center: {
16881                                     titlebar: false,
16882                                     autoScroll:false,
16883                                     closeOnTab: true,
16884                                     tabPosition: 'top',
16885                                       //resizeTabs: true,
16886                                     alwaysShowTabs: true,
16887                                     hideTabs: false
16888                                      //minTabWidth: 140
16889                                  }
16890                              })
16891                         
16892                          };
16893                          break;
16894                     }
16895                          
16896                     if (typeof(Roo.bootstrap.Body) != 'undefined' ) {
16897                         this.parent = { el :  new  Roo.bootstrap.Body() };
16898                         Roo.debug && Roo.log("setting el to doc body");
16899                          
16900                     } else {
16901                         throw "Container is bootstrap body, but Roo.bootstrap.Body is not defined";
16902                     }
16903                     break;
16904                 case 'bootstrap':
16905                     this.parent = { el : true};
16906                     // fall through
16907                 default:
16908                     el = Roo.get(ename);
16909                     if (typeof(Roo.bootstrap) != 'undefined' && tree['|xns'] == 'Roo.bootstrap') {
16910                         this.parent = { el : true};
16911                     }
16912                     
16913                     break;
16914             }
16915                 
16916             
16917             if (!el && !this.parent) {
16918                 Roo.debug && Roo.log("Warning - element can not be found :#" + ename );
16919                 return;
16920             }
16921         }
16922         
16923         Roo.debug && Roo.log("EL:");
16924         Roo.debug && Roo.log(el);
16925         Roo.debug && Roo.log("this.parent.el:");
16926         Roo.debug && Roo.log(this.parent.el);
16927         
16928
16929         // altertive root elements ??? - we need a better way to indicate these.
16930         var is_alt = Roo.XComponent.is_alt ||
16931                     (typeof(tree.el) != 'undefined' && tree.el == document.body) ||
16932                     (typeof(Roo.bootstrap) != 'undefined' && tree.xns == Roo.bootstrap) ||
16933                     (typeof(Roo.mailer) != 'undefined' && tree.xns == Roo.mailer) ;
16934         
16935         
16936         
16937         if (!this.parent && is_alt) {
16938             //el = Roo.get(document.body);
16939             this.parent = { el : true };
16940         }
16941             
16942             
16943         
16944         if (!this.parent) {
16945             
16946             Roo.debug && Roo.log("no parent - creating one");
16947             
16948             el = el ? Roo.get(el) : false;      
16949             
16950             if (typeof(Roo.BorderLayout) == 'undefined' ) {
16951                 
16952                 this.parent =  {
16953                     el : new Roo.bootstrap.layout.Border({
16954                         el: el || document.body,
16955                     
16956                         center: {
16957                             titlebar: false,
16958                             autoScroll:false,
16959                             closeOnTab: true,
16960                             tabPosition: 'top',
16961                              //resizeTabs: true,
16962                             alwaysShowTabs: false,
16963                             hideTabs: true,
16964                             minTabWidth: 140,
16965                             overflow: 'visible'
16966                          }
16967                      })
16968                 };
16969             } else {
16970             
16971                 // it's a top level one..
16972                 this.parent =  {
16973                     el : new Roo.BorderLayout(el || document.body, {
16974                         center: {
16975                             titlebar: false,
16976                             autoScroll:false,
16977                             closeOnTab: true,
16978                             tabPosition: 'top',
16979                              //resizeTabs: true,
16980                             alwaysShowTabs: el && hp? false :  true,
16981                             hideTabs: el || !hp ? true :  false,
16982                             minTabWidth: 140
16983                          }
16984                     })
16985                 };
16986             }
16987         }
16988         
16989         if (!this.parent.el) {
16990                 // probably an old style ctor, which has been disabled.
16991                 return;
16992
16993         }
16994                 // The 'tree' method is  '_tree now' 
16995             
16996         tree.region = tree.region || this.region;
16997         var is_body = false;
16998         if (this.parent.el === true) {
16999             // bootstrap... - body..
17000             if (el) {
17001                 tree.el = el;
17002             }
17003             this.parent.el = Roo.factory(tree);
17004             is_body = true;
17005         }
17006         
17007         this.el = this.parent.el.addxtype(tree, undefined, is_body);
17008         this.fireEvent('built', this);
17009         
17010         this.panel = this.el;
17011         this.layout = this.panel.layout;
17012         this.parentLayout = this.parent.layout  || false;  
17013          
17014     }
17015     
17016 });
17017
17018 Roo.apply(Roo.XComponent, {
17019     /**
17020      * @property  hideProgress
17021      * true to disable the building progress bar.. usefull on single page renders.
17022      * @type Boolean
17023      */
17024     hideProgress : false,
17025     /**
17026      * @property  buildCompleted
17027      * True when the builder has completed building the interface.
17028      * @type Boolean
17029      */
17030     buildCompleted : false,
17031      
17032     /**
17033      * @property  topModule
17034      * the upper most module - uses document.element as it's constructor.
17035      * @type Object
17036      */
17037      
17038     topModule  : false,
17039       
17040     /**
17041      * @property  modules
17042      * array of modules to be created by registration system.
17043      * @type {Array} of Roo.XComponent
17044      */
17045     
17046     modules : [],
17047     /**
17048      * @property  elmodules
17049      * array of modules to be created by which use #ID 
17050      * @type {Array} of Roo.XComponent
17051      */
17052      
17053     elmodules : [],
17054
17055      /**
17056      * @property  is_alt
17057      * Is an alternative Root - normally used by bootstrap or other systems,
17058      *    where the top element in the tree can wrap 'body' 
17059      * @type {boolean}  (default false)
17060      */
17061      
17062     is_alt : false,
17063     /**
17064      * @property  build_from_html
17065      * Build elements from html - used by bootstrap HTML stuff 
17066      *    - this is cleared after build is completed
17067      * @type {boolean}    (default false)
17068      */
17069      
17070     build_from_html : false,
17071     /**
17072      * Register components to be built later.
17073      *
17074      * This solves the following issues
17075      * - Building is not done on page load, but after an authentication process has occured.
17076      * - Interface elements are registered on page load
17077      * - Parent Interface elements may not be loaded before child, so this handles that..
17078      * 
17079      *
17080      * example:
17081      * 
17082      * MyApp.register({
17083           order : '000001',
17084           module : 'Pman.Tab.projectMgr',
17085           region : 'center',
17086           parent : 'Pman.layout',
17087           disabled : false,  // or use a function..
17088         })
17089      
17090      * * @param {Object} details about module
17091      */
17092     register : function(obj) {
17093                 
17094         Roo.XComponent.event.fireEvent('register', obj);
17095         switch(typeof(obj.disabled) ) {
17096                 
17097             case 'undefined':
17098                 break;
17099             
17100             case 'function':
17101                 if ( obj.disabled() ) {
17102                         return;
17103                 }
17104                 break;
17105             
17106             default:
17107                 if (obj.disabled || obj.region == '#disabled') {
17108                         return;
17109                 }
17110                 break;
17111         }
17112                 
17113         this.modules.push(obj);
17114          
17115     },
17116     /**
17117      * convert a string to an object..
17118      * eg. 'AAA.BBB' -> finds AAA.BBB
17119
17120      */
17121     
17122     toObject : function(str)
17123     {
17124         if (!str || typeof(str) == 'object') {
17125             return str;
17126         }
17127         if (str.substring(0,1) == '#') {
17128             return str;
17129         }
17130
17131         var ar = str.split('.');
17132         var rt, o;
17133         rt = ar.shift();
17134             /** eval:var:o */
17135         try {
17136             eval('if (typeof ' + rt + ' == "undefined"){ o = false;} o = ' + rt + ';');
17137         } catch (e) {
17138             throw "Module not found : " + str;
17139         }
17140         
17141         if (o === false) {
17142             throw "Module not found : " + str;
17143         }
17144         Roo.each(ar, function(e) {
17145             if (typeof(o[e]) == 'undefined') {
17146                 throw "Module not found : " + str;
17147             }
17148             o = o[e];
17149         });
17150         
17151         return o;
17152         
17153     },
17154     
17155     
17156     /**
17157      * move modules into their correct place in the tree..
17158      * 
17159      */
17160     preBuild : function ()
17161     {
17162         var _t = this;
17163         Roo.each(this.modules , function (obj)
17164         {
17165             Roo.XComponent.event.fireEvent('beforebuild', obj);
17166             
17167             var opar = obj.parent;
17168             try { 
17169                 obj.parent = this.toObject(opar);
17170             } catch(e) {
17171                 Roo.debug && Roo.log("parent:toObject failed: " + e.toString());
17172                 return;
17173             }
17174             
17175             if (!obj.parent) {
17176                 Roo.debug && Roo.log("GOT top level module");
17177                 Roo.debug && Roo.log(obj);
17178                 obj.modules = new Roo.util.MixedCollection(false, 
17179                     function(o) { return o.order + '' }
17180                 );
17181                 this.topModule = obj;
17182                 return;
17183             }
17184                         // parent is a string (usually a dom element name..)
17185             if (typeof(obj.parent) == 'string') {
17186                 this.elmodules.push(obj);
17187                 return;
17188             }
17189             if (obj.parent.constructor != Roo.XComponent) {
17190                 Roo.debug && Roo.log("Warning : Object Parent is not instance of XComponent:" + obj.name)
17191             }
17192             if (!obj.parent.modules) {
17193                 obj.parent.modules = new Roo.util.MixedCollection(false, 
17194                     function(o) { return o.order + '' }
17195                 );
17196             }
17197             if (obj.parent.disabled) {
17198                 obj.disabled = true;
17199             }
17200             obj.parent.modules.add(obj);
17201         }, this);
17202     },
17203     
17204      /**
17205      * make a list of modules to build.
17206      * @return {Array} list of modules. 
17207      */ 
17208     
17209     buildOrder : function()
17210     {
17211         var _this = this;
17212         var cmp = function(a,b) {   
17213             return String(a).toUpperCase() > String(b).toUpperCase() ? 1 : -1;
17214         };
17215         if ((!this.topModule || !this.topModule.modules) && !this.elmodules.length) {
17216             throw "No top level modules to build";
17217         }
17218         
17219         // make a flat list in order of modules to build.
17220         var mods = this.topModule ? [ this.topModule ] : [];
17221                 
17222         
17223         // elmodules (is a list of DOM based modules )
17224         Roo.each(this.elmodules, function(e) {
17225             mods.push(e);
17226             if (!this.topModule &&
17227                 typeof(e.parent) == 'string' &&
17228                 e.parent.substring(0,1) == '#' &&
17229                 Roo.get(e.parent.substr(1))
17230                ) {
17231                 
17232                 _this.topModule = e;
17233             }
17234             
17235         });
17236
17237         
17238         // add modules to their parents..
17239         var addMod = function(m) {
17240             Roo.debug && Roo.log("build Order: add: " + m.name);
17241                 
17242             mods.push(m);
17243             if (m.modules && !m.disabled) {
17244                 Roo.debug && Roo.log("build Order: " + m.modules.length + " child modules");
17245                 m.modules.keySort('ASC',  cmp );
17246                 Roo.debug && Roo.log("build Order: " + m.modules.length + " child modules (after sort)");
17247     
17248                 m.modules.each(addMod);
17249             } else {
17250                 Roo.debug && Roo.log("build Order: no child modules");
17251             }
17252             // not sure if this is used any more..
17253             if (m.finalize) {
17254                 m.finalize.name = m.name + " (clean up) ";
17255                 mods.push(m.finalize);
17256             }
17257             
17258         }
17259         if (this.topModule && this.topModule.modules) { 
17260             this.topModule.modules.keySort('ASC',  cmp );
17261             this.topModule.modules.each(addMod);
17262         } 
17263         return mods;
17264     },
17265     
17266      /**
17267      * Build the registered modules.
17268      * @param {Object} parent element.
17269      * @param {Function} optional method to call after module has been added.
17270      * 
17271      */ 
17272    
17273     build : function(opts) 
17274     {
17275         
17276         if (typeof(opts) != 'undefined') {
17277             Roo.apply(this,opts);
17278         }
17279         
17280         this.preBuild();
17281         var mods = this.buildOrder();
17282       
17283         //this.allmods = mods;
17284         //Roo.debug && Roo.log(mods);
17285         //return;
17286         if (!mods.length) { // should not happen
17287             throw "NO modules!!!";
17288         }
17289         
17290         
17291         var msg = "Building Interface...";
17292         // flash it up as modal - so we store the mask!?
17293         if (!this.hideProgress && Roo.MessageBox) {
17294             Roo.MessageBox.show({ title: 'loading' });
17295             Roo.MessageBox.show({
17296                title: "Please wait...",
17297                msg: msg,
17298                width:450,
17299                progress:true,
17300                buttons : false,
17301                closable:false,
17302                modal: false
17303               
17304             });
17305         }
17306         var total = mods.length;
17307         
17308         var _this = this;
17309         var progressRun = function() {
17310             if (!mods.length) {
17311                 Roo.debug && Roo.log('hide?');
17312                 if (!this.hideProgress && Roo.MessageBox) {
17313                     Roo.MessageBox.hide();
17314                 }
17315                 Roo.XComponent.build_from_html = false; // reset, so dialogs will be build from javascript
17316                 
17317                 Roo.XComponent.event.fireEvent('buildcomplete', _this.topModule);
17318                 
17319                 // THE END...
17320                 return false;   
17321             }
17322             
17323             var m = mods.shift();
17324             
17325             
17326             Roo.debug && Roo.log(m);
17327             // not sure if this is supported any more.. - modules that are are just function
17328             if (typeof(m) == 'function') { 
17329                 m.call(this);
17330                 return progressRun.defer(10, _this);
17331             } 
17332             
17333             
17334             msg = "Building Interface " + (total  - mods.length) + 
17335                     " of " + total + 
17336                     (m.name ? (' - ' + m.name) : '');
17337                         Roo.debug && Roo.log(msg);
17338             if (!_this.hideProgress &&  Roo.MessageBox) { 
17339                 Roo.MessageBox.updateProgress(  (total  - mods.length)/total, msg  );
17340             }
17341             
17342          
17343             // is the module disabled?
17344             var disabled = (typeof(m.disabled) == 'function') ?
17345                 m.disabled.call(m.module.disabled) : m.disabled;    
17346             
17347             
17348             if (disabled) {
17349                 return progressRun(); // we do not update the display!
17350             }
17351             
17352             // now build 
17353             
17354                         
17355                         
17356             m.render();
17357             // it's 10 on top level, and 1 on others??? why...
17358             return progressRun.defer(10, _this);
17359              
17360         }
17361         progressRun.defer(1, _this);
17362      
17363         
17364         
17365     },
17366     /**
17367      * Overlay a set of modified strings onto a component
17368      * This is dependant on our builder exporting the strings and 'named strings' elements.
17369      * 
17370      * @param {Object} element to overlay on - eg. Pman.Dialog.Login
17371      * @param {Object} associative array of 'named' string and it's new value.
17372      * 
17373      */
17374         overlayStrings : function( component, strings )
17375     {
17376         if (typeof(component['_named_strings']) == 'undefined') {
17377             throw "ERROR: component does not have _named_strings";
17378         }
17379         for ( var k in strings ) {
17380             var md = typeof(component['_named_strings'][k]) == 'undefined' ? false : component['_named_strings'][k];
17381             if (md !== false) {
17382                 component['_strings'][md] = strings[k];
17383             } else {
17384                 Roo.log('could not find named string: ' + k + ' in');
17385                 Roo.log(component);
17386             }
17387             
17388         }
17389         
17390     },
17391     
17392         
17393         /**
17394          * Event Object.
17395          *
17396          *
17397          */
17398         event: false, 
17399     /**
17400          * wrapper for event.on - aliased later..  
17401          * Typically use to register a event handler for register:
17402          *
17403          * eg. Roo.XComponent.on('register', function(comp) { comp.disable = true } );
17404          *
17405          */
17406     on : false
17407    
17408     
17409     
17410 });
17411
17412 Roo.XComponent.event = new Roo.util.Observable({
17413                 events : { 
17414                         /**
17415                          * @event register
17416                          * Fires when an Component is registered,
17417                          * set the disable property on the Component to stop registration.
17418                          * @param {Roo.XComponent} c the component being registerd.
17419                          * 
17420                          */
17421                         'register' : true,
17422             /**
17423                          * @event beforebuild
17424                          * Fires before each Component is built
17425                          * can be used to apply permissions.
17426                          * @param {Roo.XComponent} c the component being registerd.
17427                          * 
17428                          */
17429                         'beforebuild' : true,
17430                         /**
17431                          * @event buildcomplete
17432                          * Fires on the top level element when all elements have been built
17433                          * @param {Roo.XComponent} the top level component.
17434                          */
17435                         'buildcomplete' : true
17436                         
17437                 }
17438 });
17439
17440 Roo.XComponent.on = Roo.XComponent.event.on.createDelegate(Roo.XComponent.event); 
17441  //
17442  /**
17443  * marked - a markdown parser
17444  * Copyright (c) 2011-2014, Christopher Jeffrey. (MIT Licensed)
17445  * https://github.com/chjj/marked
17446  */
17447
17448
17449 /**
17450  *
17451  * Roo.Markdown - is a very crude wrapper around marked..
17452  *
17453  * usage:
17454  * 
17455  * alert( Roo.Markdown.toHtml("Markdown *rocks*.") );
17456  * 
17457  * Note: move the sample code to the bottom of this
17458  * file before uncommenting it.
17459  *
17460  */
17461
17462 Roo.Markdown = {};
17463 Roo.Markdown.toHtml = function(text) {
17464     
17465     var c = new Roo.Markdown.marked.setOptions({
17466             renderer: new Roo.Markdown.marked.Renderer(),
17467             gfm: true,
17468             tables: true,
17469             breaks: false,
17470             pedantic: false,
17471             sanitize: false,
17472             smartLists: true,
17473             smartypants: false
17474           });
17475     // A FEW HACKS!!?
17476     
17477     text = text.replace(/\\\n/g,' ');
17478     return Roo.Markdown.marked(text);
17479 };
17480 //
17481 // converter
17482 //
17483 // Wraps all "globals" so that the only thing
17484 // exposed is makeHtml().
17485 //
17486 (function() {
17487     
17488      /**
17489          * eval:var:escape
17490          * eval:var:unescape
17491          * eval:var:replace
17492          */
17493       
17494     /**
17495      * Helpers
17496      */
17497     
17498     var escape = function (html, encode) {
17499       return html
17500         .replace(!encode ? /&(?!#?\w+;)/g : /&/g, '&amp;')
17501         .replace(/</g, '&lt;')
17502         .replace(/>/g, '&gt;')
17503         .replace(/"/g, '&quot;')
17504         .replace(/'/g, '&#39;');
17505     }
17506     
17507     var unescape = function (html) {
17508         // explicitly match decimal, hex, and named HTML entities 
17509       return html.replace(/&(#(?:\d+)|(?:#x[0-9A-Fa-f]+)|(?:\w+));?/g, function(_, n) {
17510         n = n.toLowerCase();
17511         if (n === 'colon') { return ':'; }
17512         if (n.charAt(0) === '#') {
17513           return n.charAt(1) === 'x'
17514             ? String.fromCharCode(parseInt(n.substring(2), 16))
17515             : String.fromCharCode(+n.substring(1));
17516         }
17517         return '';
17518       });
17519     }
17520     
17521     var replace = function (regex, opt) {
17522       regex = regex.source;
17523       opt = opt || '';
17524       return function self(name, val) {
17525         if (!name) { return new RegExp(regex, opt); }
17526         val = val.source || val;
17527         val = val.replace(/(^|[^\[])\^/g, '$1');
17528         regex = regex.replace(name, val);
17529         return self;
17530       };
17531     }
17532
17533
17534          /**
17535          * eval:var:noop
17536     */
17537     var noop = function () {}
17538     noop.exec = noop;
17539     
17540          /**
17541          * eval:var:merge
17542     */
17543     var merge = function (obj) {
17544       var i = 1
17545         , target
17546         , key;
17547     
17548       for (; i < arguments.length; i++) {
17549         target = arguments[i];
17550         for (key in target) {
17551           if (Object.prototype.hasOwnProperty.call(target, key)) {
17552             obj[key] = target[key];
17553           }
17554         }
17555       }
17556     
17557       return obj;
17558     }
17559     
17560     
17561     /**
17562      * Block-Level Grammar
17563      */
17564     
17565     
17566     
17567     
17568     var block = {
17569       newline: /^\n+/,
17570       code: /^( {4}[^\n]+\n*)+/,
17571       fences: noop,
17572       hr: /^( *[-*_]){3,} *(?:\n+|$)/,
17573       heading: /^ *(#{1,6}) *([^\n]+?) *#* *(?:\n+|$)/,
17574       nptable: noop,
17575       lheading: /^([^\n]+)\n *(=|-){2,} *(?:\n+|$)/,
17576       blockquote: /^( *>[^\n]+(\n(?!def)[^\n]+)*\n*)+/,
17577       list: /^( *)(bull) [\s\S]+?(?:hr|def|\n{2,}(?! )(?!\1bull )\n*|\s*$)/,
17578       html: /^ *(?:comment *(?:\n|\s*$)|closed *(?:\n{2,}|\s*$)|closing *(?:\n{2,}|\s*$))/,
17579       def: /^ *\[([^\]]+)\]: *<?([^\s>]+)>?(?: +["(]([^\n]+)[")])? *(?:\n+|$)/,
17580       table: noop,
17581       paragraph: /^((?:[^\n]+\n?(?!hr|heading|lheading|blockquote|tag|def))+)\n*/,
17582       text: /^[^\n]+/
17583     };
17584     
17585     block.bullet = /(?:[*+-]|\d+\.)/;
17586     block.item = /^( *)(bull) [^\n]*(?:\n(?!\1bull )[^\n]*)*/;
17587     block.item = replace(block.item, 'gm')
17588       (/bull/g, block.bullet)
17589       ();
17590     
17591     block.list = replace(block.list)
17592       (/bull/g, block.bullet)
17593       ('hr', '\\n+(?=\\1?(?:[-*_] *){3,}(?:\\n+|$))')
17594       ('def', '\\n+(?=' + block.def.source + ')')
17595       ();
17596     
17597     block.blockquote = replace(block.blockquote)
17598       ('def', block.def)
17599       ();
17600     
17601     block._tag = '(?!(?:'
17602       + 'a|em|strong|small|s|cite|q|dfn|abbr|data|time|code'
17603       + '|var|samp|kbd|sub|sup|i|b|u|mark|ruby|rt|rp|bdi|bdo'
17604       + '|span|br|wbr|ins|del|img)\\b)\\w+(?!:/|[^\\w\\s@]*@)\\b';
17605     
17606     block.html = replace(block.html)
17607       ('comment', /<!--[\s\S]*?-->/)
17608       ('closed', /<(tag)[\s\S]+?<\/\1>/)
17609       ('closing', /<tag(?:"[^"]*"|'[^']*'|[^'">])*?>/)
17610       (/tag/g, block._tag)
17611       ();
17612     
17613     block.paragraph = replace(block.paragraph)
17614       ('hr', block.hr)
17615       ('heading', block.heading)
17616       ('lheading', block.lheading)
17617       ('blockquote', block.blockquote)
17618       ('tag', '<' + block._tag)
17619       ('def', block.def)
17620       ();
17621     
17622     /**
17623      * Normal Block Grammar
17624      */
17625     
17626     block.normal = merge({}, block);
17627     
17628     /**
17629      * GFM Block Grammar
17630      */
17631     
17632     block.gfm = merge({}, block.normal, {
17633       fences: /^ *(`{3,}|~{3,})[ \.]*(\S+)? *\n([\s\S]*?)\s*\1 *(?:\n+|$)/,
17634       paragraph: /^/,
17635       heading: /^ *(#{1,6}) +([^\n]+?) *#* *(?:\n+|$)/
17636     });
17637     
17638     block.gfm.paragraph = replace(block.paragraph)
17639       ('(?!', '(?!'
17640         + block.gfm.fences.source.replace('\\1', '\\2') + '|'
17641         + block.list.source.replace('\\1', '\\3') + '|')
17642       ();
17643     
17644     /**
17645      * GFM + Tables Block Grammar
17646      */
17647     
17648     block.tables = merge({}, block.gfm, {
17649       nptable: /^ *(\S.*\|.*)\n *([-:]+ *\|[-| :]*)\n((?:.*\|.*(?:\n|$))*)\n*/,
17650       table: /^ *\|(.+)\n *\|( *[-:]+[-| :]*)\n((?: *\|.*(?:\n|$))*)\n*/
17651     });
17652     
17653     /**
17654      * Block Lexer
17655      */
17656     
17657     var Lexer = function (options) {
17658       this.tokens = [];
17659       this.tokens.links = {};
17660       this.options = options || marked.defaults;
17661       this.rules = block.normal;
17662     
17663       if (this.options.gfm) {
17664         if (this.options.tables) {
17665           this.rules = block.tables;
17666         } else {
17667           this.rules = block.gfm;
17668         }
17669       }
17670     }
17671     
17672     /**
17673      * Expose Block Rules
17674      */
17675     
17676     Lexer.rules = block;
17677     
17678     /**
17679      * Static Lex Method
17680      */
17681     
17682     Lexer.lex = function(src, options) {
17683       var lexer = new Lexer(options);
17684       return lexer.lex(src);
17685     };
17686     
17687     /**
17688      * Preprocessing
17689      */
17690     
17691     Lexer.prototype.lex = function(src) {
17692       src = src
17693         .replace(/\r\n|\r/g, '\n')
17694         .replace(/\t/g, '    ')
17695         .replace(/\u00a0/g, ' ')
17696         .replace(/\u2424/g, '\n');
17697     
17698       return this.token(src, true);
17699     };
17700     
17701     /**
17702      * Lexing
17703      */
17704     
17705     Lexer.prototype.token = function(src, top, bq) {
17706       var src = src.replace(/^ +$/gm, '')
17707         , next
17708         , loose
17709         , cap
17710         , bull
17711         , b
17712         , item
17713         , space
17714         , i
17715         , l;
17716     
17717       while (src) {
17718         // newline
17719         if (cap = this.rules.newline.exec(src)) {
17720           src = src.substring(cap[0].length);
17721           if (cap[0].length > 1) {
17722             this.tokens.push({
17723               type: 'space'
17724             });
17725           }
17726         }
17727     
17728         // code
17729         if (cap = this.rules.code.exec(src)) {
17730           src = src.substring(cap[0].length);
17731           cap = cap[0].replace(/^ {4}/gm, '');
17732           this.tokens.push({
17733             type: 'code',
17734             text: !this.options.pedantic
17735               ? cap.replace(/\n+$/, '')
17736               : cap
17737           });
17738           continue;
17739         }
17740     
17741         // fences (gfm)
17742         if (cap = this.rules.fences.exec(src)) {
17743           src = src.substring(cap[0].length);
17744           this.tokens.push({
17745             type: 'code',
17746             lang: cap[2],
17747             text: cap[3] || ''
17748           });
17749           continue;
17750         }
17751     
17752         // heading
17753         if (cap = this.rules.heading.exec(src)) {
17754           src = src.substring(cap[0].length);
17755           this.tokens.push({
17756             type: 'heading',
17757             depth: cap[1].length,
17758             text: cap[2]
17759           });
17760           continue;
17761         }
17762     
17763         // table no leading pipe (gfm)
17764         if (top && (cap = this.rules.nptable.exec(src))) {
17765           src = src.substring(cap[0].length);
17766     
17767           item = {
17768             type: 'table',
17769             header: cap[1].replace(/^ *| *\| *$/g, '').split(/ *\| */),
17770             align: cap[2].replace(/^ *|\| *$/g, '').split(/ *\| */),
17771             cells: cap[3].replace(/\n$/, '').split('\n')
17772           };
17773     
17774           for (i = 0; i < item.align.length; i++) {
17775             if (/^ *-+: *$/.test(item.align[i])) {
17776               item.align[i] = 'right';
17777             } else if (/^ *:-+: *$/.test(item.align[i])) {
17778               item.align[i] = 'center';
17779             } else if (/^ *:-+ *$/.test(item.align[i])) {
17780               item.align[i] = 'left';
17781             } else {
17782               item.align[i] = null;
17783             }
17784           }
17785     
17786           for (i = 0; i < item.cells.length; i++) {
17787             item.cells[i] = item.cells[i].split(/ *\| */);
17788           }
17789     
17790           this.tokens.push(item);
17791     
17792           continue;
17793         }
17794     
17795         // lheading
17796         if (cap = this.rules.lheading.exec(src)) {
17797           src = src.substring(cap[0].length);
17798           this.tokens.push({
17799             type: 'heading',
17800             depth: cap[2] === '=' ? 1 : 2,
17801             text: cap[1]
17802           });
17803           continue;
17804         }
17805     
17806         // hr
17807         if (cap = this.rules.hr.exec(src)) {
17808           src = src.substring(cap[0].length);
17809           this.tokens.push({
17810             type: 'hr'
17811           });
17812           continue;
17813         }
17814     
17815         // blockquote
17816         if (cap = this.rules.blockquote.exec(src)) {
17817           src = src.substring(cap[0].length);
17818     
17819           this.tokens.push({
17820             type: 'blockquote_start'
17821           });
17822     
17823           cap = cap[0].replace(/^ *> ?/gm, '');
17824     
17825           // Pass `top` to keep the current
17826           // "toplevel" state. This is exactly
17827           // how markdown.pl works.
17828           this.token(cap, top, true);
17829     
17830           this.tokens.push({
17831             type: 'blockquote_end'
17832           });
17833     
17834           continue;
17835         }
17836     
17837         // list
17838         if (cap = this.rules.list.exec(src)) {
17839           src = src.substring(cap[0].length);
17840           bull = cap[2];
17841     
17842           this.tokens.push({
17843             type: 'list_start',
17844             ordered: bull.length > 1
17845           });
17846     
17847           // Get each top-level item.
17848           cap = cap[0].match(this.rules.item);
17849     
17850           next = false;
17851           l = cap.length;
17852           i = 0;
17853     
17854           for (; i < l; i++) {
17855             item = cap[i];
17856     
17857             // Remove the list item's bullet
17858             // so it is seen as the next token.
17859             space = item.length;
17860             item = item.replace(/^ *([*+-]|\d+\.) +/, '');
17861     
17862             // Outdent whatever the
17863             // list item contains. Hacky.
17864             if (~item.indexOf('\n ')) {
17865               space -= item.length;
17866               item = !this.options.pedantic
17867                 ? item.replace(new RegExp('^ {1,' + space + '}', 'gm'), '')
17868                 : item.replace(/^ {1,4}/gm, '');
17869             }
17870     
17871             // Determine whether the next list item belongs here.
17872             // Backpedal if it does not belong in this list.
17873             if (this.options.smartLists && i !== l - 1) {
17874               b = block.bullet.exec(cap[i + 1])[0];
17875               if (bull !== b && !(bull.length > 1 && b.length > 1)) {
17876                 src = cap.slice(i + 1).join('\n') + src;
17877                 i = l - 1;
17878               }
17879             }
17880     
17881             // Determine whether item is loose or not.
17882             // Use: /(^|\n)(?! )[^\n]+\n\n(?!\s*$)/
17883             // for discount behavior.
17884             loose = next || /\n\n(?!\s*$)/.test(item);
17885             if (i !== l - 1) {
17886               next = item.charAt(item.length - 1) === '\n';
17887               if (!loose) { loose = next; }
17888             }
17889     
17890             this.tokens.push({
17891               type: loose
17892                 ? 'loose_item_start'
17893                 : 'list_item_start'
17894             });
17895     
17896             // Recurse.
17897             this.token(item, false, bq);
17898     
17899             this.tokens.push({
17900               type: 'list_item_end'
17901             });
17902           }
17903     
17904           this.tokens.push({
17905             type: 'list_end'
17906           });
17907     
17908           continue;
17909         }
17910     
17911         // html
17912         if (cap = this.rules.html.exec(src)) {
17913           src = src.substring(cap[0].length);
17914           this.tokens.push({
17915             type: this.options.sanitize
17916               ? 'paragraph'
17917               : 'html',
17918             pre: !this.options.sanitizer
17919               && (cap[1] === 'pre' || cap[1] === 'script' || cap[1] === 'style'),
17920             text: cap[0]
17921           });
17922           continue;
17923         }
17924     
17925         // def
17926         if ((!bq && top) && (cap = this.rules.def.exec(src))) {
17927           src = src.substring(cap[0].length);
17928           this.tokens.links[cap[1].toLowerCase()] = {
17929             href: cap[2],
17930             title: cap[3]
17931           };
17932           continue;
17933         }
17934     
17935         // table (gfm)
17936         if (top && (cap = this.rules.table.exec(src))) {
17937           src = src.substring(cap[0].length);
17938     
17939           item = {
17940             type: 'table',
17941             header: cap[1].replace(/^ *| *\| *$/g, '').split(/ *\| */),
17942             align: cap[2].replace(/^ *|\| *$/g, '').split(/ *\| */),
17943             cells: cap[3].replace(/(?: *\| *)?\n$/, '').split('\n')
17944           };
17945     
17946           for (i = 0; i < item.align.length; i++) {
17947             if (/^ *-+: *$/.test(item.align[i])) {
17948               item.align[i] = 'right';
17949             } else if (/^ *:-+: *$/.test(item.align[i])) {
17950               item.align[i] = 'center';
17951             } else if (/^ *:-+ *$/.test(item.align[i])) {
17952               item.align[i] = 'left';
17953             } else {
17954               item.align[i] = null;
17955             }
17956           }
17957     
17958           for (i = 0; i < item.cells.length; i++) {
17959             item.cells[i] = item.cells[i]
17960               .replace(/^ *\| *| *\| *$/g, '')
17961               .split(/ *\| */);
17962           }
17963     
17964           this.tokens.push(item);
17965     
17966           continue;
17967         }
17968     
17969         // top-level paragraph
17970         if (top && (cap = this.rules.paragraph.exec(src))) {
17971           src = src.substring(cap[0].length);
17972           this.tokens.push({
17973             type: 'paragraph',
17974             text: cap[1].charAt(cap[1].length - 1) === '\n'
17975               ? cap[1].slice(0, -1)
17976               : cap[1]
17977           });
17978           continue;
17979         }
17980     
17981         // text
17982         if (cap = this.rules.text.exec(src)) {
17983           // Top-level should never reach here.
17984           src = src.substring(cap[0].length);
17985           this.tokens.push({
17986             type: 'text',
17987             text: cap[0]
17988           });
17989           continue;
17990         }
17991     
17992         if (src) {
17993           throw new
17994             Error('Infinite loop on byte: ' + src.charCodeAt(0));
17995         }
17996       }
17997     
17998       return this.tokens;
17999     };
18000     
18001     /**
18002      * Inline-Level Grammar
18003      */
18004     
18005     var inline = {
18006       escape: /^\\([\\`*{}\[\]()#+\-.!_>])/,
18007       autolink: /^<([^ >]+(@|:\/)[^ >]+)>/,
18008       url: noop,
18009       tag: /^<!--[\s\S]*?-->|^<\/?\w+(?:"[^"]*"|'[^']*'|[^'">])*?>/,
18010       link: /^!?\[(inside)\]\(href\)/,
18011       reflink: /^!?\[(inside)\]\s*\[([^\]]*)\]/,
18012       nolink: /^!?\[((?:\[[^\]]*\]|[^\[\]])*)\]/,
18013       strong: /^__([\s\S]+?)__(?!_)|^\*\*([\s\S]+?)\*\*(?!\*)/,
18014       em: /^\b_((?:[^_]|__)+?)_\b|^\*((?:\*\*|[\s\S])+?)\*(?!\*)/,
18015       code: /^(`+)\s*([\s\S]*?[^`])\s*\1(?!`)/,
18016       br: /^ {2,}\n(?!\s*$)/,
18017       del: noop,
18018       text: /^[\s\S]+?(?=[\\<!\[_*`]| {2,}\n|$)/
18019     };
18020     
18021     inline._inside = /(?:\[[^\]]*\]|[^\[\]]|\](?=[^\[]*\]))*/;
18022     inline._href = /\s*<?([\s\S]*?)>?(?:\s+['"]([\s\S]*?)['"])?\s*/;
18023     
18024     inline.link = replace(inline.link)
18025       ('inside', inline._inside)
18026       ('href', inline._href)
18027       ();
18028     
18029     inline.reflink = replace(inline.reflink)
18030       ('inside', inline._inside)
18031       ();
18032     
18033     /**
18034      * Normal Inline Grammar
18035      */
18036     
18037     inline.normal = merge({}, inline);
18038     
18039     /**
18040      * Pedantic Inline Grammar
18041      */
18042     
18043     inline.pedantic = merge({}, inline.normal, {
18044       strong: /^__(?=\S)([\s\S]*?\S)__(?!_)|^\*\*(?=\S)([\s\S]*?\S)\*\*(?!\*)/,
18045       em: /^_(?=\S)([\s\S]*?\S)_(?!_)|^\*(?=\S)([\s\S]*?\S)\*(?!\*)/
18046     });
18047     
18048     /**
18049      * GFM Inline Grammar
18050      */
18051     
18052     inline.gfm = merge({}, inline.normal, {
18053       escape: replace(inline.escape)('])', '~|])')(),
18054       url: /^(https?:\/\/[^\s<]+[^<.,:;"')\]\s])/,
18055       del: /^~~(?=\S)([\s\S]*?\S)~~/,
18056       text: replace(inline.text)
18057         (']|', '~]|')
18058         ('|', '|https?://|')
18059         ()
18060     });
18061     
18062     /**
18063      * GFM + Line Breaks Inline Grammar
18064      */
18065     
18066     inline.breaks = merge({}, inline.gfm, {
18067       br: replace(inline.br)('{2,}', '*')(),
18068       text: replace(inline.gfm.text)('{2,}', '*')()
18069     });
18070     
18071     /**
18072      * Inline Lexer & Compiler
18073      */
18074     
18075     var InlineLexer  = function (links, options) {
18076       this.options = options || marked.defaults;
18077       this.links = links;
18078       this.rules = inline.normal;
18079       this.renderer = this.options.renderer || new Renderer;
18080       this.renderer.options = this.options;
18081     
18082       if (!this.links) {
18083         throw new
18084           Error('Tokens array requires a `links` property.');
18085       }
18086     
18087       if (this.options.gfm) {
18088         if (this.options.breaks) {
18089           this.rules = inline.breaks;
18090         } else {
18091           this.rules = inline.gfm;
18092         }
18093       } else if (this.options.pedantic) {
18094         this.rules = inline.pedantic;
18095       }
18096     }
18097     
18098     /**
18099      * Expose Inline Rules
18100      */
18101     
18102     InlineLexer.rules = inline;
18103     
18104     /**
18105      * Static Lexing/Compiling Method
18106      */
18107     
18108     InlineLexer.output = function(src, links, options) {
18109       var inline = new InlineLexer(links, options);
18110       return inline.output(src);
18111     };
18112     
18113     /**
18114      * Lexing/Compiling
18115      */
18116     
18117     InlineLexer.prototype.output = function(src) {
18118       var out = ''
18119         , link
18120         , text
18121         , href
18122         , cap;
18123     
18124       while (src) {
18125         // escape
18126         if (cap = this.rules.escape.exec(src)) {
18127           src = src.substring(cap[0].length);
18128           out += cap[1];
18129           continue;
18130         }
18131     
18132         // autolink
18133         if (cap = this.rules.autolink.exec(src)) {
18134           src = src.substring(cap[0].length);
18135           if (cap[2] === '@') {
18136             text = cap[1].charAt(6) === ':'
18137               ? this.mangle(cap[1].substring(7))
18138               : this.mangle(cap[1]);
18139             href = this.mangle('mailto:') + text;
18140           } else {
18141             text = escape(cap[1]);
18142             href = text;
18143           }
18144           out += this.renderer.link(href, null, text);
18145           continue;
18146         }
18147     
18148         // url (gfm)
18149         if (!this.inLink && (cap = this.rules.url.exec(src))) {
18150           src = src.substring(cap[0].length);
18151           text = escape(cap[1]);
18152           href = text;
18153           out += this.renderer.link(href, null, text);
18154           continue;
18155         }
18156     
18157         // tag
18158         if (cap = this.rules.tag.exec(src)) {
18159           if (!this.inLink && /^<a /i.test(cap[0])) {
18160             this.inLink = true;
18161           } else if (this.inLink && /^<\/a>/i.test(cap[0])) {
18162             this.inLink = false;
18163           }
18164           src = src.substring(cap[0].length);
18165           out += this.options.sanitize
18166             ? this.options.sanitizer
18167               ? this.options.sanitizer(cap[0])
18168               : escape(cap[0])
18169             : cap[0];
18170           continue;
18171         }
18172     
18173         // link
18174         if (cap = this.rules.link.exec(src)) {
18175           src = src.substring(cap[0].length);
18176           this.inLink = true;
18177           out += this.outputLink(cap, {
18178             href: cap[2],
18179             title: cap[3]
18180           });
18181           this.inLink = false;
18182           continue;
18183         }
18184     
18185         // reflink, nolink
18186         if ((cap = this.rules.reflink.exec(src))
18187             || (cap = this.rules.nolink.exec(src))) {
18188           src = src.substring(cap[0].length);
18189           link = (cap[2] || cap[1]).replace(/\s+/g, ' ');
18190           link = this.links[link.toLowerCase()];
18191           if (!link || !link.href) {
18192             out += cap[0].charAt(0);
18193             src = cap[0].substring(1) + src;
18194             continue;
18195           }
18196           this.inLink = true;
18197           out += this.outputLink(cap, link);
18198           this.inLink = false;
18199           continue;
18200         }
18201     
18202         // strong
18203         if (cap = this.rules.strong.exec(src)) {
18204           src = src.substring(cap[0].length);
18205           out += this.renderer.strong(this.output(cap[2] || cap[1]));
18206           continue;
18207         }
18208     
18209         // em
18210         if (cap = this.rules.em.exec(src)) {
18211           src = src.substring(cap[0].length);
18212           out += this.renderer.em(this.output(cap[2] || cap[1]));
18213           continue;
18214         }
18215     
18216         // code
18217         if (cap = this.rules.code.exec(src)) {
18218           src = src.substring(cap[0].length);
18219           out += this.renderer.codespan(escape(cap[2], true));
18220           continue;
18221         }
18222     
18223         // br
18224         if (cap = this.rules.br.exec(src)) {
18225           src = src.substring(cap[0].length);
18226           out += this.renderer.br();
18227           continue;
18228         }
18229     
18230         // del (gfm)
18231         if (cap = this.rules.del.exec(src)) {
18232           src = src.substring(cap[0].length);
18233           out += this.renderer.del(this.output(cap[1]));
18234           continue;
18235         }
18236     
18237         // text
18238         if (cap = this.rules.text.exec(src)) {
18239           src = src.substring(cap[0].length);
18240           out += this.renderer.text(escape(this.smartypants(cap[0])));
18241           continue;
18242         }
18243     
18244         if (src) {
18245           throw new
18246             Error('Infinite loop on byte: ' + src.charCodeAt(0));
18247         }
18248       }
18249     
18250       return out;
18251     };
18252     
18253     /**
18254      * Compile Link
18255      */
18256     
18257     InlineLexer.prototype.outputLink = function(cap, link) {
18258       var href = escape(link.href)
18259         , title = link.title ? escape(link.title) : null;
18260     
18261       return cap[0].charAt(0) !== '!'
18262         ? this.renderer.link(href, title, this.output(cap[1]))
18263         : this.renderer.image(href, title, escape(cap[1]));
18264     };
18265     
18266     /**
18267      * Smartypants Transformations
18268      */
18269     
18270     InlineLexer.prototype.smartypants = function(text) {
18271       if (!this.options.smartypants)  { return text; }
18272       return text
18273         // em-dashes
18274         .replace(/---/g, '\u2014')
18275         // en-dashes
18276         .replace(/--/g, '\u2013')
18277         // opening singles
18278         .replace(/(^|[-\u2014/(\[{"\s])'/g, '$1\u2018')
18279         // closing singles & apostrophes
18280         .replace(/'/g, '\u2019')
18281         // opening doubles
18282         .replace(/(^|[-\u2014/(\[{\u2018\s])"/g, '$1\u201c')
18283         // closing doubles
18284         .replace(/"/g, '\u201d')
18285         // ellipses
18286         .replace(/\.{3}/g, '\u2026');
18287     };
18288     
18289     /**
18290      * Mangle Links
18291      */
18292     
18293     InlineLexer.prototype.mangle = function(text) {
18294       if (!this.options.mangle) { return text; }
18295       var out = ''
18296         , l = text.length
18297         , i = 0
18298         , ch;
18299     
18300       for (; i < l; i++) {
18301         ch = text.charCodeAt(i);
18302         if (Math.random() > 0.5) {
18303           ch = 'x' + ch.toString(16);
18304         }
18305         out += '&#' + ch + ';';
18306       }
18307     
18308       return out;
18309     };
18310     
18311     /**
18312      * Renderer
18313      */
18314     
18315      /**
18316          * eval:var:Renderer
18317     */
18318     
18319     var Renderer   = function (options) {
18320       this.options = options || {};
18321     }
18322     
18323     Renderer.prototype.code = function(code, lang, escaped) {
18324       if (this.options.highlight) {
18325         var out = this.options.highlight(code, lang);
18326         if (out != null && out !== code) {
18327           escaped = true;
18328           code = out;
18329         }
18330       } else {
18331             // hack!!! - it's already escapeD?
18332             escaped = true;
18333       }
18334     
18335       if (!lang) {
18336         return '<pre><code>'
18337           + (escaped ? code : escape(code, true))
18338           + '\n</code></pre>';
18339       }
18340     
18341       return '<pre><code class="'
18342         + this.options.langPrefix
18343         + escape(lang, true)
18344         + '">'
18345         + (escaped ? code : escape(code, true))
18346         + '\n</code></pre>\n';
18347     };
18348     
18349     Renderer.prototype.blockquote = function(quote) {
18350       return '<blockquote>\n' + quote + '</blockquote>\n';
18351     };
18352     
18353     Renderer.prototype.html = function(html) {
18354       return html;
18355     };
18356     
18357     Renderer.prototype.heading = function(text, level, raw) {
18358       return '<h'
18359         + level
18360         + ' id="'
18361         + this.options.headerPrefix
18362         + raw.toLowerCase().replace(/[^\w]+/g, '-')
18363         + '">'
18364         + text
18365         + '</h'
18366         + level
18367         + '>\n';
18368     };
18369     
18370     Renderer.prototype.hr = function() {
18371       return this.options.xhtml ? '<hr/>\n' : '<hr>\n';
18372     };
18373     
18374     Renderer.prototype.list = function(body, ordered) {
18375       var type = ordered ? 'ol' : 'ul';
18376       return '<' + type + '>\n' + body + '</' + type + '>\n';
18377     };
18378     
18379     Renderer.prototype.listitem = function(text) {
18380       return '<li>' + text + '</li>\n';
18381     };
18382     
18383     Renderer.prototype.paragraph = function(text) {
18384       return '<p>' + text + '</p>\n';
18385     };
18386     
18387     Renderer.prototype.table = function(header, body) {
18388       return '<table class="table table-striped">\n'
18389         + '<thead>\n'
18390         + header
18391         + '</thead>\n'
18392         + '<tbody>\n'
18393         + body
18394         + '</tbody>\n'
18395         + '</table>\n';
18396     };
18397     
18398     Renderer.prototype.tablerow = function(content) {
18399       return '<tr>\n' + content + '</tr>\n';
18400     };
18401     
18402     Renderer.prototype.tablecell = function(content, flags) {
18403       var type = flags.header ? 'th' : 'td';
18404       var tag = flags.align
18405         ? '<' + type + ' style="text-align:' + flags.align + '">'
18406         : '<' + type + '>';
18407       return tag + content + '</' + type + '>\n';
18408     };
18409     
18410     // span level renderer
18411     Renderer.prototype.strong = function(text) {
18412       return '<strong>' + text + '</strong>';
18413     };
18414     
18415     Renderer.prototype.em = function(text) {
18416       return '<em>' + text + '</em>';
18417     };
18418     
18419     Renderer.prototype.codespan = function(text) {
18420       return '<code>' + text + '</code>';
18421     };
18422     
18423     Renderer.prototype.br = function() {
18424       return this.options.xhtml ? '<br/>' : '<br>';
18425     };
18426     
18427     Renderer.prototype.del = function(text) {
18428       return '<del>' + text + '</del>';
18429     };
18430     
18431     Renderer.prototype.link = function(href, title, text) {
18432       if (this.options.sanitize) {
18433         try {
18434           var prot = decodeURIComponent(unescape(href))
18435             .replace(/[^\w:]/g, '')
18436             .toLowerCase();
18437         } catch (e) {
18438           return '';
18439         }
18440         if (prot.indexOf('javascript:') === 0 || prot.indexOf('vbscript:') === 0) {
18441           return '';
18442         }
18443       }
18444       var out = '<a href="' + href + '"';
18445       if (title) {
18446         out += ' title="' + title + '"';
18447       }
18448       out += '>' + text + '</a>';
18449       return out;
18450     };
18451     
18452     Renderer.prototype.image = function(href, title, text) {
18453       var out = '<img src="' + href + '" alt="' + text + '"';
18454       if (title) {
18455         out += ' title="' + title + '"';
18456       }
18457       out += this.options.xhtml ? '/>' : '>';
18458       return out;
18459     };
18460     
18461     Renderer.prototype.text = function(text) {
18462       return text;
18463     };
18464     
18465     /**
18466      * Parsing & Compiling
18467      */
18468          /**
18469          * eval:var:Parser
18470     */
18471     
18472     var Parser= function (options) {
18473       this.tokens = [];
18474       this.token = null;
18475       this.options = options || marked.defaults;
18476       this.options.renderer = this.options.renderer || new Renderer;
18477       this.renderer = this.options.renderer;
18478       this.renderer.options = this.options;
18479     }
18480     
18481     /**
18482      * Static Parse Method
18483      */
18484     
18485     Parser.parse = function(src, options, renderer) {
18486       var parser = new Parser(options, renderer);
18487       return parser.parse(src);
18488     };
18489     
18490     /**
18491      * Parse Loop
18492      */
18493     
18494     Parser.prototype.parse = function(src) {
18495       this.inline = new InlineLexer(src.links, this.options, this.renderer);
18496       this.tokens = src.reverse();
18497     
18498       var out = '';
18499       while (this.next()) {
18500         out += this.tok();
18501       }
18502     
18503       return out;
18504     };
18505     
18506     /**
18507      * Next Token
18508      */
18509     
18510     Parser.prototype.next = function() {
18511       return this.token = this.tokens.pop();
18512     };
18513     
18514     /**
18515      * Preview Next Token
18516      */
18517     
18518     Parser.prototype.peek = function() {
18519       return this.tokens[this.tokens.length - 1] || 0;
18520     };
18521     
18522     /**
18523      * Parse Text Tokens
18524      */
18525     
18526     Parser.prototype.parseText = function() {
18527       var body = this.token.text;
18528     
18529       while (this.peek().type === 'text') {
18530         body += '\n' + this.next().text;
18531       }
18532     
18533       return this.inline.output(body);
18534     };
18535     
18536     /**
18537      * Parse Current Token
18538      */
18539     
18540     Parser.prototype.tok = function() {
18541       switch (this.token.type) {
18542         case 'space': {
18543           return '';
18544         }
18545         case 'hr': {
18546           return this.renderer.hr();
18547         }
18548         case 'heading': {
18549           return this.renderer.heading(
18550             this.inline.output(this.token.text),
18551             this.token.depth,
18552             this.token.text);
18553         }
18554         case 'code': {
18555           return this.renderer.code(this.token.text,
18556             this.token.lang,
18557             this.token.escaped);
18558         }
18559         case 'table': {
18560           var header = ''
18561             , body = ''
18562             , i
18563             , row
18564             , cell
18565             , flags
18566             , j;
18567     
18568           // header
18569           cell = '';
18570           for (i = 0; i < this.token.header.length; i++) {
18571             flags = { header: true, align: this.token.align[i] };
18572             cell += this.renderer.tablecell(
18573               this.inline.output(this.token.header[i]),
18574               { header: true, align: this.token.align[i] }
18575             );
18576           }
18577           header += this.renderer.tablerow(cell);
18578     
18579           for (i = 0; i < this.token.cells.length; i++) {
18580             row = this.token.cells[i];
18581     
18582             cell = '';
18583             for (j = 0; j < row.length; j++) {
18584               cell += this.renderer.tablecell(
18585                 this.inline.output(row[j]),
18586                 { header: false, align: this.token.align[j] }
18587               );
18588             }
18589     
18590             body += this.renderer.tablerow(cell);
18591           }
18592           return this.renderer.table(header, body);
18593         }
18594         case 'blockquote_start': {
18595           var body = '';
18596     
18597           while (this.next().type !== 'blockquote_end') {
18598             body += this.tok();
18599           }
18600     
18601           return this.renderer.blockquote(body);
18602         }
18603         case 'list_start': {
18604           var body = ''
18605             , ordered = this.token.ordered;
18606     
18607           while (this.next().type !== 'list_end') {
18608             body += this.tok();
18609           }
18610     
18611           return this.renderer.list(body, ordered);
18612         }
18613         case 'list_item_start': {
18614           var body = '';
18615     
18616           while (this.next().type !== 'list_item_end') {
18617             body += this.token.type === 'text'
18618               ? this.parseText()
18619               : this.tok();
18620           }
18621     
18622           return this.renderer.listitem(body);
18623         }
18624         case 'loose_item_start': {
18625           var body = '';
18626     
18627           while (this.next().type !== 'list_item_end') {
18628             body += this.tok();
18629           }
18630     
18631           return this.renderer.listitem(body);
18632         }
18633         case 'html': {
18634           var html = !this.token.pre && !this.options.pedantic
18635             ? this.inline.output(this.token.text)
18636             : this.token.text;
18637           return this.renderer.html(html);
18638         }
18639         case 'paragraph': {
18640           return this.renderer.paragraph(this.inline.output(this.token.text));
18641         }
18642         case 'text': {
18643           return this.renderer.paragraph(this.parseText());
18644         }
18645       }
18646     };
18647   
18648     
18649     /**
18650      * Marked
18651      */
18652          /**
18653          * eval:var:marked
18654     */
18655     var marked = function (src, opt, callback) {
18656       if (callback || typeof opt === 'function') {
18657         if (!callback) {
18658           callback = opt;
18659           opt = null;
18660         }
18661     
18662         opt = merge({}, marked.defaults, opt || {});
18663     
18664         var highlight = opt.highlight
18665           , tokens
18666           , pending
18667           , i = 0;
18668     
18669         try {
18670           tokens = Lexer.lex(src, opt)
18671         } catch (e) {
18672           return callback(e);
18673         }
18674     
18675         pending = tokens.length;
18676          /**
18677          * eval:var:done
18678     */
18679         var done = function(err) {
18680           if (err) {
18681             opt.highlight = highlight;
18682             return callback(err);
18683           }
18684     
18685           var out;
18686     
18687           try {
18688             out = Parser.parse(tokens, opt);
18689           } catch (e) {
18690             err = e;
18691           }
18692     
18693           opt.highlight = highlight;
18694     
18695           return err
18696             ? callback(err)
18697             : callback(null, out);
18698         };
18699     
18700         if (!highlight || highlight.length < 3) {
18701           return done();
18702         }
18703     
18704         delete opt.highlight;
18705     
18706         if (!pending) { return done(); }
18707     
18708         for (; i < tokens.length; i++) {
18709           (function(token) {
18710             if (token.type !== 'code') {
18711               return --pending || done();
18712             }
18713             return highlight(token.text, token.lang, function(err, code) {
18714               if (err) { return done(err); }
18715               if (code == null || code === token.text) {
18716                 return --pending || done();
18717               }
18718               token.text = code;
18719               token.escaped = true;
18720               --pending || done();
18721             });
18722           })(tokens[i]);
18723         }
18724     
18725         return;
18726       }
18727       try {
18728         if (opt) { opt = merge({}, marked.defaults, opt); }
18729         return Parser.parse(Lexer.lex(src, opt), opt);
18730       } catch (e) {
18731         e.message += '\nPlease report this to https://github.com/chjj/marked.';
18732         if ((opt || marked.defaults).silent) {
18733           return '<p>An error occured:</p><pre>'
18734             + escape(e.message + '', true)
18735             + '</pre>';
18736         }
18737         throw e;
18738       }
18739     }
18740     
18741     /**
18742      * Options
18743      */
18744     
18745     marked.options =
18746     marked.setOptions = function(opt) {
18747       merge(marked.defaults, opt);
18748       return marked;
18749     };
18750     
18751     marked.defaults = {
18752       gfm: true,
18753       tables: true,
18754       breaks: false,
18755       pedantic: false,
18756       sanitize: false,
18757       sanitizer: null,
18758       mangle: true,
18759       smartLists: false,
18760       silent: false,
18761       highlight: null,
18762       langPrefix: 'lang-',
18763       smartypants: false,
18764       headerPrefix: '',
18765       renderer: new Renderer,
18766       xhtml: false
18767     };
18768     
18769     /**
18770      * Expose
18771      */
18772     
18773     marked.Parser = Parser;
18774     marked.parser = Parser.parse;
18775     
18776     marked.Renderer = Renderer;
18777     
18778     marked.Lexer = Lexer;
18779     marked.lexer = Lexer.lex;
18780     
18781     marked.InlineLexer = InlineLexer;
18782     marked.inlineLexer = InlineLexer.output;
18783     
18784     marked.parse = marked;
18785     
18786     Roo.Markdown.marked = marked;
18787
18788 })();/*
18789  * Based on:
18790  * Ext JS Library 1.1.1
18791  * Copyright(c) 2006-2007, Ext JS, LLC.
18792  *
18793  * Originally Released Under LGPL - original licence link has changed is not relivant.
18794  *
18795  * Fork - LGPL
18796  * <script type="text/javascript">
18797  */
18798
18799
18800
18801 /*
18802  * These classes are derivatives of the similarly named classes in the YUI Library.
18803  * The original license:
18804  * Copyright (c) 2006, Yahoo! Inc. All rights reserved.
18805  * Code licensed under the BSD License:
18806  * http://developer.yahoo.net/yui/license.txt
18807  */
18808
18809 (function() {
18810
18811 var Event=Roo.EventManager;
18812 var Dom=Roo.lib.Dom;
18813
18814 /**
18815  * @class Roo.dd.DragDrop
18816  * @extends Roo.util.Observable
18817  * Defines the interface and base operation of items that that can be
18818  * dragged or can be drop targets.  It was designed to be extended, overriding
18819  * the event handlers for startDrag, onDrag, onDragOver and onDragOut.
18820  * Up to three html elements can be associated with a DragDrop instance:
18821  * <ul>
18822  * <li>linked element: the element that is passed into the constructor.
18823  * This is the element which defines the boundaries for interaction with
18824  * other DragDrop objects.</li>
18825  * <li>handle element(s): The drag operation only occurs if the element that
18826  * was clicked matches a handle element.  By default this is the linked
18827  * element, but there are times that you will want only a portion of the
18828  * linked element to initiate the drag operation, and the setHandleElId()
18829  * method provides a way to define this.</li>
18830  * <li>drag element: this represents the element that would be moved along
18831  * with the cursor during a drag operation.  By default, this is the linked
18832  * element itself as in {@link Roo.dd.DD}.  setDragElId() lets you define
18833  * a separate element that would be moved, as in {@link Roo.dd.DDProxy}.
18834  * </li>
18835  * </ul>
18836  * This class should not be instantiated until the onload event to ensure that
18837  * the associated elements are available.
18838  * The following would define a DragDrop obj that would interact with any
18839  * other DragDrop obj in the "group1" group:
18840  * <pre>
18841  *  dd = new Roo.dd.DragDrop("div1", "group1");
18842  * </pre>
18843  * Since none of the event handlers have been implemented, nothing would
18844  * actually happen if you were to run the code above.  Normally you would
18845  * override this class or one of the default implementations, but you can
18846  * also override the methods you want on an instance of the class...
18847  * <pre>
18848  *  dd.onDragDrop = function(e, id) {
18849  *  &nbsp;&nbsp;alert("dd was dropped on " + id);
18850  *  }
18851  * </pre>
18852  * @constructor
18853  * @param {String} id of the element that is linked to this instance
18854  * @param {String} sGroup the group of related DragDrop objects
18855  * @param {object} config an object containing configurable attributes
18856  *                Valid properties for DragDrop:
18857  *                    padding, isTarget, maintainOffset, primaryButtonOnly
18858  */
18859 Roo.dd.DragDrop = function(id, sGroup, config) {
18860     if (id) {
18861         this.init(id, sGroup, config);
18862     }
18863     
18864 };
18865
18866 Roo.extend(Roo.dd.DragDrop, Roo.util.Observable , {
18867
18868     /**
18869      * The id of the element associated with this object.  This is what we
18870      * refer to as the "linked element" because the size and position of
18871      * this element is used to determine when the drag and drop objects have
18872      * interacted.
18873      * @property id
18874      * @type String
18875      */
18876     id: null,
18877
18878     /**
18879      * Configuration attributes passed into the constructor
18880      * @property config
18881      * @type object
18882      */
18883     config: null,
18884
18885     /**
18886      * The id of the element that will be dragged.  By default this is same
18887      * as the linked element , but could be changed to another element. Ex:
18888      * Roo.dd.DDProxy
18889      * @property dragElId
18890      * @type String
18891      * @private
18892      */
18893     dragElId: null,
18894
18895     /**
18896      * the id of the element that initiates the drag operation.  By default
18897      * this is the linked element, but could be changed to be a child of this
18898      * element.  This lets us do things like only starting the drag when the
18899      * header element within the linked html element is clicked.
18900      * @property handleElId
18901      * @type String
18902      * @private
18903      */
18904     handleElId: null,
18905
18906     /**
18907      * An associative array of HTML tags that will be ignored if clicked.
18908      * @property invalidHandleTypes
18909      * @type {string: string}
18910      */
18911     invalidHandleTypes: null,
18912
18913     /**
18914      * An associative array of ids for elements that will be ignored if clicked
18915      * @property invalidHandleIds
18916      * @type {string: string}
18917      */
18918     invalidHandleIds: null,
18919
18920     /**
18921      * An indexted array of css class names for elements that will be ignored
18922      * if clicked.
18923      * @property invalidHandleClasses
18924      * @type string[]
18925      */
18926     invalidHandleClasses: null,
18927
18928     /**
18929      * The linked element's absolute X position at the time the drag was
18930      * started
18931      * @property startPageX
18932      * @type int
18933      * @private
18934      */
18935     startPageX: 0,
18936
18937     /**
18938      * The linked element's absolute X position at the time the drag was
18939      * started
18940      * @property startPageY
18941      * @type int
18942      * @private
18943      */
18944     startPageY: 0,
18945
18946     /**
18947      * The group defines a logical collection of DragDrop objects that are
18948      * related.  Instances only get events when interacting with other
18949      * DragDrop object in the same group.  This lets us define multiple
18950      * groups using a single DragDrop subclass if we want.
18951      * @property groups
18952      * @type {string: string}
18953      */
18954     groups: null,
18955
18956     /**
18957      * Individual drag/drop instances can be locked.  This will prevent
18958      * onmousedown start drag.
18959      * @property locked
18960      * @type boolean
18961      * @private
18962      */
18963     locked: false,
18964
18965     /**
18966      * Lock this instance
18967      * @method lock
18968      */
18969     lock: function() { this.locked = true; },
18970
18971     /**
18972      * Unlock this instace
18973      * @method unlock
18974      */
18975     unlock: function() { this.locked = false; },
18976
18977     /**
18978      * By default, all insances can be a drop target.  This can be disabled by
18979      * setting isTarget to false.
18980      * @method isTarget
18981      * @type boolean
18982      */
18983     isTarget: true,
18984
18985     /**
18986      * The padding configured for this drag and drop object for calculating
18987      * the drop zone intersection with this object.
18988      * @method padding
18989      * @type int[]
18990      */
18991     padding: null,
18992
18993     /**
18994      * Cached reference to the linked element
18995      * @property _domRef
18996      * @private
18997      */
18998     _domRef: null,
18999
19000     /**
19001      * Internal typeof flag
19002      * @property __ygDragDrop
19003      * @private
19004      */
19005     __ygDragDrop: true,
19006
19007     /**
19008      * Set to true when horizontal contraints are applied
19009      * @property constrainX
19010      * @type boolean
19011      * @private
19012      */
19013     constrainX: false,
19014
19015     /**
19016      * Set to true when vertical contraints are applied
19017      * @property constrainY
19018      * @type boolean
19019      * @private
19020      */
19021     constrainY: false,
19022
19023     /**
19024      * The left constraint
19025      * @property minX
19026      * @type int
19027      * @private
19028      */
19029     minX: 0,
19030
19031     /**
19032      * The right constraint
19033      * @property maxX
19034      * @type int
19035      * @private
19036      */
19037     maxX: 0,
19038
19039     /**
19040      * The up constraint
19041      * @property minY
19042      * @type int
19043      * @type int
19044      * @private
19045      */
19046     minY: 0,
19047
19048     /**
19049      * The down constraint
19050      * @property maxY
19051      * @type int
19052      * @private
19053      */
19054     maxY: 0,
19055
19056     /**
19057      * Maintain offsets when we resetconstraints.  Set to true when you want
19058      * the position of the element relative to its parent to stay the same
19059      * when the page changes
19060      *
19061      * @property maintainOffset
19062      * @type boolean
19063      */
19064     maintainOffset: false,
19065
19066     /**
19067      * Array of pixel locations the element will snap to if we specified a
19068      * horizontal graduation/interval.  This array is generated automatically
19069      * when you define a tick interval.
19070      * @property xTicks
19071      * @type int[]
19072      */
19073     xTicks: null,
19074
19075     /**
19076      * Array of pixel locations the element will snap to if we specified a
19077      * vertical graduation/interval.  This array is generated automatically
19078      * when you define a tick interval.
19079      * @property yTicks
19080      * @type int[]
19081      */
19082     yTicks: null,
19083
19084     /**
19085      * By default the drag and drop instance will only respond to the primary
19086      * button click (left button for a right-handed mouse).  Set to true to
19087      * allow drag and drop to start with any mouse click that is propogated
19088      * by the browser
19089      * @property primaryButtonOnly
19090      * @type boolean
19091      */
19092     primaryButtonOnly: true,
19093
19094     /**
19095      * The availabe property is false until the linked dom element is accessible.
19096      * @property available
19097      * @type boolean
19098      */
19099     available: false,
19100
19101     /**
19102      * By default, drags can only be initiated if the mousedown occurs in the
19103      * region the linked element is.  This is done in part to work around a
19104      * bug in some browsers that mis-report the mousedown if the previous
19105      * mouseup happened outside of the window.  This property is set to true
19106      * if outer handles are defined.
19107      *
19108      * @property hasOuterHandles
19109      * @type boolean
19110      * @default false
19111      */
19112     hasOuterHandles: false,
19113
19114     /**
19115      * Code that executes immediately before the startDrag event
19116      * @method b4StartDrag
19117      * @private
19118      */
19119     b4StartDrag: function(x, y) { },
19120
19121     /**
19122      * Abstract method called after a drag/drop object is clicked
19123      * and the drag or mousedown time thresholds have beeen met.
19124      * @method startDrag
19125      * @param {int} X click location
19126      * @param {int} Y click location
19127      */
19128     startDrag: function(x, y) { /* override this */ },
19129
19130     /**
19131      * Code that executes immediately before the onDrag event
19132      * @method b4Drag
19133      * @private
19134      */
19135     b4Drag: function(e) { },
19136
19137     /**
19138      * Abstract method called during the onMouseMove event while dragging an
19139      * object.
19140      * @method onDrag
19141      * @param {Event} e the mousemove event
19142      */
19143     onDrag: function(e) { /* override this */ },
19144
19145     /**
19146      * Abstract method called when this element fist begins hovering over
19147      * another DragDrop obj
19148      * @method onDragEnter
19149      * @param {Event} e the mousemove event
19150      * @param {String|DragDrop[]} id In POINT mode, the element
19151      * id this is hovering over.  In INTERSECT mode, an array of one or more
19152      * dragdrop items being hovered over.
19153      */
19154     onDragEnter: function(e, id) { /* override this */ },
19155
19156     /**
19157      * Code that executes immediately before the onDragOver event
19158      * @method b4DragOver
19159      * @private
19160      */
19161     b4DragOver: function(e) { },
19162
19163     /**
19164      * Abstract method called when this element is hovering over another
19165      * DragDrop obj
19166      * @method onDragOver
19167      * @param {Event} e the mousemove event
19168      * @param {String|DragDrop[]} id In POINT mode, the element
19169      * id this is hovering over.  In INTERSECT mode, an array of dd items
19170      * being hovered over.
19171      */
19172     onDragOver: function(e, id) { /* override this */ },
19173
19174     /**
19175      * Code that executes immediately before the onDragOut event
19176      * @method b4DragOut
19177      * @private
19178      */
19179     b4DragOut: function(e) { },
19180
19181     /**
19182      * Abstract method called when we are no longer hovering over an element
19183      * @method onDragOut
19184      * @param {Event} e the mousemove event
19185      * @param {String|DragDrop[]} id In POINT mode, the element
19186      * id this was hovering over.  In INTERSECT mode, an array of dd items
19187      * that the mouse is no longer over.
19188      */
19189     onDragOut: function(e, id) { /* override this */ },
19190
19191     /**
19192      * Code that executes immediately before the onDragDrop event
19193      * @method b4DragDrop
19194      * @private
19195      */
19196     b4DragDrop: function(e) { },
19197
19198     /**
19199      * Abstract method called when this item is dropped on another DragDrop
19200      * obj
19201      * @method onDragDrop
19202      * @param {Event} e the mouseup event
19203      * @param {String|DragDrop[]} id In POINT mode, the element
19204      * id this was dropped on.  In INTERSECT mode, an array of dd items this
19205      * was dropped on.
19206      */
19207     onDragDrop: function(e, id) { /* override this */ },
19208
19209     /**
19210      * Abstract method called when this item is dropped on an area with no
19211      * drop target
19212      * @method onInvalidDrop
19213      * @param {Event} e the mouseup event
19214      */
19215     onInvalidDrop: function(e) { /* override this */ },
19216
19217     /**
19218      * Code that executes immediately before the endDrag event
19219      * @method b4EndDrag
19220      * @private
19221      */
19222     b4EndDrag: function(e) { },
19223
19224     /**
19225      * Fired when we are done dragging the object
19226      * @method endDrag
19227      * @param {Event} e the mouseup event
19228      */
19229     endDrag: function(e) { /* override this */ },
19230
19231     /**
19232      * Code executed immediately before the onMouseDown event
19233      * @method b4MouseDown
19234      * @param {Event} e the mousedown event
19235      * @private
19236      */
19237     b4MouseDown: function(e) {  },
19238
19239     /**
19240      * Event handler that fires when a drag/drop obj gets a mousedown
19241      * @method onMouseDown
19242      * @param {Event} e the mousedown event
19243      */
19244     onMouseDown: function(e) { /* override this */ },
19245
19246     /**
19247      * Event handler that fires when a drag/drop obj gets a mouseup
19248      * @method onMouseUp
19249      * @param {Event} e the mouseup event
19250      */
19251     onMouseUp: function(e) { /* override this */ },
19252
19253     /**
19254      * Override the onAvailable method to do what is needed after the initial
19255      * position was determined.
19256      * @method onAvailable
19257      */
19258     onAvailable: function () {
19259     },
19260
19261     /*
19262      * Provides default constraint padding to "constrainTo" elements (defaults to {left: 0, right:0, top:0, bottom:0}).
19263      * @type Object
19264      */
19265     defaultPadding : {left:0, right:0, top:0, bottom:0},
19266
19267     /*
19268      * Initializes the drag drop object's constraints to restrict movement to a certain element.
19269  *
19270  * Usage:
19271  <pre><code>
19272  var dd = new Roo.dd.DDProxy("dragDiv1", "proxytest",
19273                 { dragElId: "existingProxyDiv" });
19274  dd.startDrag = function(){
19275      this.constrainTo("parent-id");
19276  };
19277  </code></pre>
19278  * Or you can initalize it using the {@link Roo.Element} object:
19279  <pre><code>
19280  Roo.get("dragDiv1").initDDProxy("proxytest", {dragElId: "existingProxyDiv"}, {
19281      startDrag : function(){
19282          this.constrainTo("parent-id");
19283      }
19284  });
19285  </code></pre>
19286      * @param {String/HTMLElement/Element} constrainTo The element to constrain to.
19287      * @param {Object/Number} pad (optional) Pad provides a way to specify "padding" of the constraints,
19288      * and can be either a number for symmetrical padding (4 would be equal to {left:4, right:4, top:4, bottom:4}) or
19289      * an object containing the sides to pad. For example: {right:10, bottom:10}
19290      * @param {Boolean} inContent (optional) Constrain the draggable in the content box of the element (inside padding and borders)
19291      */
19292     constrainTo : function(constrainTo, pad, inContent){
19293         if(typeof pad == "number"){
19294             pad = {left: pad, right:pad, top:pad, bottom:pad};
19295         }
19296         pad = pad || this.defaultPadding;
19297         var b = Roo.get(this.getEl()).getBox();
19298         var ce = Roo.get(constrainTo);
19299         var s = ce.getScroll();
19300         var c, cd = ce.dom;
19301         if(cd == document.body){
19302             c = { x: s.left, y: s.top, width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
19303         }else{
19304             xy = ce.getXY();
19305             c = {x : xy[0]+s.left, y: xy[1]+s.top, width: cd.clientWidth, height: cd.clientHeight};
19306         }
19307
19308
19309         var topSpace = b.y - c.y;
19310         var leftSpace = b.x - c.x;
19311
19312         this.resetConstraints();
19313         this.setXConstraint(leftSpace - (pad.left||0), // left
19314                 c.width - leftSpace - b.width - (pad.right||0) //right
19315         );
19316         this.setYConstraint(topSpace - (pad.top||0), //top
19317                 c.height - topSpace - b.height - (pad.bottom||0) //bottom
19318         );
19319     },
19320
19321     /**
19322      * Returns a reference to the linked element
19323      * @method getEl
19324      * @return {HTMLElement} the html element
19325      */
19326     getEl: function() {
19327         if (!this._domRef) {
19328             this._domRef = Roo.getDom(this.id);
19329         }
19330
19331         return this._domRef;
19332     },
19333
19334     /**
19335      * Returns a reference to the actual element to drag.  By default this is
19336      * the same as the html element, but it can be assigned to another
19337      * element. An example of this can be found in Roo.dd.DDProxy
19338      * @method getDragEl
19339      * @return {HTMLElement} the html element
19340      */
19341     getDragEl: function() {
19342         return Roo.getDom(this.dragElId);
19343     },
19344
19345     /**
19346      * Sets up the DragDrop object.  Must be called in the constructor of any
19347      * Roo.dd.DragDrop subclass
19348      * @method init
19349      * @param id the id of the linked element
19350      * @param {String} sGroup the group of related items
19351      * @param {object} config configuration attributes
19352      */
19353     init: function(id, sGroup, config) {
19354         this.initTarget(id, sGroup, config);
19355         if (!Roo.isTouch) {
19356             Event.on(this.id, "mousedown", this.handleMouseDown, this);
19357         }
19358         Event.on(this.id, "touchstart", this.handleMouseDown, this);
19359         // Event.on(this.id, "selectstart", Event.preventDefault);
19360     },
19361
19362     /**
19363      * Initializes Targeting functionality only... the object does not
19364      * get a mousedown handler.
19365      * @method initTarget
19366      * @param id the id of the linked element
19367      * @param {String} sGroup the group of related items
19368      * @param {object} config configuration attributes
19369      */
19370     initTarget: function(id, sGroup, config) {
19371
19372         // configuration attributes
19373         this.config = config || {};
19374
19375         // create a local reference to the drag and drop manager
19376         this.DDM = Roo.dd.DDM;
19377         // initialize the groups array
19378         this.groups = {};
19379
19380         // assume that we have an element reference instead of an id if the
19381         // parameter is not a string
19382         if (typeof id !== "string") {
19383             id = Roo.id(id);
19384         }
19385
19386         // set the id
19387         this.id = id;
19388
19389         // add to an interaction group
19390         this.addToGroup((sGroup) ? sGroup : "default");
19391
19392         // We don't want to register this as the handle with the manager
19393         // so we just set the id rather than calling the setter.
19394         this.handleElId = id;
19395
19396         // the linked element is the element that gets dragged by default
19397         this.setDragElId(id);
19398
19399         // by default, clicked anchors will not start drag operations.
19400         this.invalidHandleTypes = { A: "A" };
19401         this.invalidHandleIds = {};
19402         this.invalidHandleClasses = [];
19403
19404         this.applyConfig();
19405
19406         this.handleOnAvailable();
19407     },
19408
19409     /**
19410      * Applies the configuration parameters that were passed into the constructor.
19411      * This is supposed to happen at each level through the inheritance chain.  So
19412      * a DDProxy implentation will execute apply config on DDProxy, DD, and
19413      * DragDrop in order to get all of the parameters that are available in
19414      * each object.
19415      * @method applyConfig
19416      */
19417     applyConfig: function() {
19418
19419         // configurable properties:
19420         //    padding, isTarget, maintainOffset, primaryButtonOnly
19421         this.padding           = this.config.padding || [0, 0, 0, 0];
19422         this.isTarget          = (this.config.isTarget !== false);
19423         this.maintainOffset    = (this.config.maintainOffset);
19424         this.primaryButtonOnly = (this.config.primaryButtonOnly !== false);
19425
19426     },
19427
19428     /**
19429      * Executed when the linked element is available
19430      * @method handleOnAvailable
19431      * @private
19432      */
19433     handleOnAvailable: function() {
19434         this.available = true;
19435         this.resetConstraints();
19436         this.onAvailable();
19437     },
19438
19439      /**
19440      * Configures the padding for the target zone in px.  Effectively expands
19441      * (or reduces) the virtual object size for targeting calculations.
19442      * Supports css-style shorthand; if only one parameter is passed, all sides
19443      * will have that padding, and if only two are passed, the top and bottom
19444      * will have the first param, the left and right the second.
19445      * @method setPadding
19446      * @param {int} iTop    Top pad
19447      * @param {int} iRight  Right pad
19448      * @param {int} iBot    Bot pad
19449      * @param {int} iLeft   Left pad
19450      */
19451     setPadding: function(iTop, iRight, iBot, iLeft) {
19452         // this.padding = [iLeft, iRight, iTop, iBot];
19453         if (!iRight && 0 !== iRight) {
19454             this.padding = [iTop, iTop, iTop, iTop];
19455         } else if (!iBot && 0 !== iBot) {
19456             this.padding = [iTop, iRight, iTop, iRight];
19457         } else {
19458             this.padding = [iTop, iRight, iBot, iLeft];
19459         }
19460     },
19461
19462     /**
19463      * Stores the initial placement of the linked element.
19464      * @method setInitialPosition
19465      * @param {int} diffX   the X offset, default 0
19466      * @param {int} diffY   the Y offset, default 0
19467      */
19468     setInitPosition: function(diffX, diffY) {
19469         var el = this.getEl();
19470
19471         if (!this.DDM.verifyEl(el)) {
19472             return;
19473         }
19474
19475         var dx = diffX || 0;
19476         var dy = diffY || 0;
19477
19478         var p = Dom.getXY( el );
19479
19480         this.initPageX = p[0] - dx;
19481         this.initPageY = p[1] - dy;
19482
19483         this.lastPageX = p[0];
19484         this.lastPageY = p[1];
19485
19486
19487         this.setStartPosition(p);
19488     },
19489
19490     /**
19491      * Sets the start position of the element.  This is set when the obj
19492      * is initialized, the reset when a drag is started.
19493      * @method setStartPosition
19494      * @param pos current position (from previous lookup)
19495      * @private
19496      */
19497     setStartPosition: function(pos) {
19498         var p = pos || Dom.getXY( this.getEl() );
19499         this.deltaSetXY = null;
19500
19501         this.startPageX = p[0];
19502         this.startPageY = p[1];
19503     },
19504
19505     /**
19506      * Add this instance to a group of related drag/drop objects.  All
19507      * instances belong to at least one group, and can belong to as many
19508      * groups as needed.
19509      * @method addToGroup
19510      * @param sGroup {string} the name of the group
19511      */
19512     addToGroup: function(sGroup) {
19513         this.groups[sGroup] = true;
19514         this.DDM.regDragDrop(this, sGroup);
19515     },
19516
19517     /**
19518      * Remove's this instance from the supplied interaction group
19519      * @method removeFromGroup
19520      * @param {string}  sGroup  The group to drop
19521      */
19522     removeFromGroup: function(sGroup) {
19523         if (this.groups[sGroup]) {
19524             delete this.groups[sGroup];
19525         }
19526
19527         this.DDM.removeDDFromGroup(this, sGroup);
19528     },
19529
19530     /**
19531      * Allows you to specify that an element other than the linked element
19532      * will be moved with the cursor during a drag
19533      * @method setDragElId
19534      * @param id {string} the id of the element that will be used to initiate the drag
19535      */
19536     setDragElId: function(id) {
19537         this.dragElId = id;
19538     },
19539
19540     /**
19541      * Allows you to specify a child of the linked element that should be
19542      * used to initiate the drag operation.  An example of this would be if
19543      * you have a content div with text and links.  Clicking anywhere in the
19544      * content area would normally start the drag operation.  Use this method
19545      * to specify that an element inside of the content div is the element
19546      * that starts the drag operation.
19547      * @method setHandleElId
19548      * @param id {string} the id of the element that will be used to
19549      * initiate the drag.
19550      */
19551     setHandleElId: function(id) {
19552         if (typeof id !== "string") {
19553             id = Roo.id(id);
19554         }
19555         this.handleElId = id;
19556         this.DDM.regHandle(this.id, id);
19557     },
19558
19559     /**
19560      * Allows you to set an element outside of the linked element as a drag
19561      * handle
19562      * @method setOuterHandleElId
19563      * @param id the id of the element that will be used to initiate the drag
19564      */
19565     setOuterHandleElId: function(id) {
19566         if (typeof id !== "string") {
19567             id = Roo.id(id);
19568         }
19569         Event.on(id, "mousedown",
19570                 this.handleMouseDown, this);
19571         this.setHandleElId(id);
19572
19573         this.hasOuterHandles = true;
19574     },
19575
19576     /**
19577      * Remove all drag and drop hooks for this element
19578      * @method unreg
19579      */
19580     unreg: function() {
19581         Event.un(this.id, "mousedown",
19582                 this.handleMouseDown);
19583         Event.un(this.id, "touchstart",
19584                 this.handleMouseDown);
19585         this._domRef = null;
19586         this.DDM._remove(this);
19587     },
19588
19589     destroy : function(){
19590         this.unreg();
19591     },
19592
19593     /**
19594      * Returns true if this instance is locked, or the drag drop mgr is locked
19595      * (meaning that all drag/drop is disabled on the page.)
19596      * @method isLocked
19597      * @return {boolean} true if this obj or all drag/drop is locked, else
19598      * false
19599      */
19600     isLocked: function() {
19601         return (this.DDM.isLocked() || this.locked);
19602     },
19603
19604     /**
19605      * Fired when this object is clicked
19606      * @method handleMouseDown
19607      * @param {Event} e
19608      * @param {Roo.dd.DragDrop} oDD the clicked dd object (this dd obj)
19609      * @private
19610      */
19611     handleMouseDown: function(e, oDD){
19612      
19613         if (!Roo.isTouch && this.primaryButtonOnly && e.button != 0) {
19614             //Roo.log('not touch/ button !=0');
19615             return;
19616         }
19617         if (e.browserEvent.touches && e.browserEvent.touches.length != 1) {
19618             return; // double touch..
19619         }
19620         
19621
19622         if (this.isLocked()) {
19623             //Roo.log('locked');
19624             return;
19625         }
19626
19627         this.DDM.refreshCache(this.groups);
19628 //        Roo.log([Roo.lib.Event.getPageX(e), Roo.lib.Event.getPageY(e)]);
19629         var pt = new Roo.lib.Point(Roo.lib.Event.getPageX(e), Roo.lib.Event.getPageY(e));
19630         if (!this.hasOuterHandles && !this.DDM.isOverTarget(pt, this) )  {
19631             //Roo.log('no outer handes or not over target');
19632                 // do nothing.
19633         } else {
19634 //            Roo.log('check validator');
19635             if (this.clickValidator(e)) {
19636 //                Roo.log('validate success');
19637                 // set the initial element position
19638                 this.setStartPosition();
19639
19640
19641                 this.b4MouseDown(e);
19642                 this.onMouseDown(e);
19643
19644                 this.DDM.handleMouseDown(e, this);
19645
19646                 this.DDM.stopEvent(e);
19647             } else {
19648
19649
19650             }
19651         }
19652     },
19653
19654     clickValidator: function(e) {
19655         var target = e.getTarget();
19656         return ( this.isValidHandleChild(target) &&
19657                     (this.id == this.handleElId ||
19658                         this.DDM.handleWasClicked(target, this.id)) );
19659     },
19660
19661     /**
19662      * Allows you to specify a tag name that should not start a drag operation
19663      * when clicked.  This is designed to facilitate embedding links within a
19664      * drag handle that do something other than start the drag.
19665      * @method addInvalidHandleType
19666      * @param {string} tagName the type of element to exclude
19667      */
19668     addInvalidHandleType: function(tagName) {
19669         var type = tagName.toUpperCase();
19670         this.invalidHandleTypes[type] = type;
19671     },
19672
19673     /**
19674      * Lets you to specify an element id for a child of a drag handle
19675      * that should not initiate a drag
19676      * @method addInvalidHandleId
19677      * @param {string} id the element id of the element you wish to ignore
19678      */
19679     addInvalidHandleId: function(id) {
19680         if (typeof id !== "string") {
19681             id = Roo.id(id);
19682         }
19683         this.invalidHandleIds[id] = id;
19684     },
19685
19686     /**
19687      * Lets you specify a css class of elements that will not initiate a drag
19688      * @method addInvalidHandleClass
19689      * @param {string} cssClass the class of the elements you wish to ignore
19690      */
19691     addInvalidHandleClass: function(cssClass) {
19692         this.invalidHandleClasses.push(cssClass);
19693     },
19694
19695     /**
19696      * Unsets an excluded tag name set by addInvalidHandleType
19697      * @method removeInvalidHandleType
19698      * @param {string} tagName the type of element to unexclude
19699      */
19700     removeInvalidHandleType: function(tagName) {
19701         var type = tagName.toUpperCase();
19702         // this.invalidHandleTypes[type] = null;
19703         delete this.invalidHandleTypes[type];
19704     },
19705
19706     /**
19707      * Unsets an invalid handle id
19708      * @method removeInvalidHandleId
19709      * @param {string} id the id of the element to re-enable
19710      */
19711     removeInvalidHandleId: function(id) {
19712         if (typeof id !== "string") {
19713             id = Roo.id(id);
19714         }
19715         delete this.invalidHandleIds[id];
19716     },
19717
19718     /**
19719      * Unsets an invalid css class
19720      * @method removeInvalidHandleClass
19721      * @param {string} cssClass the class of the element(s) you wish to
19722      * re-enable
19723      */
19724     removeInvalidHandleClass: function(cssClass) {
19725         for (var i=0, len=this.invalidHandleClasses.length; i<len; ++i) {
19726             if (this.invalidHandleClasses[i] == cssClass) {
19727                 delete this.invalidHandleClasses[i];
19728             }
19729         }
19730     },
19731
19732     /**
19733      * Checks the tag exclusion list to see if this click should be ignored
19734      * @method isValidHandleChild
19735      * @param {HTMLElement} node the HTMLElement to evaluate
19736      * @return {boolean} true if this is a valid tag type, false if not
19737      */
19738     isValidHandleChild: function(node) {
19739
19740         var valid = true;
19741         // var n = (node.nodeName == "#text") ? node.parentNode : node;
19742         var nodeName;
19743         try {
19744             nodeName = node.nodeName.toUpperCase();
19745         } catch(e) {
19746             nodeName = node.nodeName;
19747         }
19748         valid = valid && !this.invalidHandleTypes[nodeName];
19749         valid = valid && !this.invalidHandleIds[node.id];
19750
19751         for (var i=0, len=this.invalidHandleClasses.length; valid && i<len; ++i) {
19752             valid = !Dom.hasClass(node, this.invalidHandleClasses[i]);
19753         }
19754
19755
19756         return valid;
19757
19758     },
19759
19760     /**
19761      * Create the array of horizontal tick marks if an interval was specified
19762      * in setXConstraint().
19763      * @method setXTicks
19764      * @private
19765      */
19766     setXTicks: function(iStartX, iTickSize) {
19767         this.xTicks = [];
19768         this.xTickSize = iTickSize;
19769
19770         var tickMap = {};
19771
19772         for (var i = this.initPageX; i >= this.minX; i = i - iTickSize) {
19773             if (!tickMap[i]) {
19774                 this.xTicks[this.xTicks.length] = i;
19775                 tickMap[i] = true;
19776             }
19777         }
19778
19779         for (i = this.initPageX; i <= this.maxX; i = i + iTickSize) {
19780             if (!tickMap[i]) {
19781                 this.xTicks[this.xTicks.length] = i;
19782                 tickMap[i] = true;
19783             }
19784         }
19785
19786         this.xTicks.sort(this.DDM.numericSort) ;
19787     },
19788
19789     /**
19790      * Create the array of vertical tick marks if an interval was specified in
19791      * setYConstraint().
19792      * @method setYTicks
19793      * @private
19794      */
19795     setYTicks: function(iStartY, iTickSize) {
19796         this.yTicks = [];
19797         this.yTickSize = iTickSize;
19798
19799         var tickMap = {};
19800
19801         for (var i = this.initPageY; i >= this.minY; i = i - iTickSize) {
19802             if (!tickMap[i]) {
19803                 this.yTicks[this.yTicks.length] = i;
19804                 tickMap[i] = true;
19805             }
19806         }
19807
19808         for (i = this.initPageY; i <= this.maxY; i = i + iTickSize) {
19809             if (!tickMap[i]) {
19810                 this.yTicks[this.yTicks.length] = i;
19811                 tickMap[i] = true;
19812             }
19813         }
19814
19815         this.yTicks.sort(this.DDM.numericSort) ;
19816     },
19817
19818     /**
19819      * By default, the element can be dragged any place on the screen.  Use
19820      * this method to limit the horizontal travel of the element.  Pass in
19821      * 0,0 for the parameters if you want to lock the drag to the y axis.
19822      * @method setXConstraint
19823      * @param {int} iLeft the number of pixels the element can move to the left
19824      * @param {int} iRight the number of pixels the element can move to the
19825      * right
19826      * @param {int} iTickSize optional parameter for specifying that the
19827      * element
19828      * should move iTickSize pixels at a time.
19829      */
19830     setXConstraint: function(iLeft, iRight, iTickSize) {
19831         this.leftConstraint = iLeft;
19832         this.rightConstraint = iRight;
19833
19834         this.minX = this.initPageX - iLeft;
19835         this.maxX = this.initPageX + iRight;
19836         if (iTickSize) { this.setXTicks(this.initPageX, iTickSize); }
19837
19838         this.constrainX = true;
19839     },
19840
19841     /**
19842      * Clears any constraints applied to this instance.  Also clears ticks
19843      * since they can't exist independent of a constraint at this time.
19844      * @method clearConstraints
19845      */
19846     clearConstraints: function() {
19847         this.constrainX = false;
19848         this.constrainY = false;
19849         this.clearTicks();
19850     },
19851
19852     /**
19853      * Clears any tick interval defined for this instance
19854      * @method clearTicks
19855      */
19856     clearTicks: function() {
19857         this.xTicks = null;
19858         this.yTicks = null;
19859         this.xTickSize = 0;
19860         this.yTickSize = 0;
19861     },
19862
19863     /**
19864      * By default, the element can be dragged any place on the screen.  Set
19865      * this to limit the vertical travel of the element.  Pass in 0,0 for the
19866      * parameters if you want to lock the drag to the x axis.
19867      * @method setYConstraint
19868      * @param {int} iUp the number of pixels the element can move up
19869      * @param {int} iDown the number of pixels the element can move down
19870      * @param {int} iTickSize optional parameter for specifying that the
19871      * element should move iTickSize pixels at a time.
19872      */
19873     setYConstraint: function(iUp, iDown, iTickSize) {
19874         this.topConstraint = iUp;
19875         this.bottomConstraint = iDown;
19876
19877         this.minY = this.initPageY - iUp;
19878         this.maxY = this.initPageY + iDown;
19879         if (iTickSize) { this.setYTicks(this.initPageY, iTickSize); }
19880
19881         this.constrainY = true;
19882
19883     },
19884
19885     /**
19886      * resetConstraints must be called if you manually reposition a dd element.
19887      * @method resetConstraints
19888      * @param {boolean} maintainOffset
19889      */
19890     resetConstraints: function() {
19891
19892
19893         // Maintain offsets if necessary
19894         if (this.initPageX || this.initPageX === 0) {
19895             // figure out how much this thing has moved
19896             var dx = (this.maintainOffset) ? this.lastPageX - this.initPageX : 0;
19897             var dy = (this.maintainOffset) ? this.lastPageY - this.initPageY : 0;
19898
19899             this.setInitPosition(dx, dy);
19900
19901         // This is the first time we have detected the element's position
19902         } else {
19903             this.setInitPosition();
19904         }
19905
19906         if (this.constrainX) {
19907             this.setXConstraint( this.leftConstraint,
19908                                  this.rightConstraint,
19909                                  this.xTickSize        );
19910         }
19911
19912         if (this.constrainY) {
19913             this.setYConstraint( this.topConstraint,
19914                                  this.bottomConstraint,
19915                                  this.yTickSize         );
19916         }
19917     },
19918
19919     /**
19920      * Normally the drag element is moved pixel by pixel, but we can specify
19921      * that it move a number of pixels at a time.  This method resolves the
19922      * location when we have it set up like this.
19923      * @method getTick
19924      * @param {int} val where we want to place the object
19925      * @param {int[]} tickArray sorted array of valid points
19926      * @return {int} the closest tick
19927      * @private
19928      */
19929     getTick: function(val, tickArray) {
19930
19931         if (!tickArray) {
19932             // If tick interval is not defined, it is effectively 1 pixel,
19933             // so we return the value passed to us.
19934             return val;
19935         } else if (tickArray[0] >= val) {
19936             // The value is lower than the first tick, so we return the first
19937             // tick.
19938             return tickArray[0];
19939         } else {
19940             for (var i=0, len=tickArray.length; i<len; ++i) {
19941                 var next = i + 1;
19942                 if (tickArray[next] && tickArray[next] >= val) {
19943                     var diff1 = val - tickArray[i];
19944                     var diff2 = tickArray[next] - val;
19945                     return (diff2 > diff1) ? tickArray[i] : tickArray[next];
19946                 }
19947             }
19948
19949             // The value is larger than the last tick, so we return the last
19950             // tick.
19951             return tickArray[tickArray.length - 1];
19952         }
19953     },
19954
19955     /**
19956      * toString method
19957      * @method toString
19958      * @return {string} string representation of the dd obj
19959      */
19960     toString: function() {
19961         return ("DragDrop " + this.id);
19962     }
19963
19964 });
19965
19966 })();
19967 /*
19968  * Based on:
19969  * Ext JS Library 1.1.1
19970  * Copyright(c) 2006-2007, Ext JS, LLC.
19971  *
19972  * Originally Released Under LGPL - original licence link has changed is not relivant.
19973  *
19974  * Fork - LGPL
19975  * <script type="text/javascript">
19976  */
19977
19978
19979 /**
19980  * The drag and drop utility provides a framework for building drag and drop
19981  * applications.  In addition to enabling drag and drop for specific elements,
19982  * the drag and drop elements are tracked by the manager class, and the
19983  * interactions between the various elements are tracked during the drag and
19984  * the implementing code is notified about these important moments.
19985  */
19986
19987 // Only load the library once.  Rewriting the manager class would orphan
19988 // existing drag and drop instances.
19989 if (!Roo.dd.DragDropMgr) {
19990
19991 /**
19992  * @class Roo.dd.DragDropMgr
19993  * DragDropMgr is a singleton that tracks the element interaction for
19994  * all DragDrop items in the window.  Generally, you will not call
19995  * this class directly, but it does have helper methods that could
19996  * be useful in your DragDrop implementations.
19997  * @singleton
19998  */
19999 Roo.dd.DragDropMgr = function() {
20000
20001     var Event = Roo.EventManager;
20002
20003     return {
20004
20005         /**
20006          * Two dimensional Array of registered DragDrop objects.  The first
20007          * dimension is the DragDrop item group, the second the DragDrop
20008          * object.
20009          * @property ids
20010          * @type {string: string}
20011          * @private
20012          * @static
20013          */
20014         ids: {},
20015
20016         /**
20017          * Array of element ids defined as drag handles.  Used to determine
20018          * if the element that generated the mousedown event is actually the
20019          * handle and not the html element itself.
20020          * @property handleIds
20021          * @type {string: string}
20022          * @private
20023          * @static
20024          */
20025         handleIds: {},
20026
20027         /**
20028          * the DragDrop object that is currently being dragged
20029          * @property dragCurrent
20030          * @type DragDrop
20031          * @private
20032          * @static
20033          **/
20034         dragCurrent: null,
20035
20036         /**
20037          * the DragDrop object(s) that are being hovered over
20038          * @property dragOvers
20039          * @type Array
20040          * @private
20041          * @static
20042          */
20043         dragOvers: {},
20044
20045         /**
20046          * the X distance between the cursor and the object being dragged
20047          * @property deltaX
20048          * @type int
20049          * @private
20050          * @static
20051          */
20052         deltaX: 0,
20053
20054         /**
20055          * the Y distance between the cursor and the object being dragged
20056          * @property deltaY
20057          * @type int
20058          * @private
20059          * @static
20060          */
20061         deltaY: 0,
20062
20063         /**
20064          * Flag to determine if we should prevent the default behavior of the
20065          * events we define. By default this is true, but this can be set to
20066          * false if you need the default behavior (not recommended)
20067          * @property preventDefault
20068          * @type boolean
20069          * @static
20070          */
20071         preventDefault: true,
20072
20073         /**
20074          * Flag to determine if we should stop the propagation of the events
20075          * we generate. This is true by default but you may want to set it to
20076          * false if the html element contains other features that require the
20077          * mouse click.
20078          * @property stopPropagation
20079          * @type boolean
20080          * @static
20081          */
20082         stopPropagation: true,
20083
20084         /**
20085          * Internal flag that is set to true when drag and drop has been
20086          * intialized
20087          * @property initialized
20088          * @private
20089          * @static
20090          */
20091         initalized: false,
20092
20093         /**
20094          * All drag and drop can be disabled.
20095          * @property locked
20096          * @private
20097          * @static
20098          */
20099         locked: false,
20100
20101         /**
20102          * Called the first time an element is registered.
20103          * @method init
20104          * @private
20105          * @static
20106          */
20107         init: function() {
20108             this.initialized = true;
20109         },
20110
20111         /**
20112          * In point mode, drag and drop interaction is defined by the
20113          * location of the cursor during the drag/drop
20114          * @property POINT
20115          * @type int
20116          * @static
20117          */
20118         POINT: 0,
20119
20120         /**
20121          * In intersect mode, drag and drop interactio nis defined by the
20122          * overlap of two or more drag and drop objects.
20123          * @property INTERSECT
20124          * @type int
20125          * @static
20126          */
20127         INTERSECT: 1,
20128
20129         /**
20130          * The current drag and drop mode.  Default: POINT
20131          * @property mode
20132          * @type int
20133          * @static
20134          */
20135         mode: 0,
20136
20137         /**
20138          * Runs method on all drag and drop objects
20139          * @method _execOnAll
20140          * @private
20141          * @static
20142          */
20143         _execOnAll: function(sMethod, args) {
20144             for (var i in this.ids) {
20145                 for (var j in this.ids[i]) {
20146                     var oDD = this.ids[i][j];
20147                     if (! this.isTypeOfDD(oDD)) {
20148                         continue;
20149                     }
20150                     oDD[sMethod].apply(oDD, args);
20151                 }
20152             }
20153         },
20154
20155         /**
20156          * Drag and drop initialization.  Sets up the global event handlers
20157          * @method _onLoad
20158          * @private
20159          * @static
20160          */
20161         _onLoad: function() {
20162
20163             this.init();
20164
20165             if (!Roo.isTouch) {
20166                 Event.on(document, "mouseup",   this.handleMouseUp, this, true);
20167                 Event.on(document, "mousemove", this.handleMouseMove, this, true);
20168             }
20169             Event.on(document, "touchend",   this.handleMouseUp, this, true);
20170             Event.on(document, "touchmove", this.handleMouseMove, this, true);
20171             
20172             Event.on(window,   "unload",    this._onUnload, this, true);
20173             Event.on(window,   "resize",    this._onResize, this, true);
20174             // Event.on(window,   "mouseout",    this._test);
20175
20176         },
20177
20178         /**
20179          * Reset constraints on all drag and drop objs
20180          * @method _onResize
20181          * @private
20182          * @static
20183          */
20184         _onResize: function(e) {
20185             this._execOnAll("resetConstraints", []);
20186         },
20187
20188         /**
20189          * Lock all drag and drop functionality
20190          * @method lock
20191          * @static
20192          */
20193         lock: function() { this.locked = true; },
20194
20195         /**
20196          * Unlock all drag and drop functionality
20197          * @method unlock
20198          * @static
20199          */
20200         unlock: function() { this.locked = false; },
20201
20202         /**
20203          * Is drag and drop locked?
20204          * @method isLocked
20205          * @return {boolean} True if drag and drop is locked, false otherwise.
20206          * @static
20207          */
20208         isLocked: function() { return this.locked; },
20209
20210         /**
20211          * Location cache that is set for all drag drop objects when a drag is
20212          * initiated, cleared when the drag is finished.
20213          * @property locationCache
20214          * @private
20215          * @static
20216          */
20217         locationCache: {},
20218
20219         /**
20220          * Set useCache to false if you want to force object the lookup of each
20221          * drag and drop linked element constantly during a drag.
20222          * @property useCache
20223          * @type boolean
20224          * @static
20225          */
20226         useCache: true,
20227
20228         /**
20229          * The number of pixels that the mouse needs to move after the
20230          * mousedown before the drag is initiated.  Default=3;
20231          * @property clickPixelThresh
20232          * @type int
20233          * @static
20234          */
20235         clickPixelThresh: 3,
20236
20237         /**
20238          * The number of milliseconds after the mousedown event to initiate the
20239          * drag if we don't get a mouseup event. Default=1000
20240          * @property clickTimeThresh
20241          * @type int
20242          * @static
20243          */
20244         clickTimeThresh: 350,
20245
20246         /**
20247          * Flag that indicates that either the drag pixel threshold or the
20248          * mousdown time threshold has been met
20249          * @property dragThreshMet
20250          * @type boolean
20251          * @private
20252          * @static
20253          */
20254         dragThreshMet: false,
20255
20256         /**
20257          * Timeout used for the click time threshold
20258          * @property clickTimeout
20259          * @type Object
20260          * @private
20261          * @static
20262          */
20263         clickTimeout: null,
20264
20265         /**
20266          * The X position of the mousedown event stored for later use when a
20267          * drag threshold is met.
20268          * @property startX
20269          * @type int
20270          * @private
20271          * @static
20272          */
20273         startX: 0,
20274
20275         /**
20276          * The Y position of the mousedown event stored for later use when a
20277          * drag threshold is met.
20278          * @property startY
20279          * @type int
20280          * @private
20281          * @static
20282          */
20283         startY: 0,
20284
20285         /**
20286          * Each DragDrop instance must be registered with the DragDropMgr.
20287          * This is executed in DragDrop.init()
20288          * @method regDragDrop
20289          * @param {DragDrop} oDD the DragDrop object to register
20290          * @param {String} sGroup the name of the group this element belongs to
20291          * @static
20292          */
20293         regDragDrop: function(oDD, sGroup) {
20294             if (!this.initialized) { this.init(); }
20295
20296             if (!this.ids[sGroup]) {
20297                 this.ids[sGroup] = {};
20298             }
20299             this.ids[sGroup][oDD.id] = oDD;
20300         },
20301
20302         /**
20303          * Removes the supplied dd instance from the supplied group. Executed
20304          * by DragDrop.removeFromGroup, so don't call this function directly.
20305          * @method removeDDFromGroup
20306          * @private
20307          * @static
20308          */
20309         removeDDFromGroup: function(oDD, sGroup) {
20310             if (!this.ids[sGroup]) {
20311                 this.ids[sGroup] = {};
20312             }
20313
20314             var obj = this.ids[sGroup];
20315             if (obj && obj[oDD.id]) {
20316                 delete obj[oDD.id];
20317             }
20318         },
20319
20320         /**
20321          * Unregisters a drag and drop item.  This is executed in
20322          * DragDrop.unreg, use that method instead of calling this directly.
20323          * @method _remove
20324          * @private
20325          * @static
20326          */
20327         _remove: function(oDD) {
20328             for (var g in oDD.groups) {
20329                 if (g && this.ids[g][oDD.id]) {
20330                     delete this.ids[g][oDD.id];
20331                 }
20332             }
20333             delete this.handleIds[oDD.id];
20334         },
20335
20336         /**
20337          * Each DragDrop handle element must be registered.  This is done
20338          * automatically when executing DragDrop.setHandleElId()
20339          * @method regHandle
20340          * @param {String} sDDId the DragDrop id this element is a handle for
20341          * @param {String} sHandleId the id of the element that is the drag
20342          * handle
20343          * @static
20344          */
20345         regHandle: function(sDDId, sHandleId) {
20346             if (!this.handleIds[sDDId]) {
20347                 this.handleIds[sDDId] = {};
20348             }
20349             this.handleIds[sDDId][sHandleId] = sHandleId;
20350         },
20351
20352         /**
20353          * Utility function to determine if a given element has been
20354          * registered as a drag drop item.
20355          * @method isDragDrop
20356          * @param {String} id the element id to check
20357          * @return {boolean} true if this element is a DragDrop item,
20358          * false otherwise
20359          * @static
20360          */
20361         isDragDrop: function(id) {
20362             return ( this.getDDById(id) ) ? true : false;
20363         },
20364
20365         /**
20366          * Returns the drag and drop instances that are in all groups the
20367          * passed in instance belongs to.
20368          * @method getRelated
20369          * @param {DragDrop} p_oDD the obj to get related data for
20370          * @param {boolean} bTargetsOnly if true, only return targetable objs
20371          * @return {DragDrop[]} the related instances
20372          * @static
20373          */
20374         getRelated: function(p_oDD, bTargetsOnly) {
20375             var oDDs = [];
20376             for (var i in p_oDD.groups) {
20377                 for (j in this.ids[i]) {
20378                     var dd = this.ids[i][j];
20379                     if (! this.isTypeOfDD(dd)) {
20380                         continue;
20381                     }
20382                     if (!bTargetsOnly || dd.isTarget) {
20383                         oDDs[oDDs.length] = dd;
20384                     }
20385                 }
20386             }
20387
20388             return oDDs;
20389         },
20390
20391         /**
20392          * Returns true if the specified dd target is a legal target for
20393          * the specifice drag obj
20394          * @method isLegalTarget
20395          * @param {DragDrop} the drag obj
20396          * @param {DragDrop} the target
20397          * @return {boolean} true if the target is a legal target for the
20398          * dd obj
20399          * @static
20400          */
20401         isLegalTarget: function (oDD, oTargetDD) {
20402             var targets = this.getRelated(oDD, true);
20403             for (var i=0, len=targets.length;i<len;++i) {
20404                 if (targets[i].id == oTargetDD.id) {
20405                     return true;
20406                 }
20407             }
20408
20409             return false;
20410         },
20411
20412         /**
20413          * My goal is to be able to transparently determine if an object is
20414          * typeof DragDrop, and the exact subclass of DragDrop.  typeof
20415          * returns "object", oDD.constructor.toString() always returns
20416          * "DragDrop" and not the name of the subclass.  So for now it just
20417          * evaluates a well-known variable in DragDrop.
20418          * @method isTypeOfDD
20419          * @param {Object} the object to evaluate
20420          * @return {boolean} true if typeof oDD = DragDrop
20421          * @static
20422          */
20423         isTypeOfDD: function (oDD) {
20424             return (oDD && oDD.__ygDragDrop);
20425         },
20426
20427         /**
20428          * Utility function to determine if a given element has been
20429          * registered as a drag drop handle for the given Drag Drop object.
20430          * @method isHandle
20431          * @param {String} id the element id to check
20432          * @return {boolean} true if this element is a DragDrop handle, false
20433          * otherwise
20434          * @static
20435          */
20436         isHandle: function(sDDId, sHandleId) {
20437             return ( this.handleIds[sDDId] &&
20438                             this.handleIds[sDDId][sHandleId] );
20439         },
20440
20441         /**
20442          * Returns the DragDrop instance for a given id
20443          * @method getDDById
20444          * @param {String} id the id of the DragDrop object
20445          * @return {DragDrop} the drag drop object, null if it is not found
20446          * @static
20447          */
20448         getDDById: function(id) {
20449             for (var i in this.ids) {
20450                 if (this.ids[i][id]) {
20451                     return this.ids[i][id];
20452                 }
20453             }
20454             return null;
20455         },
20456
20457         /**
20458          * Fired after a registered DragDrop object gets the mousedown event.
20459          * Sets up the events required to track the object being dragged
20460          * @method handleMouseDown
20461          * @param {Event} e the event
20462          * @param oDD the DragDrop object being dragged
20463          * @private
20464          * @static
20465          */
20466         handleMouseDown: function(e, oDD) {
20467             if(Roo.QuickTips){
20468                 Roo.QuickTips.disable();
20469             }
20470             this.currentTarget = e.getTarget();
20471
20472             this.dragCurrent = oDD;
20473
20474             var el = oDD.getEl();
20475
20476             // track start position
20477             this.startX = e.getPageX();
20478             this.startY = e.getPageY();
20479
20480             this.deltaX = this.startX - el.offsetLeft;
20481             this.deltaY = this.startY - el.offsetTop;
20482
20483             this.dragThreshMet = false;
20484
20485             this.clickTimeout = setTimeout(
20486                     function() {
20487                         var DDM = Roo.dd.DDM;
20488                         DDM.startDrag(DDM.startX, DDM.startY);
20489                     },
20490                     this.clickTimeThresh );
20491         },
20492
20493         /**
20494          * Fired when either the drag pixel threshol or the mousedown hold
20495          * time threshold has been met.
20496          * @method startDrag
20497          * @param x {int} the X position of the original mousedown
20498          * @param y {int} the Y position of the original mousedown
20499          * @static
20500          */
20501         startDrag: function(x, y) {
20502             clearTimeout(this.clickTimeout);
20503             if (this.dragCurrent) {
20504                 this.dragCurrent.b4StartDrag(x, y);
20505                 this.dragCurrent.startDrag(x, y);
20506             }
20507             this.dragThreshMet = true;
20508         },
20509
20510         /**
20511          * Internal function to handle the mouseup event.  Will be invoked
20512          * from the context of the document.
20513          * @method handleMouseUp
20514          * @param {Event} e the event
20515          * @private
20516          * @static
20517          */
20518         handleMouseUp: function(e) {
20519
20520             if(Roo.QuickTips){
20521                 Roo.QuickTips.enable();
20522             }
20523             if (! this.dragCurrent) {
20524                 return;
20525             }
20526
20527             clearTimeout(this.clickTimeout);
20528
20529             if (this.dragThreshMet) {
20530                 this.fireEvents(e, true);
20531             } else {
20532             }
20533
20534             this.stopDrag(e);
20535
20536             this.stopEvent(e);
20537         },
20538
20539         /**
20540          * Utility to stop event propagation and event default, if these
20541          * features are turned on.
20542          * @method stopEvent
20543          * @param {Event} e the event as returned by this.getEvent()
20544          * @static
20545          */
20546         stopEvent: function(e){
20547             if(this.stopPropagation) {
20548                 e.stopPropagation();
20549             }
20550
20551             if (this.preventDefault) {
20552                 e.preventDefault();
20553             }
20554         },
20555
20556         /**
20557          * Internal function to clean up event handlers after the drag
20558          * operation is complete
20559          * @method stopDrag
20560          * @param {Event} e the event
20561          * @private
20562          * @static
20563          */
20564         stopDrag: function(e) {
20565             // Fire the drag end event for the item that was dragged
20566             if (this.dragCurrent) {
20567                 if (this.dragThreshMet) {
20568                     this.dragCurrent.b4EndDrag(e);
20569                     this.dragCurrent.endDrag(e);
20570                 }
20571
20572                 this.dragCurrent.onMouseUp(e);
20573             }
20574
20575             this.dragCurrent = null;
20576             this.dragOvers = {};
20577         },
20578
20579         /**
20580          * Internal function to handle the mousemove event.  Will be invoked
20581          * from the context of the html element.
20582          *
20583          * @TODO figure out what we can do about mouse events lost when the
20584          * user drags objects beyond the window boundary.  Currently we can
20585          * detect this in internet explorer by verifying that the mouse is
20586          * down during the mousemove event.  Firefox doesn't give us the
20587          * button state on the mousemove event.
20588          * @method handleMouseMove
20589          * @param {Event} e the event
20590          * @private
20591          * @static
20592          */
20593         handleMouseMove: function(e) {
20594             if (! this.dragCurrent) {
20595                 return true;
20596             }
20597
20598             // var button = e.which || e.button;
20599
20600             // check for IE mouseup outside of page boundary
20601             if (Roo.isIE && (e.button !== 0 && e.button !== 1 && e.button !== 2)) {
20602                 this.stopEvent(e);
20603                 return this.handleMouseUp(e);
20604             }
20605
20606             if (!this.dragThreshMet) {
20607                 var diffX = Math.abs(this.startX - e.getPageX());
20608                 var diffY = Math.abs(this.startY - e.getPageY());
20609                 if (diffX > this.clickPixelThresh ||
20610                             diffY > this.clickPixelThresh) {
20611                     this.startDrag(this.startX, this.startY);
20612                 }
20613             }
20614
20615             if (this.dragThreshMet) {
20616                 this.dragCurrent.b4Drag(e);
20617                 this.dragCurrent.onDrag(e);
20618                 if(!this.dragCurrent.moveOnly){
20619                     this.fireEvents(e, false);
20620                 }
20621             }
20622
20623             this.stopEvent(e);
20624
20625             return true;
20626         },
20627
20628         /**
20629          * Iterates over all of the DragDrop elements to find ones we are
20630          * hovering over or dropping on
20631          * @method fireEvents
20632          * @param {Event} e the event
20633          * @param {boolean} isDrop is this a drop op or a mouseover op?
20634          * @private
20635          * @static
20636          */
20637         fireEvents: function(e, isDrop) {
20638             var dc = this.dragCurrent;
20639
20640             // If the user did the mouse up outside of the window, we could
20641             // get here even though we have ended the drag.
20642             if (!dc || dc.isLocked()) {
20643                 return;
20644             }
20645
20646             var pt = e.getPoint();
20647
20648             // cache the previous dragOver array
20649             var oldOvers = [];
20650
20651             var outEvts   = [];
20652             var overEvts  = [];
20653             var dropEvts  = [];
20654             var enterEvts = [];
20655
20656             // Check to see if the object(s) we were hovering over is no longer
20657             // being hovered over so we can fire the onDragOut event
20658             for (var i in this.dragOvers) {
20659
20660                 var ddo = this.dragOvers[i];
20661
20662                 if (! this.isTypeOfDD(ddo)) {
20663                     continue;
20664                 }
20665
20666                 if (! this.isOverTarget(pt, ddo, this.mode)) {
20667                     outEvts.push( ddo );
20668                 }
20669
20670                 oldOvers[i] = true;
20671                 delete this.dragOvers[i];
20672             }
20673
20674             for (var sGroup in dc.groups) {
20675
20676                 if ("string" != typeof sGroup) {
20677                     continue;
20678                 }
20679
20680                 for (i in this.ids[sGroup]) {
20681                     var oDD = this.ids[sGroup][i];
20682                     if (! this.isTypeOfDD(oDD)) {
20683                         continue;
20684                     }
20685
20686                     if (oDD.isTarget && !oDD.isLocked() && oDD != dc) {
20687                         if (this.isOverTarget(pt, oDD, this.mode)) {
20688                             // look for drop interactions
20689                             if (isDrop) {
20690                                 dropEvts.push( oDD );
20691                             // look for drag enter and drag over interactions
20692                             } else {
20693
20694                                 // initial drag over: dragEnter fires
20695                                 if (!oldOvers[oDD.id]) {
20696                                     enterEvts.push( oDD );
20697                                 // subsequent drag overs: dragOver fires
20698                                 } else {
20699                                     overEvts.push( oDD );
20700                                 }
20701
20702                                 this.dragOvers[oDD.id] = oDD;
20703                             }
20704                         }
20705                     }
20706                 }
20707             }
20708
20709             if (this.mode) {
20710                 if (outEvts.length) {
20711                     dc.b4DragOut(e, outEvts);
20712                     dc.onDragOut(e, outEvts);
20713                 }
20714
20715                 if (enterEvts.length) {
20716                     dc.onDragEnter(e, enterEvts);
20717                 }
20718
20719                 if (overEvts.length) {
20720                     dc.b4DragOver(e, overEvts);
20721                     dc.onDragOver(e, overEvts);
20722                 }
20723
20724                 if (dropEvts.length) {
20725                     dc.b4DragDrop(e, dropEvts);
20726                     dc.onDragDrop(e, dropEvts);
20727                 }
20728
20729             } else {
20730                 // fire dragout events
20731                 var len = 0;
20732                 for (i=0, len=outEvts.length; i<len; ++i) {
20733                     dc.b4DragOut(e, outEvts[i].id);
20734                     dc.onDragOut(e, outEvts[i].id);
20735                 }
20736
20737                 // fire enter events
20738                 for (i=0,len=enterEvts.length; i<len; ++i) {
20739                     // dc.b4DragEnter(e, oDD.id);
20740                     dc.onDragEnter(e, enterEvts[i].id);
20741                 }
20742
20743                 // fire over events
20744                 for (i=0,len=overEvts.length; i<len; ++i) {
20745                     dc.b4DragOver(e, overEvts[i].id);
20746                     dc.onDragOver(e, overEvts[i].id);
20747                 }
20748
20749                 // fire drop events
20750                 for (i=0, len=dropEvts.length; i<len; ++i) {
20751                     dc.b4DragDrop(e, dropEvts[i].id);
20752                     dc.onDragDrop(e, dropEvts[i].id);
20753                 }
20754
20755             }
20756
20757             // notify about a drop that did not find a target
20758             if (isDrop && !dropEvts.length) {
20759                 dc.onInvalidDrop(e);
20760             }
20761
20762         },
20763
20764         /**
20765          * Helper function for getting the best match from the list of drag
20766          * and drop objects returned by the drag and drop events when we are
20767          * in INTERSECT mode.  It returns either the first object that the
20768          * cursor is over, or the object that has the greatest overlap with
20769          * the dragged element.
20770          * @method getBestMatch
20771          * @param  {DragDrop[]} dds The array of drag and drop objects
20772          * targeted
20773          * @return {DragDrop}       The best single match
20774          * @static
20775          */
20776         getBestMatch: function(dds) {
20777             var winner = null;
20778             // Return null if the input is not what we expect
20779             //if (!dds || !dds.length || dds.length == 0) {
20780                // winner = null;
20781             // If there is only one item, it wins
20782             //} else if (dds.length == 1) {
20783
20784             var len = dds.length;
20785
20786             if (len == 1) {
20787                 winner = dds[0];
20788             } else {
20789                 // Loop through the targeted items
20790                 for (var i=0; i<len; ++i) {
20791                     var dd = dds[i];
20792                     // If the cursor is over the object, it wins.  If the
20793                     // cursor is over multiple matches, the first one we come
20794                     // to wins.
20795                     if (dd.cursorIsOver) {
20796                         winner = dd;
20797                         break;
20798                     // Otherwise the object with the most overlap wins
20799                     } else {
20800                         if (!winner ||
20801                             winner.overlap.getArea() < dd.overlap.getArea()) {
20802                             winner = dd;
20803                         }
20804                     }
20805                 }
20806             }
20807
20808             return winner;
20809         },
20810
20811         /**
20812          * Refreshes the cache of the top-left and bottom-right points of the
20813          * drag and drop objects in the specified group(s).  This is in the
20814          * format that is stored in the drag and drop instance, so typical
20815          * usage is:
20816          * <code>
20817          * Roo.dd.DragDropMgr.refreshCache(ddinstance.groups);
20818          * </code>
20819          * Alternatively:
20820          * <code>
20821          * Roo.dd.DragDropMgr.refreshCache({group1:true, group2:true});
20822          * </code>
20823          * @TODO this really should be an indexed array.  Alternatively this
20824          * method could accept both.
20825          * @method refreshCache
20826          * @param {Object} groups an associative array of groups to refresh
20827          * @static
20828          */
20829         refreshCache: function(groups) {
20830             for (var sGroup in groups) {
20831                 if ("string" != typeof sGroup) {
20832                     continue;
20833                 }
20834                 for (var i in this.ids[sGroup]) {
20835                     var oDD = this.ids[sGroup][i];
20836
20837                     if (this.isTypeOfDD(oDD)) {
20838                     // if (this.isTypeOfDD(oDD) && oDD.isTarget) {
20839                         var loc = this.getLocation(oDD);
20840                         if (loc) {
20841                             this.locationCache[oDD.id] = loc;
20842                         } else {
20843                             delete this.locationCache[oDD.id];
20844                             // this will unregister the drag and drop object if
20845                             // the element is not in a usable state
20846                             // oDD.unreg();
20847                         }
20848                     }
20849                 }
20850             }
20851         },
20852
20853         /**
20854          * This checks to make sure an element exists and is in the DOM.  The
20855          * main purpose is to handle cases where innerHTML is used to remove
20856          * drag and drop objects from the DOM.  IE provides an 'unspecified
20857          * error' when trying to access the offsetParent of such an element
20858          * @method verifyEl
20859          * @param {HTMLElement} el the element to check
20860          * @return {boolean} true if the element looks usable
20861          * @static
20862          */
20863         verifyEl: function(el) {
20864             if (el) {
20865                 var parent;
20866                 if(Roo.isIE){
20867                     try{
20868                         parent = el.offsetParent;
20869                     }catch(e){}
20870                 }else{
20871                     parent = el.offsetParent;
20872                 }
20873                 if (parent) {
20874                     return true;
20875                 }
20876             }
20877
20878             return false;
20879         },
20880
20881         /**
20882          * Returns a Region object containing the drag and drop element's position
20883          * and size, including the padding configured for it
20884          * @method getLocation
20885          * @param {DragDrop} oDD the drag and drop object to get the
20886          *                       location for
20887          * @return {Roo.lib.Region} a Region object representing the total area
20888          *                             the element occupies, including any padding
20889          *                             the instance is configured for.
20890          * @static
20891          */
20892         getLocation: function(oDD) {
20893             if (! this.isTypeOfDD(oDD)) {
20894                 return null;
20895             }
20896
20897             var el = oDD.getEl(), pos, x1, x2, y1, y2, t, r, b, l;
20898
20899             try {
20900                 pos= Roo.lib.Dom.getXY(el);
20901             } catch (e) { }
20902
20903             if (!pos) {
20904                 return null;
20905             }
20906
20907             x1 = pos[0];
20908             x2 = x1 + el.offsetWidth;
20909             y1 = pos[1];
20910             y2 = y1 + el.offsetHeight;
20911
20912             t = y1 - oDD.padding[0];
20913             r = x2 + oDD.padding[1];
20914             b = y2 + oDD.padding[2];
20915             l = x1 - oDD.padding[3];
20916
20917             return new Roo.lib.Region( t, r, b, l );
20918         },
20919
20920         /**
20921          * Checks the cursor location to see if it over the target
20922          * @method isOverTarget
20923          * @param {Roo.lib.Point} pt The point to evaluate
20924          * @param {DragDrop} oTarget the DragDrop object we are inspecting
20925          * @return {boolean} true if the mouse is over the target
20926          * @private
20927          * @static
20928          */
20929         isOverTarget: function(pt, oTarget, intersect) {
20930             // use cache if available
20931             var loc = this.locationCache[oTarget.id];
20932             if (!loc || !this.useCache) {
20933                 loc = this.getLocation(oTarget);
20934                 this.locationCache[oTarget.id] = loc;
20935
20936             }
20937
20938             if (!loc) {
20939                 return false;
20940             }
20941
20942             oTarget.cursorIsOver = loc.contains( pt );
20943
20944             // DragDrop is using this as a sanity check for the initial mousedown
20945             // in this case we are done.  In POINT mode, if the drag obj has no
20946             // contraints, we are also done. Otherwise we need to evaluate the
20947             // location of the target as related to the actual location of the
20948             // dragged element.
20949             var dc = this.dragCurrent;
20950             if (!dc || !dc.getTargetCoord ||
20951                     (!intersect && !dc.constrainX && !dc.constrainY)) {
20952                 return oTarget.cursorIsOver;
20953             }
20954
20955             oTarget.overlap = null;
20956
20957             // Get the current location of the drag element, this is the
20958             // location of the mouse event less the delta that represents
20959             // where the original mousedown happened on the element.  We
20960             // need to consider constraints and ticks as well.
20961             var pos = dc.getTargetCoord(pt.x, pt.y);
20962
20963             var el = dc.getDragEl();
20964             var curRegion = new Roo.lib.Region( pos.y,
20965                                                    pos.x + el.offsetWidth,
20966                                                    pos.y + el.offsetHeight,
20967                                                    pos.x );
20968
20969             var overlap = curRegion.intersect(loc);
20970
20971             if (overlap) {
20972                 oTarget.overlap = overlap;
20973                 return (intersect) ? true : oTarget.cursorIsOver;
20974             } else {
20975                 return false;
20976             }
20977         },
20978
20979         /**
20980          * unload event handler
20981          * @method _onUnload
20982          * @private
20983          * @static
20984          */
20985         _onUnload: function(e, me) {
20986             Roo.dd.DragDropMgr.unregAll();
20987         },
20988
20989         /**
20990          * Cleans up the drag and drop events and objects.
20991          * @method unregAll
20992          * @private
20993          * @static
20994          */
20995         unregAll: function() {
20996
20997             if (this.dragCurrent) {
20998                 this.stopDrag();
20999                 this.dragCurrent = null;
21000             }
21001
21002             this._execOnAll("unreg", []);
21003
21004             for (i in this.elementCache) {
21005                 delete this.elementCache[i];
21006             }
21007
21008             this.elementCache = {};
21009             this.ids = {};
21010         },
21011
21012         /**
21013          * A cache of DOM elements
21014          * @property elementCache
21015          * @private
21016          * @static
21017          */
21018         elementCache: {},
21019
21020         /**
21021          * Get the wrapper for the DOM element specified
21022          * @method getElWrapper
21023          * @param {String} id the id of the element to get
21024          * @return {Roo.dd.DDM.ElementWrapper} the wrapped element
21025          * @private
21026          * @deprecated This wrapper isn't that useful
21027          * @static
21028          */
21029         getElWrapper: function(id) {
21030             var oWrapper = this.elementCache[id];
21031             if (!oWrapper || !oWrapper.el) {
21032                 oWrapper = this.elementCache[id] =
21033                     new this.ElementWrapper(Roo.getDom(id));
21034             }
21035             return oWrapper;
21036         },
21037
21038         /**
21039          * Returns the actual DOM element
21040          * @method getElement
21041          * @param {String} id the id of the elment to get
21042          * @return {Object} The element
21043          * @deprecated use Roo.getDom instead
21044          * @static
21045          */
21046         getElement: function(id) {
21047             return Roo.getDom(id);
21048         },
21049
21050         /**
21051          * Returns the style property for the DOM element (i.e.,
21052          * document.getElById(id).style)
21053          * @method getCss
21054          * @param {String} id the id of the elment to get
21055          * @return {Object} The style property of the element
21056          * @deprecated use Roo.getDom instead
21057          * @static
21058          */
21059         getCss: function(id) {
21060             var el = Roo.getDom(id);
21061             return (el) ? el.style : null;
21062         },
21063
21064         /**
21065          * Inner class for cached elements
21066          * @class DragDropMgr.ElementWrapper
21067          * @for DragDropMgr
21068          * @private
21069          * @deprecated
21070          */
21071         ElementWrapper: function(el) {
21072                 /**
21073                  * The element
21074                  * @property el
21075                  */
21076                 this.el = el || null;
21077                 /**
21078                  * The element id
21079                  * @property id
21080                  */
21081                 this.id = this.el && el.id;
21082                 /**
21083                  * A reference to the style property
21084                  * @property css
21085                  */
21086                 this.css = this.el && el.style;
21087             },
21088
21089         /**
21090          * Returns the X position of an html element
21091          * @method getPosX
21092          * @param el the element for which to get the position
21093          * @return {int} the X coordinate
21094          * @for DragDropMgr
21095          * @deprecated use Roo.lib.Dom.getX instead
21096          * @static
21097          */
21098         getPosX: function(el) {
21099             return Roo.lib.Dom.getX(el);
21100         },
21101
21102         /**
21103          * Returns the Y position of an html element
21104          * @method getPosY
21105          * @param el the element for which to get the position
21106          * @return {int} the Y coordinate
21107          * @deprecated use Roo.lib.Dom.getY instead
21108          * @static
21109          */
21110         getPosY: function(el) {
21111             return Roo.lib.Dom.getY(el);
21112         },
21113
21114         /**
21115          * Swap two nodes.  In IE, we use the native method, for others we
21116          * emulate the IE behavior
21117          * @method swapNode
21118          * @param n1 the first node to swap
21119          * @param n2 the other node to swap
21120          * @static
21121          */
21122         swapNode: function(n1, n2) {
21123             if (n1.swapNode) {
21124                 n1.swapNode(n2);
21125             } else {
21126                 var p = n2.parentNode;
21127                 var s = n2.nextSibling;
21128
21129                 if (s == n1) {
21130                     p.insertBefore(n1, n2);
21131                 } else if (n2 == n1.nextSibling) {
21132                     p.insertBefore(n2, n1);
21133                 } else {
21134                     n1.parentNode.replaceChild(n2, n1);
21135                     p.insertBefore(n1, s);
21136                 }
21137             }
21138         },
21139
21140         /**
21141          * Returns the current scroll position
21142          * @method getScroll
21143          * @private
21144          * @static
21145          */
21146         getScroll: function () {
21147             var t, l, dde=document.documentElement, db=document.body;
21148             if (dde && (dde.scrollTop || dde.scrollLeft)) {
21149                 t = dde.scrollTop;
21150                 l = dde.scrollLeft;
21151             } else if (db) {
21152                 t = db.scrollTop;
21153                 l = db.scrollLeft;
21154             } else {
21155
21156             }
21157             return { top: t, left: l };
21158         },
21159
21160         /**
21161          * Returns the specified element style property
21162          * @method getStyle
21163          * @param {HTMLElement} el          the element
21164          * @param {string}      styleProp   the style property
21165          * @return {string} The value of the style property
21166          * @deprecated use Roo.lib.Dom.getStyle
21167          * @static
21168          */
21169         getStyle: function(el, styleProp) {
21170             return Roo.fly(el).getStyle(styleProp);
21171         },
21172
21173         /**
21174          * Gets the scrollTop
21175          * @method getScrollTop
21176          * @return {int} the document's scrollTop
21177          * @static
21178          */
21179         getScrollTop: function () { return this.getScroll().top; },
21180
21181         /**
21182          * Gets the scrollLeft
21183          * @method getScrollLeft
21184          * @return {int} the document's scrollTop
21185          * @static
21186          */
21187         getScrollLeft: function () { return this.getScroll().left; },
21188
21189         /**
21190          * Sets the x/y position of an element to the location of the
21191          * target element.
21192          * @method moveToEl
21193          * @param {HTMLElement} moveEl      The element to move
21194          * @param {HTMLElement} targetEl    The position reference element
21195          * @static
21196          */
21197         moveToEl: function (moveEl, targetEl) {
21198             var aCoord = Roo.lib.Dom.getXY(targetEl);
21199             Roo.lib.Dom.setXY(moveEl, aCoord);
21200         },
21201
21202         /**
21203          * Numeric array sort function
21204          * @method numericSort
21205          * @static
21206          */
21207         numericSort: function(a, b) { return (a - b); },
21208
21209         /**
21210          * Internal counter
21211          * @property _timeoutCount
21212          * @private
21213          * @static
21214          */
21215         _timeoutCount: 0,
21216
21217         /**
21218          * Trying to make the load order less important.  Without this we get
21219          * an error if this file is loaded before the Event Utility.
21220          * @method _addListeners
21221          * @private
21222          * @static
21223          */
21224         _addListeners: function() {
21225             var DDM = Roo.dd.DDM;
21226             if ( Roo.lib.Event && document ) {
21227                 DDM._onLoad();
21228             } else {
21229                 if (DDM._timeoutCount > 2000) {
21230                 } else {
21231                     setTimeout(DDM._addListeners, 10);
21232                     if (document && document.body) {
21233                         DDM._timeoutCount += 1;
21234                     }
21235                 }
21236             }
21237         },
21238
21239         /**
21240          * Recursively searches the immediate parent and all child nodes for
21241          * the handle element in order to determine wheter or not it was
21242          * clicked.
21243          * @method handleWasClicked
21244          * @param node the html element to inspect
21245          * @static
21246          */
21247         handleWasClicked: function(node, id) {
21248             if (this.isHandle(id, node.id)) {
21249                 return true;
21250             } else {
21251                 // check to see if this is a text node child of the one we want
21252                 var p = node.parentNode;
21253
21254                 while (p) {
21255                     if (this.isHandle(id, p.id)) {
21256                         return true;
21257                     } else {
21258                         p = p.parentNode;
21259                     }
21260                 }
21261             }
21262
21263             return false;
21264         }
21265
21266     };
21267
21268 }();
21269
21270 // shorter alias, save a few bytes
21271 Roo.dd.DDM = Roo.dd.DragDropMgr;
21272 Roo.dd.DDM._addListeners();
21273
21274 }/*
21275  * Based on:
21276  * Ext JS Library 1.1.1
21277  * Copyright(c) 2006-2007, Ext JS, LLC.
21278  *
21279  * Originally Released Under LGPL - original licence link has changed is not relivant.
21280  *
21281  * Fork - LGPL
21282  * <script type="text/javascript">
21283  */
21284
21285 /**
21286  * @class Roo.dd.DD
21287  * A DragDrop implementation where the linked element follows the
21288  * mouse cursor during a drag.
21289  * @extends Roo.dd.DragDrop
21290  * @constructor
21291  * @param {String} id the id of the linked element
21292  * @param {String} sGroup the group of related DragDrop items
21293  * @param {object} config an object containing configurable attributes
21294  *                Valid properties for DD:
21295  *                    scroll
21296  */
21297 Roo.dd.DD = function(id, sGroup, config) {
21298     if (id) {
21299         this.init(id, sGroup, config);
21300     }
21301 };
21302
21303 Roo.extend(Roo.dd.DD, Roo.dd.DragDrop, {
21304
21305     /**
21306      * When set to true, the utility automatically tries to scroll the browser
21307      * window wehn a drag and drop element is dragged near the viewport boundary.
21308      * Defaults to true.
21309      * @property scroll
21310      * @type boolean
21311      */
21312     scroll: true,
21313
21314     /**
21315      * Sets the pointer offset to the distance between the linked element's top
21316      * left corner and the location the element was clicked
21317      * @method autoOffset
21318      * @param {int} iPageX the X coordinate of the click
21319      * @param {int} iPageY the Y coordinate of the click
21320      */
21321     autoOffset: function(iPageX, iPageY) {
21322         var x = iPageX - this.startPageX;
21323         var y = iPageY - this.startPageY;
21324         this.setDelta(x, y);
21325     },
21326
21327     /**
21328      * Sets the pointer offset.  You can call this directly to force the
21329      * offset to be in a particular location (e.g., pass in 0,0 to set it
21330      * to the center of the object)
21331      * @method setDelta
21332      * @param {int} iDeltaX the distance from the left
21333      * @param {int} iDeltaY the distance from the top
21334      */
21335     setDelta: function(iDeltaX, iDeltaY) {
21336         this.deltaX = iDeltaX;
21337         this.deltaY = iDeltaY;
21338     },
21339
21340     /**
21341      * Sets the drag element to the location of the mousedown or click event,
21342      * maintaining the cursor location relative to the location on the element
21343      * that was clicked.  Override this if you want to place the element in a
21344      * location other than where the cursor is.
21345      * @method setDragElPos
21346      * @param {int} iPageX the X coordinate of the mousedown or drag event
21347      * @param {int} iPageY the Y coordinate of the mousedown or drag event
21348      */
21349     setDragElPos: function(iPageX, iPageY) {
21350         // the first time we do this, we are going to check to make sure
21351         // the element has css positioning
21352
21353         var el = this.getDragEl();
21354         this.alignElWithMouse(el, iPageX, iPageY);
21355     },
21356
21357     /**
21358      * Sets the element to the location of the mousedown or click event,
21359      * maintaining the cursor location relative to the location on the element
21360      * that was clicked.  Override this if you want to place the element in a
21361      * location other than where the cursor is.
21362      * @method alignElWithMouse
21363      * @param {HTMLElement} el the element to move
21364      * @param {int} iPageX the X coordinate of the mousedown or drag event
21365      * @param {int} iPageY the Y coordinate of the mousedown or drag event
21366      */
21367     alignElWithMouse: function(el, iPageX, iPageY) {
21368         var oCoord = this.getTargetCoord(iPageX, iPageY);
21369         var fly = el.dom ? el : Roo.fly(el);
21370         if (!this.deltaSetXY) {
21371             var aCoord = [oCoord.x, oCoord.y];
21372             fly.setXY(aCoord);
21373             var newLeft = fly.getLeft(true);
21374             var newTop  = fly.getTop(true);
21375             this.deltaSetXY = [ newLeft - oCoord.x, newTop - oCoord.y ];
21376         } else {
21377             fly.setLeftTop(oCoord.x + this.deltaSetXY[0], oCoord.y + this.deltaSetXY[1]);
21378         }
21379
21380         this.cachePosition(oCoord.x, oCoord.y);
21381         this.autoScroll(oCoord.x, oCoord.y, el.offsetHeight, el.offsetWidth);
21382         return oCoord;
21383     },
21384
21385     /**
21386      * Saves the most recent position so that we can reset the constraints and
21387      * tick marks on-demand.  We need to know this so that we can calculate the
21388      * number of pixels the element is offset from its original position.
21389      * @method cachePosition
21390      * @param iPageX the current x position (optional, this just makes it so we
21391      * don't have to look it up again)
21392      * @param iPageY the current y position (optional, this just makes it so we
21393      * don't have to look it up again)
21394      */
21395     cachePosition: function(iPageX, iPageY) {
21396         if (iPageX) {
21397             this.lastPageX = iPageX;
21398             this.lastPageY = iPageY;
21399         } else {
21400             var aCoord = Roo.lib.Dom.getXY(this.getEl());
21401             this.lastPageX = aCoord[0];
21402             this.lastPageY = aCoord[1];
21403         }
21404     },
21405
21406     /**
21407      * Auto-scroll the window if the dragged object has been moved beyond the
21408      * visible window boundary.
21409      * @method autoScroll
21410      * @param {int} x the drag element's x position
21411      * @param {int} y the drag element's y position
21412      * @param {int} h the height of the drag element
21413      * @param {int} w the width of the drag element
21414      * @private
21415      */
21416     autoScroll: function(x, y, h, w) {
21417
21418         if (this.scroll) {
21419             // The client height
21420             var clientH = Roo.lib.Dom.getViewWidth();
21421
21422             // The client width
21423             var clientW = Roo.lib.Dom.getViewHeight();
21424
21425             // The amt scrolled down
21426             var st = this.DDM.getScrollTop();
21427
21428             // The amt scrolled right
21429             var sl = this.DDM.getScrollLeft();
21430
21431             // Location of the bottom of the element
21432             var bot = h + y;
21433
21434             // Location of the right of the element
21435             var right = w + x;
21436
21437             // The distance from the cursor to the bottom of the visible area,
21438             // adjusted so that we don't scroll if the cursor is beyond the
21439             // element drag constraints
21440             var toBot = (clientH + st - y - this.deltaY);
21441
21442             // The distance from the cursor to the right of the visible area
21443             var toRight = (clientW + sl - x - this.deltaX);
21444
21445
21446             // How close to the edge the cursor must be before we scroll
21447             // var thresh = (document.all) ? 100 : 40;
21448             var thresh = 40;
21449
21450             // How many pixels to scroll per autoscroll op.  This helps to reduce
21451             // clunky scrolling. IE is more sensitive about this ... it needs this
21452             // value to be higher.
21453             var scrAmt = (document.all) ? 80 : 30;
21454
21455             // Scroll down if we are near the bottom of the visible page and the
21456             // obj extends below the crease
21457             if ( bot > clientH && toBot < thresh ) {
21458                 window.scrollTo(sl, st + scrAmt);
21459             }
21460
21461             // Scroll up if the window is scrolled down and the top of the object
21462             // goes above the top border
21463             if ( y < st && st > 0 && y - st < thresh ) {
21464                 window.scrollTo(sl, st - scrAmt);
21465             }
21466
21467             // Scroll right if the obj is beyond the right border and the cursor is
21468             // near the border.
21469             if ( right > clientW && toRight < thresh ) {
21470                 window.scrollTo(sl + scrAmt, st);
21471             }
21472
21473             // Scroll left if the window has been scrolled to the right and the obj
21474             // extends past the left border
21475             if ( x < sl && sl > 0 && x - sl < thresh ) {
21476                 window.scrollTo(sl - scrAmt, st);
21477             }
21478         }
21479     },
21480
21481     /**
21482      * Finds the location the element should be placed if we want to move
21483      * it to where the mouse location less the click offset would place us.
21484      * @method getTargetCoord
21485      * @param {int} iPageX the X coordinate of the click
21486      * @param {int} iPageY the Y coordinate of the click
21487      * @return an object that contains the coordinates (Object.x and Object.y)
21488      * @private
21489      */
21490     getTargetCoord: function(iPageX, iPageY) {
21491
21492
21493         var x = iPageX - this.deltaX;
21494         var y = iPageY - this.deltaY;
21495
21496         if (this.constrainX) {
21497             if (x < this.minX) { x = this.minX; }
21498             if (x > this.maxX) { x = this.maxX; }
21499         }
21500
21501         if (this.constrainY) {
21502             if (y < this.minY) { y = this.minY; }
21503             if (y > this.maxY) { y = this.maxY; }
21504         }
21505
21506         x = this.getTick(x, this.xTicks);
21507         y = this.getTick(y, this.yTicks);
21508
21509
21510         return {x:x, y:y};
21511     },
21512
21513     /*
21514      * Sets up config options specific to this class. Overrides
21515      * Roo.dd.DragDrop, but all versions of this method through the
21516      * inheritance chain are called
21517      */
21518     applyConfig: function() {
21519         Roo.dd.DD.superclass.applyConfig.call(this);
21520         this.scroll = (this.config.scroll !== false);
21521     },
21522
21523     /*
21524      * Event that fires prior to the onMouseDown event.  Overrides
21525      * Roo.dd.DragDrop.
21526      */
21527     b4MouseDown: function(e) {
21528         // this.resetConstraints();
21529         this.autoOffset(e.getPageX(),
21530                             e.getPageY());
21531     },
21532
21533     /*
21534      * Event that fires prior to the onDrag event.  Overrides
21535      * Roo.dd.DragDrop.
21536      */
21537     b4Drag: function(e) {
21538         this.setDragElPos(e.getPageX(),
21539                             e.getPageY());
21540     },
21541
21542     toString: function() {
21543         return ("DD " + this.id);
21544     }
21545
21546     //////////////////////////////////////////////////////////////////////////
21547     // Debugging ygDragDrop events that can be overridden
21548     //////////////////////////////////////////////////////////////////////////
21549     /*
21550     startDrag: function(x, y) {
21551     },
21552
21553     onDrag: function(e) {
21554     },
21555
21556     onDragEnter: function(e, id) {
21557     },
21558
21559     onDragOver: function(e, id) {
21560     },
21561
21562     onDragOut: function(e, id) {
21563     },
21564
21565     onDragDrop: function(e, id) {
21566     },
21567
21568     endDrag: function(e) {
21569     }
21570
21571     */
21572
21573 });/*
21574  * Based on:
21575  * Ext JS Library 1.1.1
21576  * Copyright(c) 2006-2007, Ext JS, LLC.
21577  *
21578  * Originally Released Under LGPL - original licence link has changed is not relivant.
21579  *
21580  * Fork - LGPL
21581  * <script type="text/javascript">
21582  */
21583
21584 /**
21585  * @class Roo.dd.DDProxy
21586  * A DragDrop implementation that inserts an empty, bordered div into
21587  * the document that follows the cursor during drag operations.  At the time of
21588  * the click, the frame div is resized to the dimensions of the linked html
21589  * element, and moved to the exact location of the linked element.
21590  *
21591  * References to the "frame" element refer to the single proxy element that
21592  * was created to be dragged in place of all DDProxy elements on the
21593  * page.
21594  *
21595  * @extends Roo.dd.DD
21596  * @constructor
21597  * @param {String} id the id of the linked html element
21598  * @param {String} sGroup the group of related DragDrop objects
21599  * @param {object} config an object containing configurable attributes
21600  *                Valid properties for DDProxy in addition to those in DragDrop:
21601  *                   resizeFrame, centerFrame, dragElId
21602  */
21603 Roo.dd.DDProxy = function(id, sGroup, config) {
21604     if (id) {
21605         this.init(id, sGroup, config);
21606         this.initFrame();
21607     }
21608 };
21609
21610 /**
21611  * The default drag frame div id
21612  * @property Roo.dd.DDProxy.dragElId
21613  * @type String
21614  * @static
21615  */
21616 Roo.dd.DDProxy.dragElId = "ygddfdiv";
21617
21618 Roo.extend(Roo.dd.DDProxy, Roo.dd.DD, {
21619
21620     /**
21621      * By default we resize the drag frame to be the same size as the element
21622      * we want to drag (this is to get the frame effect).  We can turn it off
21623      * if we want a different behavior.
21624      * @property resizeFrame
21625      * @type boolean
21626      */
21627     resizeFrame: true,
21628
21629     /**
21630      * By default the frame is positioned exactly where the drag element is, so
21631      * we use the cursor offset provided by Roo.dd.DD.  Another option that works only if
21632      * you do not have constraints on the obj is to have the drag frame centered
21633      * around the cursor.  Set centerFrame to true for this effect.
21634      * @property centerFrame
21635      * @type boolean
21636      */
21637     centerFrame: false,
21638
21639     /**
21640      * Creates the proxy element if it does not yet exist
21641      * @method createFrame
21642      */
21643     createFrame: function() {
21644         var self = this;
21645         var body = document.body;
21646
21647         if (!body || !body.firstChild) {
21648             setTimeout( function() { self.createFrame(); }, 50 );
21649             return;
21650         }
21651
21652         var div = this.getDragEl();
21653
21654         if (!div) {
21655             div    = document.createElement("div");
21656             div.id = this.dragElId;
21657             var s  = div.style;
21658
21659             s.position   = "absolute";
21660             s.visibility = "hidden";
21661             s.cursor     = "move";
21662             s.border     = "2px solid #aaa";
21663             s.zIndex     = 999;
21664
21665             // appendChild can blow up IE if invoked prior to the window load event
21666             // while rendering a table.  It is possible there are other scenarios
21667             // that would cause this to happen as well.
21668             body.insertBefore(div, body.firstChild);
21669         }
21670     },
21671
21672     /**
21673      * Initialization for the drag frame element.  Must be called in the
21674      * constructor of all subclasses
21675      * @method initFrame
21676      */
21677     initFrame: function() {
21678         this.createFrame();
21679     },
21680
21681     applyConfig: function() {
21682         Roo.dd.DDProxy.superclass.applyConfig.call(this);
21683
21684         this.resizeFrame = (this.config.resizeFrame !== false);
21685         this.centerFrame = (this.config.centerFrame);
21686         this.setDragElId(this.config.dragElId || Roo.dd.DDProxy.dragElId);
21687     },
21688
21689     /**
21690      * Resizes the drag frame to the dimensions of the clicked object, positions
21691      * it over the object, and finally displays it
21692      * @method showFrame
21693      * @param {int} iPageX X click position
21694      * @param {int} iPageY Y click position
21695      * @private
21696      */
21697     showFrame: function(iPageX, iPageY) {
21698         var el = this.getEl();
21699         var dragEl = this.getDragEl();
21700         var s = dragEl.style;
21701
21702         this._resizeProxy();
21703
21704         if (this.centerFrame) {
21705             this.setDelta( Math.round(parseInt(s.width,  10)/2),
21706                            Math.round(parseInt(s.height, 10)/2) );
21707         }
21708
21709         this.setDragElPos(iPageX, iPageY);
21710
21711         Roo.fly(dragEl).show();
21712     },
21713
21714     /**
21715      * The proxy is automatically resized to the dimensions of the linked
21716      * element when a drag is initiated, unless resizeFrame is set to false
21717      * @method _resizeProxy
21718      * @private
21719      */
21720     _resizeProxy: function() {
21721         if (this.resizeFrame) {
21722             var el = this.getEl();
21723             Roo.fly(this.getDragEl()).setSize(el.offsetWidth, el.offsetHeight);
21724         }
21725     },
21726
21727     // overrides Roo.dd.DragDrop
21728     b4MouseDown: function(e) {
21729         var x = e.getPageX();
21730         var y = e.getPageY();
21731         this.autoOffset(x, y);
21732         this.setDragElPos(x, y);
21733     },
21734
21735     // overrides Roo.dd.DragDrop
21736     b4StartDrag: function(x, y) {
21737         // show the drag frame
21738         this.showFrame(x, y);
21739     },
21740
21741     // overrides Roo.dd.DragDrop
21742     b4EndDrag: function(e) {
21743         Roo.fly(this.getDragEl()).hide();
21744     },
21745
21746     // overrides Roo.dd.DragDrop
21747     // By default we try to move the element to the last location of the frame.
21748     // This is so that the default behavior mirrors that of Roo.dd.DD.
21749     endDrag: function(e) {
21750
21751         var lel = this.getEl();
21752         var del = this.getDragEl();
21753
21754         // Show the drag frame briefly so we can get its position
21755         del.style.visibility = "";
21756
21757         this.beforeMove();
21758         // Hide the linked element before the move to get around a Safari
21759         // rendering bug.
21760         lel.style.visibility = "hidden";
21761         Roo.dd.DDM.moveToEl(lel, del);
21762         del.style.visibility = "hidden";
21763         lel.style.visibility = "";
21764
21765         this.afterDrag();
21766     },
21767
21768     beforeMove : function(){
21769
21770     },
21771
21772     afterDrag : function(){
21773
21774     },
21775
21776     toString: function() {
21777         return ("DDProxy " + this.id);
21778     }
21779
21780 });
21781 /*
21782  * Based on:
21783  * Ext JS Library 1.1.1
21784  * Copyright(c) 2006-2007, Ext JS, LLC.
21785  *
21786  * Originally Released Under LGPL - original licence link has changed is not relivant.
21787  *
21788  * Fork - LGPL
21789  * <script type="text/javascript">
21790  */
21791
21792  /**
21793  * @class Roo.dd.DDTarget
21794  * A DragDrop implementation that does not move, but can be a drop
21795  * target.  You would get the same result by simply omitting implementation
21796  * for the event callbacks, but this way we reduce the processing cost of the
21797  * event listener and the callbacks.
21798  * @extends Roo.dd.DragDrop
21799  * @constructor
21800  * @param {String} id the id of the element that is a drop target
21801  * @param {String} sGroup the group of related DragDrop objects
21802  * @param {object} config an object containing configurable attributes
21803  *                 Valid properties for DDTarget in addition to those in
21804  *                 DragDrop:
21805  *                    none
21806  */
21807 Roo.dd.DDTarget = function(id, sGroup, config) {
21808     if (id) {
21809         this.initTarget(id, sGroup, config);
21810     }
21811     if (config && (config.listeners || config.events)) { 
21812         Roo.dd.DragDrop.superclass.constructor.call(this,  { 
21813             listeners : config.listeners || {}, 
21814             events : config.events || {} 
21815         });    
21816     }
21817 };
21818
21819 // Roo.dd.DDTarget.prototype = new Roo.dd.DragDrop();
21820 Roo.extend(Roo.dd.DDTarget, Roo.dd.DragDrop, {
21821     toString: function() {
21822         return ("DDTarget " + this.id);
21823     }
21824 });
21825 /*
21826  * Based on:
21827  * Ext JS Library 1.1.1
21828  * Copyright(c) 2006-2007, Ext JS, LLC.
21829  *
21830  * Originally Released Under LGPL - original licence link has changed is not relivant.
21831  *
21832  * Fork - LGPL
21833  * <script type="text/javascript">
21834  */
21835  
21836
21837 /**
21838  * @class Roo.dd.ScrollManager
21839  * Provides automatic scrolling of overflow regions in the page during drag operations.<br><br>
21840  * <b>Note: This class uses "Point Mode" and is untested in "Intersect Mode".</b>
21841  * @singleton
21842  */
21843 Roo.dd.ScrollManager = function(){
21844     var ddm = Roo.dd.DragDropMgr;
21845     var els = {};
21846     var dragEl = null;
21847     var proc = {};
21848     
21849     
21850     
21851     var onStop = function(e){
21852         dragEl = null;
21853         clearProc();
21854     };
21855     
21856     var triggerRefresh = function(){
21857         if(ddm.dragCurrent){
21858              ddm.refreshCache(ddm.dragCurrent.groups);
21859         }
21860     };
21861     
21862     var doScroll = function(){
21863         if(ddm.dragCurrent){
21864             var dds = Roo.dd.ScrollManager;
21865             if(!dds.animate){
21866                 if(proc.el.scroll(proc.dir, dds.increment)){
21867                     triggerRefresh();
21868                 }
21869             }else{
21870                 proc.el.scroll(proc.dir, dds.increment, true, dds.animDuration, triggerRefresh);
21871             }
21872         }
21873     };
21874     
21875     var clearProc = function(){
21876         if(proc.id){
21877             clearInterval(proc.id);
21878         }
21879         proc.id = 0;
21880         proc.el = null;
21881         proc.dir = "";
21882     };
21883     
21884     var startProc = function(el, dir){
21885          Roo.log('scroll startproc');
21886         clearProc();
21887         proc.el = el;
21888         proc.dir = dir;
21889         proc.id = setInterval(doScroll, Roo.dd.ScrollManager.frequency);
21890     };
21891     
21892     var onFire = function(e, isDrop){
21893        
21894         if(isDrop || !ddm.dragCurrent){ return; }
21895         var dds = Roo.dd.ScrollManager;
21896         if(!dragEl || dragEl != ddm.dragCurrent){
21897             dragEl = ddm.dragCurrent;
21898             // refresh regions on drag start
21899             dds.refreshCache();
21900         }
21901         
21902         var xy = Roo.lib.Event.getXY(e);
21903         var pt = new Roo.lib.Point(xy[0], xy[1]);
21904         for(var id in els){
21905             var el = els[id], r = el._region;
21906             if(r && r.contains(pt) && el.isScrollable()){
21907                 if(r.bottom - pt.y <= dds.thresh){
21908                     if(proc.el != el){
21909                         startProc(el, "down");
21910                     }
21911                     return;
21912                 }else if(r.right - pt.x <= dds.thresh){
21913                     if(proc.el != el){
21914                         startProc(el, "left");
21915                     }
21916                     return;
21917                 }else if(pt.y - r.top <= dds.thresh){
21918                     if(proc.el != el){
21919                         startProc(el, "up");
21920                     }
21921                     return;
21922                 }else if(pt.x - r.left <= dds.thresh){
21923                     if(proc.el != el){
21924                         startProc(el, "right");
21925                     }
21926                     return;
21927                 }
21928             }
21929         }
21930         clearProc();
21931     };
21932     
21933     ddm.fireEvents = ddm.fireEvents.createSequence(onFire, ddm);
21934     ddm.stopDrag = ddm.stopDrag.createSequence(onStop, ddm);
21935     
21936     return {
21937         /**
21938          * Registers new overflow element(s) to auto scroll
21939          * @param {String/HTMLElement/Element/Array} el The id of or the element to be scrolled or an array of either
21940          */
21941         register : function(el){
21942             if(el instanceof Array){
21943                 for(var i = 0, len = el.length; i < len; i++) {
21944                         this.register(el[i]);
21945                 }
21946             }else{
21947                 el = Roo.get(el);
21948                 els[el.id] = el;
21949             }
21950             Roo.dd.ScrollManager.els = els;
21951         },
21952         
21953         /**
21954          * Unregisters overflow element(s) so they are no longer scrolled
21955          * @param {String/HTMLElement/Element/Array} el The id of or the element to be removed or an array of either
21956          */
21957         unregister : function(el){
21958             if(el instanceof Array){
21959                 for(var i = 0, len = el.length; i < len; i++) {
21960                         this.unregister(el[i]);
21961                 }
21962             }else{
21963                 el = Roo.get(el);
21964                 delete els[el.id];
21965             }
21966         },
21967         
21968         /**
21969          * The number of pixels from the edge of a container the pointer needs to be to 
21970          * trigger scrolling (defaults to 25)
21971          * @type Number
21972          */
21973         thresh : 25,
21974         
21975         /**
21976          * The number of pixels to scroll in each scroll increment (defaults to 50)
21977          * @type Number
21978          */
21979         increment : 100,
21980         
21981         /**
21982          * The frequency of scrolls in milliseconds (defaults to 500)
21983          * @type Number
21984          */
21985         frequency : 500,
21986         
21987         /**
21988          * True to animate the scroll (defaults to true)
21989          * @type Boolean
21990          */
21991         animate: true,
21992         
21993         /**
21994          * The animation duration in seconds - 
21995          * MUST BE less than Roo.dd.ScrollManager.frequency! (defaults to .4)
21996          * @type Number
21997          */
21998         animDuration: .4,
21999         
22000         /**
22001          * Manually trigger a cache refresh.
22002          */
22003         refreshCache : function(){
22004             for(var id in els){
22005                 if(typeof els[id] == 'object'){ // for people extending the object prototype
22006                     els[id]._region = els[id].getRegion();
22007                 }
22008             }
22009         }
22010     };
22011 }();/*
22012  * Based on:
22013  * Ext JS Library 1.1.1
22014  * Copyright(c) 2006-2007, Ext JS, LLC.
22015  *
22016  * Originally Released Under LGPL - original licence link has changed is not relivant.
22017  *
22018  * Fork - LGPL
22019  * <script type="text/javascript">
22020  */
22021  
22022
22023 /**
22024  * @class Roo.dd.Registry
22025  * Provides easy access to all drag drop components that are registered on a page.  Items can be retrieved either
22026  * directly by DOM node id, or by passing in the drag drop event that occurred and looking up the event target.
22027  * @singleton
22028  */
22029 Roo.dd.Registry = function(){
22030     var elements = {}; 
22031     var handles = {}; 
22032     var autoIdSeed = 0;
22033
22034     var getId = function(el, autogen){
22035         if(typeof el == "string"){
22036             return el;
22037         }
22038         var id = el.id;
22039         if(!id && autogen !== false){
22040             id = "roodd-" + (++autoIdSeed);
22041             el.id = id;
22042         }
22043         return id;
22044     };
22045     
22046     return {
22047     /**
22048      * Register a drag drop element
22049      * @param {String|HTMLElement} element The id or DOM node to register
22050      * @param {Object} data (optional) A custom data object that will be passed between the elements that are involved
22051      * in drag drop operations.  You can populate this object with any arbitrary properties that your own code
22052      * knows how to interpret, plus there are some specific properties known to the Registry that should be
22053      * populated in the data object (if applicable):
22054      * <pre>
22055 Value      Description<br />
22056 ---------  ------------------------------------------<br />
22057 handles    Array of DOM nodes that trigger dragging<br />
22058            for the element being registered<br />
22059 isHandle   True if the element passed in triggers<br />
22060            dragging itself, else false
22061 </pre>
22062      */
22063         register : function(el, data){
22064             data = data || {};
22065             if(typeof el == "string"){
22066                 el = document.getElementById(el);
22067             }
22068             data.ddel = el;
22069             elements[getId(el)] = data;
22070             if(data.isHandle !== false){
22071                 handles[data.ddel.id] = data;
22072             }
22073             if(data.handles){
22074                 var hs = data.handles;
22075                 for(var i = 0, len = hs.length; i < len; i++){
22076                         handles[getId(hs[i])] = data;
22077                 }
22078             }
22079         },
22080
22081     /**
22082      * Unregister a drag drop element
22083      * @param {String|HTMLElement}  element The id or DOM node to unregister
22084      */
22085         unregister : function(el){
22086             var id = getId(el, false);
22087             var data = elements[id];
22088             if(data){
22089                 delete elements[id];
22090                 if(data.handles){
22091                     var hs = data.handles;
22092                     for(var i = 0, len = hs.length; i < len; i++){
22093                         delete handles[getId(hs[i], false)];
22094                     }
22095                 }
22096             }
22097         },
22098
22099     /**
22100      * Returns the handle registered for a DOM Node by id
22101      * @param {String|HTMLElement} id The DOM node or id to look up
22102      * @return {Object} handle The custom handle data
22103      */
22104         getHandle : function(id){
22105             if(typeof id != "string"){ // must be element?
22106                 id = id.id;
22107             }
22108             return handles[id];
22109         },
22110
22111     /**
22112      * Returns the handle that is registered for the DOM node that is the target of the event
22113      * @param {Event} e The event
22114      * @return {Object} handle The custom handle data
22115      */
22116         getHandleFromEvent : function(e){
22117             var t = Roo.lib.Event.getTarget(e);
22118             return t ? handles[t.id] : null;
22119         },
22120
22121     /**
22122      * Returns a custom data object that is registered for a DOM node by id
22123      * @param {String|HTMLElement} id The DOM node or id to look up
22124      * @return {Object} data The custom data
22125      */
22126         getTarget : function(id){
22127             if(typeof id != "string"){ // must be element?
22128                 id = id.id;
22129             }
22130             return elements[id];
22131         },
22132
22133     /**
22134      * Returns a custom data object that is registered for the DOM node that is the target of the event
22135      * @param {Event} e The event
22136      * @return {Object} data The custom data
22137      */
22138         getTargetFromEvent : function(e){
22139             var t = Roo.lib.Event.getTarget(e);
22140             return t ? elements[t.id] || handles[t.id] : null;
22141         }
22142     };
22143 }();/*
22144  * Based on:
22145  * Ext JS Library 1.1.1
22146  * Copyright(c) 2006-2007, Ext JS, LLC.
22147  *
22148  * Originally Released Under LGPL - original licence link has changed is not relivant.
22149  *
22150  * Fork - LGPL
22151  * <script type="text/javascript">
22152  */
22153  
22154
22155 /**
22156  * @class Roo.dd.StatusProxy
22157  * A specialized drag proxy that supports a drop status icon, {@link Roo.Layer} styles and auto-repair.  This is the
22158  * default drag proxy used by all Roo.dd components.
22159  * @constructor
22160  * @param {Object} config
22161  */
22162 Roo.dd.StatusProxy = function(config){
22163     Roo.apply(this, config);
22164     this.id = this.id || Roo.id();
22165     this.el = new Roo.Layer({
22166         dh: {
22167             id: this.id, tag: "div", cls: "x-dd-drag-proxy "+this.dropNotAllowed, children: [
22168                 {tag: "div", cls: "x-dd-drop-icon"},
22169                 {tag: "div", cls: "x-dd-drag-ghost"}
22170             ]
22171         }, 
22172         shadow: !config || config.shadow !== false
22173     });
22174     this.ghost = Roo.get(this.el.dom.childNodes[1]);
22175     this.dropStatus = this.dropNotAllowed;
22176 };
22177
22178 Roo.dd.StatusProxy.prototype = {
22179     /**
22180      * @cfg {String} dropAllowed
22181      * The CSS class to apply to the status element when drop is allowed (defaults to "x-dd-drop-ok").
22182      */
22183     dropAllowed : "x-dd-drop-ok",
22184     /**
22185      * @cfg {String} dropNotAllowed
22186      * The CSS class to apply to the status element when drop is not allowed (defaults to "x-dd-drop-nodrop").
22187      */
22188     dropNotAllowed : "x-dd-drop-nodrop",
22189
22190     /**
22191      * Updates the proxy's visual element to indicate the status of whether or not drop is allowed
22192      * over the current target element.
22193      * @param {String} cssClass The css class for the new drop status indicator image
22194      */
22195     setStatus : function(cssClass){
22196         cssClass = cssClass || this.dropNotAllowed;
22197         if(this.dropStatus != cssClass){
22198             this.el.replaceClass(this.dropStatus, cssClass);
22199             this.dropStatus = cssClass;
22200         }
22201     },
22202
22203     /**
22204      * Resets the status indicator to the default dropNotAllowed value
22205      * @param {Boolean} clearGhost True to also remove all content from the ghost, false to preserve it
22206      */
22207     reset : function(clearGhost){
22208         this.el.dom.className = "x-dd-drag-proxy " + this.dropNotAllowed;
22209         this.dropStatus = this.dropNotAllowed;
22210         if(clearGhost){
22211             this.ghost.update("");
22212         }
22213     },
22214
22215     /**
22216      * Updates the contents of the ghost element
22217      * @param {String} html The html that will replace the current innerHTML of the ghost element
22218      */
22219     update : function(html){
22220         if(typeof html == "string"){
22221             this.ghost.update(html);
22222         }else{
22223             this.ghost.update("");
22224             html.style.margin = "0";
22225             this.ghost.dom.appendChild(html);
22226         }
22227         // ensure float = none set?? cant remember why though.
22228         var el = this.ghost.dom.firstChild;
22229                 if(el){
22230                         Roo.fly(el).setStyle('float', 'none');
22231                 }
22232     },
22233     
22234     /**
22235      * Returns the underlying proxy {@link Roo.Layer}
22236      * @return {Roo.Layer} el
22237     */
22238     getEl : function(){
22239         return this.el;
22240     },
22241
22242     /**
22243      * Returns the ghost element
22244      * @return {Roo.Element} el
22245      */
22246     getGhost : function(){
22247         return this.ghost;
22248     },
22249
22250     /**
22251      * Hides the proxy
22252      * @param {Boolean} clear True to reset the status and clear the ghost contents, false to preserve them
22253      */
22254     hide : function(clear){
22255         this.el.hide();
22256         if(clear){
22257             this.reset(true);
22258         }
22259     },
22260
22261     /**
22262      * Stops the repair animation if it's currently running
22263      */
22264     stop : function(){
22265         if(this.anim && this.anim.isAnimated && this.anim.isAnimated()){
22266             this.anim.stop();
22267         }
22268     },
22269
22270     /**
22271      * Displays this proxy
22272      */
22273     show : function(){
22274         this.el.show();
22275     },
22276
22277     /**
22278      * Force the Layer to sync its shadow and shim positions to the element
22279      */
22280     sync : function(){
22281         this.el.sync();
22282     },
22283
22284     /**
22285      * Causes the proxy to return to its position of origin via an animation.  Should be called after an
22286      * invalid drop operation by the item being dragged.
22287      * @param {Array} xy The XY position of the element ([x, y])
22288      * @param {Function} callback The function to call after the repair is complete
22289      * @param {Object} scope The scope in which to execute the callback
22290      */
22291     repair : function(xy, callback, scope){
22292         this.callback = callback;
22293         this.scope = scope;
22294         if(xy && this.animRepair !== false){
22295             this.el.addClass("x-dd-drag-repair");
22296             this.el.hideUnders(true);
22297             this.anim = this.el.shift({
22298                 duration: this.repairDuration || .5,
22299                 easing: 'easeOut',
22300                 xy: xy,
22301                 stopFx: true,
22302                 callback: this.afterRepair,
22303                 scope: this
22304             });
22305         }else{
22306             this.afterRepair();
22307         }
22308     },
22309
22310     // private
22311     afterRepair : function(){
22312         this.hide(true);
22313         if(typeof this.callback == "function"){
22314             this.callback.call(this.scope || this);
22315         }
22316         this.callback = null;
22317         this.scope = null;
22318     }
22319 };/*
22320  * Based on:
22321  * Ext JS Library 1.1.1
22322  * Copyright(c) 2006-2007, Ext JS, LLC.
22323  *
22324  * Originally Released Under LGPL - original licence link has changed is not relivant.
22325  *
22326  * Fork - LGPL
22327  * <script type="text/javascript">
22328  */
22329
22330 /**
22331  * @class Roo.dd.DragSource
22332  * @extends Roo.dd.DDProxy
22333  * A simple class that provides the basic implementation needed to make any element draggable.
22334  * @constructor
22335  * @param {String/HTMLElement/Element} el The container element
22336  * @param {Object} config
22337  */
22338 Roo.dd.DragSource = function(el, config){
22339     this.el = Roo.get(el);
22340     this.dragData = {};
22341     
22342     Roo.apply(this, config);
22343     
22344     if(!this.proxy){
22345         this.proxy = new Roo.dd.StatusProxy();
22346     }
22347
22348     Roo.dd.DragSource.superclass.constructor.call(this, this.el.dom, this.ddGroup || this.group,
22349           {dragElId : this.proxy.id, resizeFrame: false, isTarget: false, scroll: this.scroll === true});
22350     
22351     this.dragging = false;
22352 };
22353
22354 Roo.extend(Roo.dd.DragSource, Roo.dd.DDProxy, {
22355     /**
22356      * @cfg {String} dropAllowed
22357      * The CSS class returned to the drag source when drop is allowed (defaults to "x-dd-drop-ok").
22358      */
22359     dropAllowed : "x-dd-drop-ok",
22360     /**
22361      * @cfg {String} dropNotAllowed
22362      * The CSS class returned to the drag source when drop is not allowed (defaults to "x-dd-drop-nodrop").
22363      */
22364     dropNotAllowed : "x-dd-drop-nodrop",
22365
22366     /**
22367      * Returns the data object associated with this drag source
22368      * @return {Object} data An object containing arbitrary data
22369      */
22370     getDragData : function(e){
22371         return this.dragData;
22372     },
22373
22374     // private
22375     onDragEnter : function(e, id){
22376         var target = Roo.dd.DragDropMgr.getDDById(id);
22377         this.cachedTarget = target;
22378         if(this.beforeDragEnter(target, e, id) !== false){
22379             if(target.isNotifyTarget){
22380                 var status = target.notifyEnter(this, e, this.dragData);
22381                 this.proxy.setStatus(status);
22382             }else{
22383                 this.proxy.setStatus(this.dropAllowed);
22384             }
22385             
22386             if(this.afterDragEnter){
22387                 /**
22388                  * An empty function by default, but provided so that you can perform a custom action
22389                  * when the dragged item enters the drop target by providing an implementation.
22390                  * @param {Roo.dd.DragDrop} target The drop target
22391                  * @param {Event} e The event object
22392                  * @param {String} id The id of the dragged element
22393                  * @method afterDragEnter
22394                  */
22395                 this.afterDragEnter(target, e, id);
22396             }
22397         }
22398     },
22399
22400     /**
22401      * An empty function by default, but provided so that you can perform a custom action
22402      * before the dragged item enters the drop target and optionally cancel the onDragEnter.
22403      * @param {Roo.dd.DragDrop} target The drop target
22404      * @param {Event} e The event object
22405      * @param {String} id The id of the dragged element
22406      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
22407      */
22408     beforeDragEnter : function(target, e, id){
22409         return true;
22410     },
22411
22412     // private
22413     alignElWithMouse: function() {
22414         Roo.dd.DragSource.superclass.alignElWithMouse.apply(this, arguments);
22415         this.proxy.sync();
22416     },
22417
22418     // private
22419     onDragOver : function(e, id){
22420         var target = this.cachedTarget || Roo.dd.DragDropMgr.getDDById(id);
22421         if(this.beforeDragOver(target, e, id) !== false){
22422             if(target.isNotifyTarget){
22423                 var status = target.notifyOver(this, e, this.dragData);
22424                 this.proxy.setStatus(status);
22425             }
22426
22427             if(this.afterDragOver){
22428                 /**
22429                  * An empty function by default, but provided so that you can perform a custom action
22430                  * while the dragged item is over the drop target by providing an implementation.
22431                  * @param {Roo.dd.DragDrop} target The drop target
22432                  * @param {Event} e The event object
22433                  * @param {String} id The id of the dragged element
22434                  * @method afterDragOver
22435                  */
22436                 this.afterDragOver(target, e, id);
22437             }
22438         }
22439     },
22440
22441     /**
22442      * An empty function by default, but provided so that you can perform a custom action
22443      * while the dragged item is over the drop target and optionally cancel the onDragOver.
22444      * @param {Roo.dd.DragDrop} target The drop target
22445      * @param {Event} e The event object
22446      * @param {String} id The id of the dragged element
22447      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
22448      */
22449     beforeDragOver : function(target, e, id){
22450         return true;
22451     },
22452
22453     // private
22454     onDragOut : function(e, id){
22455         var target = this.cachedTarget || Roo.dd.DragDropMgr.getDDById(id);
22456         if(this.beforeDragOut(target, e, id) !== false){
22457             if(target.isNotifyTarget){
22458                 target.notifyOut(this, e, this.dragData);
22459             }
22460             this.proxy.reset();
22461             if(this.afterDragOut){
22462                 /**
22463                  * An empty function by default, but provided so that you can perform a custom action
22464                  * after the dragged item is dragged out of the target without dropping.
22465                  * @param {Roo.dd.DragDrop} target The drop target
22466                  * @param {Event} e The event object
22467                  * @param {String} id The id of the dragged element
22468                  * @method afterDragOut
22469                  */
22470                 this.afterDragOut(target, e, id);
22471             }
22472         }
22473         this.cachedTarget = null;
22474     },
22475
22476     /**
22477      * An empty function by default, but provided so that you can perform a custom action before the dragged
22478      * item is dragged out of the target without dropping, and optionally cancel the onDragOut.
22479      * @param {Roo.dd.DragDrop} target The drop target
22480      * @param {Event} e The event object
22481      * @param {String} id The id of the dragged element
22482      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
22483      */
22484     beforeDragOut : function(target, e, id){
22485         return true;
22486     },
22487     
22488     // private
22489     onDragDrop : function(e, id){
22490         var target = this.cachedTarget || Roo.dd.DragDropMgr.getDDById(id);
22491         if(this.beforeDragDrop(target, e, id) !== false){
22492             if(target.isNotifyTarget){
22493                 if(target.notifyDrop(this, e, this.dragData)){ // valid drop?
22494                     this.onValidDrop(target, e, id);
22495                 }else{
22496                     this.onInvalidDrop(target, e, id);
22497                 }
22498             }else{
22499                 this.onValidDrop(target, e, id);
22500             }
22501             
22502             if(this.afterDragDrop){
22503                 /**
22504                  * An empty function by default, but provided so that you can perform a custom action
22505                  * after a valid drag drop has occurred by providing an implementation.
22506                  * @param {Roo.dd.DragDrop} target The drop target
22507                  * @param {Event} e The event object
22508                  * @param {String} id The id of the dropped element
22509                  * @method afterDragDrop
22510                  */
22511                 this.afterDragDrop(target, e, id);
22512             }
22513         }
22514         delete this.cachedTarget;
22515     },
22516
22517     /**
22518      * An empty function by default, but provided so that you can perform a custom action before the dragged
22519      * item is dropped onto the target and optionally cancel the onDragDrop.
22520      * @param {Roo.dd.DragDrop} target The drop target
22521      * @param {Event} e The event object
22522      * @param {String} id The id of the dragged element
22523      * @return {Boolean} isValid True if the drag drop event is valid, else false to cancel
22524      */
22525     beforeDragDrop : function(target, e, id){
22526         return true;
22527     },
22528
22529     // private
22530     onValidDrop : function(target, e, id){
22531         this.hideProxy();
22532         if(this.afterValidDrop){
22533             /**
22534              * An empty function by default, but provided so that you can perform a custom action
22535              * after a valid drop has occurred by providing an implementation.
22536              * @param {Object} target The target DD 
22537              * @param {Event} e The event object
22538              * @param {String} id The id of the dropped element
22539              * @method afterInvalidDrop
22540              */
22541             this.afterValidDrop(target, e, id);
22542         }
22543     },
22544
22545     // private
22546     getRepairXY : function(e, data){
22547         return this.el.getXY();  
22548     },
22549
22550     // private
22551     onInvalidDrop : function(target, e, id){
22552         this.beforeInvalidDrop(target, e, id);
22553         if(this.cachedTarget){
22554             if(this.cachedTarget.isNotifyTarget){
22555                 this.cachedTarget.notifyOut(this, e, this.dragData);
22556             }
22557             this.cacheTarget = null;
22558         }
22559         this.proxy.repair(this.getRepairXY(e, this.dragData), this.afterRepair, this);
22560
22561         if(this.afterInvalidDrop){
22562             /**
22563              * An empty function by default, but provided so that you can perform a custom action
22564              * after an invalid drop has occurred by providing an implementation.
22565              * @param {Event} e The event object
22566              * @param {String} id The id of the dropped element
22567              * @method afterInvalidDrop
22568              */
22569             this.afterInvalidDrop(e, id);
22570         }
22571     },
22572
22573     // private
22574     afterRepair : function(){
22575         if(Roo.enableFx){
22576             this.el.highlight(this.hlColor || "c3daf9");
22577         }
22578         this.dragging = false;
22579     },
22580
22581     /**
22582      * An empty function by default, but provided so that you can perform a custom action after an invalid
22583      * drop has occurred.
22584      * @param {Roo.dd.DragDrop} target The drop target
22585      * @param {Event} e The event object
22586      * @param {String} id The id of the dragged element
22587      * @return {Boolean} isValid True if the invalid drop should proceed, else false to cancel
22588      */
22589     beforeInvalidDrop : function(target, e, id){
22590         return true;
22591     },
22592
22593     // private
22594     handleMouseDown : function(e){
22595         if(this.dragging) {
22596             return;
22597         }
22598         var data = this.getDragData(e);
22599         if(data && this.onBeforeDrag(data, e) !== false){
22600             this.dragData = data;
22601             this.proxy.stop();
22602             Roo.dd.DragSource.superclass.handleMouseDown.apply(this, arguments);
22603         } 
22604     },
22605
22606     /**
22607      * An empty function by default, but provided so that you can perform a custom action before the initial
22608      * drag event begins and optionally cancel it.
22609      * @param {Object} data An object containing arbitrary data to be shared with drop targets
22610      * @param {Event} e The event object
22611      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
22612      */
22613     onBeforeDrag : function(data, e){
22614         return true;
22615     },
22616
22617     /**
22618      * An empty function by default, but provided so that you can perform a custom action once the initial
22619      * drag event has begun.  The drag cannot be canceled from this function.
22620      * @param {Number} x The x position of the click on the dragged object
22621      * @param {Number} y The y position of the click on the dragged object
22622      */
22623     onStartDrag : Roo.emptyFn,
22624
22625     // private - YUI override
22626     startDrag : function(x, y){
22627         this.proxy.reset();
22628         this.dragging = true;
22629         this.proxy.update("");
22630         this.onInitDrag(x, y);
22631         this.proxy.show();
22632     },
22633
22634     // private
22635     onInitDrag : function(x, y){
22636         var clone = this.el.dom.cloneNode(true);
22637         clone.id = Roo.id(); // prevent duplicate ids
22638         this.proxy.update(clone);
22639         this.onStartDrag(x, y);
22640         return true;
22641     },
22642
22643     /**
22644      * Returns the drag source's underlying {@link Roo.dd.StatusProxy}
22645      * @return {Roo.dd.StatusProxy} proxy The StatusProxy
22646      */
22647     getProxy : function(){
22648         return this.proxy;  
22649     },
22650
22651     /**
22652      * Hides the drag source's {@link Roo.dd.StatusProxy}
22653      */
22654     hideProxy : function(){
22655         this.proxy.hide();  
22656         this.proxy.reset(true);
22657         this.dragging = false;
22658     },
22659
22660     // private
22661     triggerCacheRefresh : function(){
22662         Roo.dd.DDM.refreshCache(this.groups);
22663     },
22664
22665     // private - override to prevent hiding
22666     b4EndDrag: function(e) {
22667     },
22668
22669     // private - override to prevent moving
22670     endDrag : function(e){
22671         this.onEndDrag(this.dragData, e);
22672     },
22673
22674     // private
22675     onEndDrag : function(data, e){
22676     },
22677     
22678     // private - pin to cursor
22679     autoOffset : function(x, y) {
22680         this.setDelta(-12, -20);
22681     }    
22682 });/*
22683  * Based on:
22684  * Ext JS Library 1.1.1
22685  * Copyright(c) 2006-2007, Ext JS, LLC.
22686  *
22687  * Originally Released Under LGPL - original licence link has changed is not relivant.
22688  *
22689  * Fork - LGPL
22690  * <script type="text/javascript">
22691  */
22692
22693
22694 /**
22695  * @class Roo.dd.DropTarget
22696  * @extends Roo.dd.DDTarget
22697  * A simple class that provides the basic implementation needed to make any element a drop target that can have
22698  * draggable items dropped onto it.  The drop has no effect until an implementation of notifyDrop is provided.
22699  * @constructor
22700  * @param {String/HTMLElement/Element} el The container element
22701  * @param {Object} config
22702  */
22703 Roo.dd.DropTarget = function(el, config){
22704     this.el = Roo.get(el);
22705     
22706     var listeners = false; ;
22707     if (config && config.listeners) {
22708         listeners= config.listeners;
22709         delete config.listeners;
22710     }
22711     Roo.apply(this, config);
22712     
22713     if(this.containerScroll){
22714         Roo.dd.ScrollManager.register(this.el);
22715     }
22716     this.addEvents( {
22717          /**
22718          * @scope Roo.dd.DropTarget
22719          */
22720          
22721          /**
22722          * @event enter
22723          * The function a {@link Roo.dd.DragSource} calls once to notify this drop target that the source is now over the
22724          * target.  This default implementation adds the CSS class specified by overClass (if any) to the drop element
22725          * and returns the dropAllowed config value.  This method should be overridden if drop validation is required.
22726          * 
22727          * IMPORTANT : it should set  this.valid to true|false
22728          * 
22729          * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
22730          * @param {Event} e The event
22731          * @param {Object} data An object containing arbitrary data supplied by the drag source
22732          */
22733         "enter" : true,
22734         
22735          /**
22736          * @event over
22737          * The function a {@link Roo.dd.DragSource} calls continuously while it is being dragged over the target.
22738          * This method will be called on every mouse movement while the drag source is over the drop target.
22739          * This default implementation simply returns the dropAllowed config value.
22740          * 
22741          * IMPORTANT : it should set  this.valid to true|false
22742          * 
22743          * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
22744          * @param {Event} e The event
22745          * @param {Object} data An object containing arbitrary data supplied by the drag source
22746          
22747          */
22748         "over" : true,
22749         /**
22750          * @event out
22751          * The function a {@link Roo.dd.DragSource} calls once to notify this drop target that the source has been dragged
22752          * out of the target without dropping.  This default implementation simply removes the CSS class specified by
22753          * overClass (if any) from the drop element.
22754          * 
22755          * 
22756          * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
22757          * @param {Event} e The event
22758          * @param {Object} data An object containing arbitrary data supplied by the drag source
22759          */
22760          "out" : true,
22761          
22762         /**
22763          * @event drop
22764          * The function a {@link Roo.dd.DragSource} calls once to notify this drop target that the dragged item has
22765          * been dropped on it.  This method has no default implementation and returns false, so you must provide an
22766          * implementation that does something to process the drop event and returns true so that the drag source's
22767          * repair action does not run.
22768          * 
22769          * IMPORTANT : it should set this.success
22770          * 
22771          * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
22772          * @param {Event} e The event
22773          * @param {Object} data An object containing arbitrary data supplied by the drag source
22774         */
22775          "drop" : true
22776     });
22777             
22778      
22779     Roo.dd.DropTarget.superclass.constructor.call(  this, 
22780         this.el.dom, 
22781         this.ddGroup || this.group,
22782         {
22783             isTarget: true,
22784             listeners : listeners || {} 
22785            
22786         
22787         }
22788     );
22789
22790 };
22791
22792 Roo.extend(Roo.dd.DropTarget, Roo.dd.DDTarget, {
22793     /**
22794      * @cfg {String} overClass
22795      * The CSS class applied to the drop target element while the drag source is over it (defaults to "").
22796      */
22797      /**
22798      * @cfg {String} ddGroup
22799      * The drag drop group to handle drop events for
22800      */
22801      
22802     /**
22803      * @cfg {String} dropAllowed
22804      * The CSS class returned to the drag source when drop is allowed (defaults to "x-dd-drop-ok").
22805      */
22806     dropAllowed : "x-dd-drop-ok",
22807     /**
22808      * @cfg {String} dropNotAllowed
22809      * The CSS class returned to the drag source when drop is not allowed (defaults to "x-dd-drop-nodrop").
22810      */
22811     dropNotAllowed : "x-dd-drop-nodrop",
22812     /**
22813      * @cfg {boolean} success
22814      * set this after drop listener.. 
22815      */
22816     success : false,
22817     /**
22818      * @cfg {boolean|String} valid true/false or string (ok-add/ok-sub/ok/nodrop)
22819      * if the drop point is valid for over/enter..
22820      */
22821     valid : false,
22822     // private
22823     isTarget : true,
22824
22825     // private
22826     isNotifyTarget : true,
22827     
22828     /**
22829      * @hide
22830      */
22831     notifyEnter : function(dd, e, data)
22832     {
22833         this.valid = true;
22834         this.fireEvent('enter', dd, e, data);
22835         if(this.overClass){
22836             this.el.addClass(this.overClass);
22837         }
22838         return typeof(this.valid) == 'string' ? 'x-dd-drop-' + this.valid : (
22839             this.valid ? this.dropAllowed : this.dropNotAllowed
22840         );
22841     },
22842
22843     /**
22844      * @hide
22845      */
22846     notifyOver : function(dd, e, data)
22847     {
22848         this.valid = true;
22849         this.fireEvent('over', dd, e, data);
22850         return typeof(this.valid) == 'string' ? 'x-dd-drop-' + this.valid : (
22851             this.valid ? this.dropAllowed : this.dropNotAllowed
22852         );
22853     },
22854
22855     /**
22856      * @hide
22857      */
22858     notifyOut : function(dd, e, data)
22859     {
22860         this.fireEvent('out', dd, e, data);
22861         if(this.overClass){
22862             this.el.removeClass(this.overClass);
22863         }
22864     },
22865
22866     /**
22867      * @hide
22868      */
22869     notifyDrop : function(dd, e, data)
22870     {
22871         this.success = false;
22872         this.fireEvent('drop', dd, e, data);
22873         return this.success;
22874     }
22875 });/*
22876  * Based on:
22877  * Ext JS Library 1.1.1
22878  * Copyright(c) 2006-2007, Ext JS, LLC.
22879  *
22880  * Originally Released Under LGPL - original licence link has changed is not relivant.
22881  *
22882  * Fork - LGPL
22883  * <script type="text/javascript">
22884  */
22885
22886
22887 /**
22888  * @class Roo.dd.DragZone
22889  * @extends Roo.dd.DragSource
22890  * This class provides a container DD instance that proxies for multiple child node sources.<br />
22891  * By default, this class requires that draggable child nodes are registered with {@link Roo.dd.Registry}.
22892  * @constructor
22893  * @param {String/HTMLElement/Element} el The container element
22894  * @param {Object} config
22895  */
22896 Roo.dd.DragZone = function(el, config){
22897     Roo.dd.DragZone.superclass.constructor.call(this, el, config);
22898     if(this.containerScroll){
22899         Roo.dd.ScrollManager.register(this.el);
22900     }
22901 };
22902
22903 Roo.extend(Roo.dd.DragZone, Roo.dd.DragSource, {
22904     /**
22905      * @cfg {Boolean} containerScroll True to register this container with the Scrollmanager
22906      * for auto scrolling during drag operations.
22907      */
22908     /**
22909      * @cfg {String} hlColor The color to use when visually highlighting the drag source in the afterRepair
22910      * method after a failed drop (defaults to "c3daf9" - light blue)
22911      */
22912
22913     /**
22914      * Called when a mousedown occurs in this container. Looks in {@link Roo.dd.Registry}
22915      * for a valid target to drag based on the mouse down. Override this method
22916      * to provide your own lookup logic (e.g. finding a child by class name). Make sure your returned
22917      * object has a "ddel" attribute (with an HTML Element) for other functions to work.
22918      * @param {EventObject} e The mouse down event
22919      * @return {Object} The dragData
22920      */
22921     getDragData : function(e){
22922         return Roo.dd.Registry.getHandleFromEvent(e);
22923     },
22924     
22925     /**
22926      * Called once drag threshold has been reached to initialize the proxy element. By default, it clones the
22927      * this.dragData.ddel
22928      * @param {Number} x The x position of the click on the dragged object
22929      * @param {Number} y The y position of the click on the dragged object
22930      * @return {Boolean} true to continue the drag, false to cancel
22931      */
22932     onInitDrag : function(x, y){
22933         this.proxy.update(this.dragData.ddel.cloneNode(true));
22934         this.onStartDrag(x, y);
22935         return true;
22936     },
22937     
22938     /**
22939      * Called after a repair of an invalid drop. By default, highlights this.dragData.ddel 
22940      */
22941     afterRepair : function(){
22942         if(Roo.enableFx){
22943             Roo.Element.fly(this.dragData.ddel).highlight(this.hlColor || "c3daf9");
22944         }
22945         this.dragging = false;
22946     },
22947
22948     /**
22949      * Called before a repair of an invalid drop to get the XY to animate to. By default returns
22950      * the XY of this.dragData.ddel
22951      * @param {EventObject} e The mouse up event
22952      * @return {Array} The xy location (e.g. [100, 200])
22953      */
22954     getRepairXY : function(e){
22955         return Roo.Element.fly(this.dragData.ddel).getXY();  
22956     }
22957 });/*
22958  * Based on:
22959  * Ext JS Library 1.1.1
22960  * Copyright(c) 2006-2007, Ext JS, LLC.
22961  *
22962  * Originally Released Under LGPL - original licence link has changed is not relivant.
22963  *
22964  * Fork - LGPL
22965  * <script type="text/javascript">
22966  */
22967 /**
22968  * @class Roo.dd.DropZone
22969  * @extends Roo.dd.DropTarget
22970  * This class provides a container DD instance that proxies for multiple child node targets.<br />
22971  * By default, this class requires that child nodes accepting drop are registered with {@link Roo.dd.Registry}.
22972  * @constructor
22973  * @param {String/HTMLElement/Element} el The container element
22974  * @param {Object} config
22975  */
22976 Roo.dd.DropZone = function(el, config){
22977     Roo.dd.DropZone.superclass.constructor.call(this, el, config);
22978 };
22979
22980 Roo.extend(Roo.dd.DropZone, Roo.dd.DropTarget, {
22981     /**
22982      * Returns a custom data object associated with the DOM node that is the target of the event.  By default
22983      * this looks up the event target in the {@link Roo.dd.Registry}, although you can override this method to
22984      * provide your own custom lookup.
22985      * @param {Event} e The event
22986      * @return {Object} data The custom data
22987      */
22988     getTargetFromEvent : function(e){
22989         return Roo.dd.Registry.getTargetFromEvent(e);
22990     },
22991
22992     /**
22993      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has entered a drop node
22994      * that it has registered.  This method has no default implementation and should be overridden to provide
22995      * node-specific processing if necessary.
22996      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from 
22997      * {@link #getTargetFromEvent} for this node)
22998      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
22999      * @param {Event} e The event
23000      * @param {Object} data An object containing arbitrary data supplied by the drag source
23001      */
23002     onNodeEnter : function(n, dd, e, data){
23003         
23004     },
23005
23006     /**
23007      * Called internally while the DropZone determines that a {@link Roo.dd.DragSource} is over a drop node
23008      * that it has registered.  The default implementation returns this.dropNotAllowed, so it should be
23009      * overridden to provide the proper feedback.
23010      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from
23011      * {@link #getTargetFromEvent} for this node)
23012      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
23013      * @param {Event} e The event
23014      * @param {Object} data An object containing arbitrary data supplied by the drag source
23015      * @return {String} status The CSS class that communicates the drop status back to the source so that the
23016      * underlying {@link Roo.dd.StatusProxy} can be updated
23017      */
23018     onNodeOver : function(n, dd, e, data){
23019         return this.dropAllowed;
23020     },
23021
23022     /**
23023      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has been dragged out of
23024      * the drop node without dropping.  This method has no default implementation and should be overridden to provide
23025      * node-specific processing if necessary.
23026      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from
23027      * {@link #getTargetFromEvent} for this node)
23028      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
23029      * @param {Event} e The event
23030      * @param {Object} data An object containing arbitrary data supplied by the drag source
23031      */
23032     onNodeOut : function(n, dd, e, data){
23033         
23034     },
23035
23036     /**
23037      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has been dropped onto
23038      * the drop node.  The default implementation returns false, so it should be overridden to provide the
23039      * appropriate processing of the drop event and return true so that the drag source's repair action does not run.
23040      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from
23041      * {@link #getTargetFromEvent} for this node)
23042      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
23043      * @param {Event} e The event
23044      * @param {Object} data An object containing arbitrary data supplied by the drag source
23045      * @return {Boolean} True if the drop was valid, else false
23046      */
23047     onNodeDrop : function(n, dd, e, data){
23048         return false;
23049     },
23050
23051     /**
23052      * Called internally while the DropZone determines that a {@link Roo.dd.DragSource} is being dragged over it,
23053      * but not over any of its registered drop nodes.  The default implementation returns this.dropNotAllowed, so
23054      * it should be overridden to provide the proper feedback if necessary.
23055      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
23056      * @param {Event} e The event
23057      * @param {Object} data An object containing arbitrary data supplied by the drag source
23058      * @return {String} status The CSS class that communicates the drop status back to the source so that the
23059      * underlying {@link Roo.dd.StatusProxy} can be updated
23060      */
23061     onContainerOver : function(dd, e, data){
23062         return this.dropNotAllowed;
23063     },
23064
23065     /**
23066      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has been dropped on it,
23067      * but not on any of its registered drop nodes.  The default implementation returns false, so it should be
23068      * overridden to provide the appropriate processing of the drop event if you need the drop zone itself to
23069      * be able to accept drops.  It should return true when valid so that the drag source's repair action does not run.
23070      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
23071      * @param {Event} e The event
23072      * @param {Object} data An object containing arbitrary data supplied by the drag source
23073      * @return {Boolean} True if the drop was valid, else false
23074      */
23075     onContainerDrop : function(dd, e, data){
23076         return false;
23077     },
23078
23079     /**
23080      * The function a {@link Roo.dd.DragSource} calls once to notify this drop zone that the source is now over
23081      * the zone.  The default implementation returns this.dropNotAllowed and expects that only registered drop
23082      * nodes can process drag drop operations, so if you need the drop zone itself to be able to process drops
23083      * you should override this method and provide a custom implementation.
23084      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
23085      * @param {Event} e The event
23086      * @param {Object} data An object containing arbitrary data supplied by the drag source
23087      * @return {String} status The CSS class that communicates the drop status back to the source so that the
23088      * underlying {@link Roo.dd.StatusProxy} can be updated
23089      */
23090     notifyEnter : function(dd, e, data){
23091         return this.dropNotAllowed;
23092     },
23093
23094     /**
23095      * The function a {@link Roo.dd.DragSource} calls continuously while it is being dragged over the drop zone.
23096      * This method will be called on every mouse movement while the drag source is over the drop zone.
23097      * It will call {@link #onNodeOver} while the drag source is over a registered node, and will also automatically
23098      * delegate to the appropriate node-specific methods as necessary when the drag source enters and exits
23099      * registered nodes ({@link #onNodeEnter}, {@link #onNodeOut}). If the drag source is not currently over a
23100      * registered node, it will call {@link #onContainerOver}.
23101      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
23102      * @param {Event} e The event
23103      * @param {Object} data An object containing arbitrary data supplied by the drag source
23104      * @return {String} status The CSS class that communicates the drop status back to the source so that the
23105      * underlying {@link Roo.dd.StatusProxy} can be updated
23106      */
23107     notifyOver : function(dd, e, data){
23108         var n = this.getTargetFromEvent(e);
23109         if(!n){ // not over valid drop target
23110             if(this.lastOverNode){
23111                 this.onNodeOut(this.lastOverNode, dd, e, data);
23112                 this.lastOverNode = null;
23113             }
23114             return this.onContainerOver(dd, e, data);
23115         }
23116         if(this.lastOverNode != n){
23117             if(this.lastOverNode){
23118                 this.onNodeOut(this.lastOverNode, dd, e, data);
23119             }
23120             this.onNodeEnter(n, dd, e, data);
23121             this.lastOverNode = n;
23122         }
23123         return this.onNodeOver(n, dd, e, data);
23124     },
23125
23126     /**
23127      * The function a {@link Roo.dd.DragSource} calls once to notify this drop zone that the source has been dragged
23128      * out of the zone without dropping.  If the drag source is currently over a registered node, the notification
23129      * will be delegated to {@link #onNodeOut} for node-specific handling, otherwise it will be ignored.
23130      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
23131      * @param {Event} e The event
23132      * @param {Object} data An object containing arbitrary data supplied by the drag zone
23133      */
23134     notifyOut : function(dd, e, data){
23135         if(this.lastOverNode){
23136             this.onNodeOut(this.lastOverNode, dd, e, data);
23137             this.lastOverNode = null;
23138         }
23139     },
23140
23141     /**
23142      * The function a {@link Roo.dd.DragSource} calls once to notify this drop zone that the dragged item has
23143      * been dropped on it.  The drag zone will look up the target node based on the event passed in, and if there
23144      * is a node registered for that event, it will delegate to {@link #onNodeDrop} for node-specific handling,
23145      * otherwise it will call {@link #onContainerDrop}.
23146      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
23147      * @param {Event} e The event
23148      * @param {Object} data An object containing arbitrary data supplied by the drag source
23149      * @return {Boolean} True if the drop was valid, else false
23150      */
23151     notifyDrop : function(dd, e, data){
23152         if(this.lastOverNode){
23153             this.onNodeOut(this.lastOverNode, dd, e, data);
23154             this.lastOverNode = null;
23155         }
23156         var n = this.getTargetFromEvent(e);
23157         return n ?
23158             this.onNodeDrop(n, dd, e, data) :
23159             this.onContainerDrop(dd, e, data);
23160     },
23161
23162     // private
23163     triggerCacheRefresh : function(){
23164         Roo.dd.DDM.refreshCache(this.groups);
23165     }  
23166 });/*
23167  * Based on:
23168  * Ext JS Library 1.1.1
23169  * Copyright(c) 2006-2007, Ext JS, LLC.
23170  *
23171  * Originally Released Under LGPL - original licence link has changed is not relivant.
23172  *
23173  * Fork - LGPL
23174  * <script type="text/javascript">
23175  */
23176
23177
23178 /**
23179  * @class Roo.data.SortTypes
23180  * @static
23181  * Defines the default sorting (casting?) comparison functions used when sorting data.
23182  */
23183 Roo.data.SortTypes = {
23184     /**
23185      * Default sort that does nothing
23186      * @param {Mixed} s The value being converted
23187      * @return {Mixed} The comparison value
23188      */
23189     none : function(s){
23190         return s;
23191     },
23192     
23193     /**
23194      * The regular expression used to strip tags
23195      * @type {RegExp}
23196      * @property
23197      */
23198     stripTagsRE : /<\/?[^>]+>/gi,
23199     
23200     /**
23201      * Strips all HTML tags to sort on text only
23202      * @param {Mixed} s The value being converted
23203      * @return {String} The comparison value
23204      */
23205     asText : function(s){
23206         return String(s).replace(this.stripTagsRE, "");
23207     },
23208     
23209     /**
23210      * Strips all HTML tags to sort on text only - Case insensitive
23211      * @param {Mixed} s The value being converted
23212      * @return {String} The comparison value
23213      */
23214     asUCText : function(s){
23215         return String(s).toUpperCase().replace(this.stripTagsRE, "");
23216     },
23217     
23218     /**
23219      * Case insensitive string
23220      * @param {Mixed} s The value being converted
23221      * @return {String} The comparison value
23222      */
23223     asUCString : function(s) {
23224         return String(s).toUpperCase();
23225     },
23226     
23227     /**
23228      * Date sorting
23229      * @param {Mixed} s The value being converted
23230      * @return {Number} The comparison value
23231      */
23232     asDate : function(s) {
23233         if(!s){
23234             return 0;
23235         }
23236         if(s instanceof Date){
23237             return s.getTime();
23238         }
23239         return Date.parse(String(s));
23240     },
23241     
23242     /**
23243      * Float sorting
23244      * @param {Mixed} s The value being converted
23245      * @return {Float} The comparison value
23246      */
23247     asFloat : function(s) {
23248         var val = parseFloat(String(s).replace(/,/g, ""));
23249         if(isNaN(val)) {
23250             val = 0;
23251         }
23252         return val;
23253     },
23254     
23255     /**
23256      * Integer sorting
23257      * @param {Mixed} s The value being converted
23258      * @return {Number} The comparison value
23259      */
23260     asInt : function(s) {
23261         var val = parseInt(String(s).replace(/,/g, ""));
23262         if(isNaN(val)) {
23263             val = 0;
23264         }
23265         return val;
23266     }
23267 };/*
23268  * Based on:
23269  * Ext JS Library 1.1.1
23270  * Copyright(c) 2006-2007, Ext JS, LLC.
23271  *
23272  * Originally Released Under LGPL - original licence link has changed is not relivant.
23273  *
23274  * Fork - LGPL
23275  * <script type="text/javascript">
23276  */
23277
23278 /**
23279 * @class Roo.data.Record
23280  * Instances of this class encapsulate both record <em>definition</em> information, and record
23281  * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
23282  * to access Records cached in an {@link Roo.data.Store} object.<br>
23283  * <p>
23284  * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
23285  * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
23286  * objects.<br>
23287  * <p>
23288  * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
23289  * @constructor
23290  * This constructor should not be used to create Record objects. Instead, use the constructor generated by
23291  * {@link #create}. The parameters are the same.
23292  * @param {Array} data An associative Array of data values keyed by the field name.
23293  * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
23294  * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
23295  * not specified an integer id is generated.
23296  */
23297 Roo.data.Record = function(data, id){
23298     this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
23299     this.data = data;
23300 };
23301
23302 /**
23303  * Generate a constructor for a specific record layout.
23304  * @param {Array} o An Array of field definition objects which specify field names, and optionally,
23305  * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
23306  * Each field definition object may contain the following properties: <ul>
23307  * <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,
23308  * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
23309  * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
23310  * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
23311  * is being used, then this is a string containing the javascript expression to reference the data relative to 
23312  * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
23313  * to the data item relative to the record element. If the mapping expression is the same as the field name,
23314  * this may be omitted.</p></li>
23315  * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
23316  * <ul><li>auto (Default, implies no conversion)</li>
23317  * <li>string</li>
23318  * <li>int</li>
23319  * <li>float</li>
23320  * <li>boolean</li>
23321  * <li>date</li></ul></p></li>
23322  * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
23323  * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
23324  * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
23325  * by the Reader into an object that will be stored in the Record. It is passed the
23326  * following parameters:<ul>
23327  * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
23328  * </ul></p></li>
23329  * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
23330  * </ul>
23331  * <br>usage:<br><pre><code>
23332 var TopicRecord = Roo.data.Record.create(
23333     {name: 'title', mapping: 'topic_title'},
23334     {name: 'author', mapping: 'username'},
23335     {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
23336     {name: 'lastPost', mapping: 'post_time', type: 'date'},
23337     {name: 'lastPoster', mapping: 'user2'},
23338     {name: 'excerpt', mapping: 'post_text'}
23339 );
23340
23341 var myNewRecord = new TopicRecord({
23342     title: 'Do my job please',
23343     author: 'noobie',
23344     totalPosts: 1,
23345     lastPost: new Date(),
23346     lastPoster: 'Animal',
23347     excerpt: 'No way dude!'
23348 });
23349 myStore.add(myNewRecord);
23350 </code></pre>
23351  * @method create
23352  * @static
23353  */
23354 Roo.data.Record.create = function(o){
23355     var f = function(){
23356         f.superclass.constructor.apply(this, arguments);
23357     };
23358     Roo.extend(f, Roo.data.Record);
23359     var p = f.prototype;
23360     p.fields = new Roo.util.MixedCollection(false, function(field){
23361         return field.name;
23362     });
23363     for(var i = 0, len = o.length; i < len; i++){
23364         p.fields.add(new Roo.data.Field(o[i]));
23365     }
23366     f.getField = function(name){
23367         return p.fields.get(name);  
23368     };
23369     return f;
23370 };
23371
23372 Roo.data.Record.AUTO_ID = 1000;
23373 Roo.data.Record.EDIT = 'edit';
23374 Roo.data.Record.REJECT = 'reject';
23375 Roo.data.Record.COMMIT = 'commit';
23376
23377 Roo.data.Record.prototype = {
23378     /**
23379      * Readonly flag - true if this record has been modified.
23380      * @type Boolean
23381      */
23382     dirty : false,
23383     editing : false,
23384     error: null,
23385     modified: null,
23386
23387     // private
23388     join : function(store){
23389         this.store = store;
23390     },
23391
23392     /**
23393      * Set the named field to the specified value.
23394      * @param {String} name The name of the field to set.
23395      * @param {Object} value The value to set the field to.
23396      */
23397     set : function(name, value){
23398         if(this.data[name] == value){
23399             return;
23400         }
23401         this.dirty = true;
23402         if(!this.modified){
23403             this.modified = {};
23404         }
23405         if(typeof this.modified[name] == 'undefined'){
23406             this.modified[name] = this.data[name];
23407         }
23408         this.data[name] = value;
23409         if(!this.editing && this.store){
23410             this.store.afterEdit(this);
23411         }       
23412     },
23413
23414     /**
23415      * Get the value of the named field.
23416      * @param {String} name The name of the field to get the value of.
23417      * @return {Object} The value of the field.
23418      */
23419     get : function(name){
23420         return this.data[name]; 
23421     },
23422
23423     // private
23424     beginEdit : function(){
23425         this.editing = true;
23426         this.modified = {}; 
23427     },
23428
23429     // private
23430     cancelEdit : function(){
23431         this.editing = false;
23432         delete this.modified;
23433     },
23434
23435     // private
23436     endEdit : function(){
23437         this.editing = false;
23438         if(this.dirty && this.store){
23439             this.store.afterEdit(this);
23440         }
23441     },
23442
23443     /**
23444      * Usually called by the {@link Roo.data.Store} which owns the Record.
23445      * Rejects all changes made to the Record since either creation, or the last commit operation.
23446      * Modified fields are reverted to their original values.
23447      * <p>
23448      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
23449      * of reject operations.
23450      */
23451     reject : function(){
23452         var m = this.modified;
23453         for(var n in m){
23454             if(typeof m[n] != "function"){
23455                 this.data[n] = m[n];
23456             }
23457         }
23458         this.dirty = false;
23459         delete this.modified;
23460         this.editing = false;
23461         if(this.store){
23462             this.store.afterReject(this);
23463         }
23464     },
23465
23466     /**
23467      * Usually called by the {@link Roo.data.Store} which owns the Record.
23468      * Commits all changes made to the Record since either creation, or the last commit operation.
23469      * <p>
23470      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
23471      * of commit operations.
23472      */
23473     commit : function(){
23474         this.dirty = false;
23475         delete this.modified;
23476         this.editing = false;
23477         if(this.store){
23478             this.store.afterCommit(this);
23479         }
23480     },
23481
23482     // private
23483     hasError : function(){
23484         return this.error != null;
23485     },
23486
23487     // private
23488     clearError : function(){
23489         this.error = null;
23490     },
23491
23492     /**
23493      * Creates a copy of this record.
23494      * @param {String} id (optional) A new record id if you don't want to use this record's id
23495      * @return {Record}
23496      */
23497     copy : function(newId) {
23498         return new this.constructor(Roo.apply({}, this.data), newId || this.id);
23499     }
23500 };/*
23501  * Based on:
23502  * Ext JS Library 1.1.1
23503  * Copyright(c) 2006-2007, Ext JS, LLC.
23504  *
23505  * Originally Released Under LGPL - original licence link has changed is not relivant.
23506  *
23507  * Fork - LGPL
23508  * <script type="text/javascript">
23509  */
23510
23511
23512
23513 /**
23514  * @class Roo.data.Store
23515  * @extends Roo.util.Observable
23516  * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
23517  * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
23518  * <p>
23519  * 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
23520  * has no knowledge of the format of the data returned by the Proxy.<br>
23521  * <p>
23522  * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
23523  * instances from the data object. These records are cached and made available through accessor functions.
23524  * @constructor
23525  * Creates a new Store.
23526  * @param {Object} config A config object containing the objects needed for the Store to access data,
23527  * and read the data into Records.
23528  */
23529 Roo.data.Store = function(config){
23530     this.data = new Roo.util.MixedCollection(false);
23531     this.data.getKey = function(o){
23532         return o.id;
23533     };
23534     this.baseParams = {};
23535     // private
23536     this.paramNames = {
23537         "start" : "start",
23538         "limit" : "limit",
23539         "sort" : "sort",
23540         "dir" : "dir",
23541         "multisort" : "_multisort"
23542     };
23543
23544     if(config && config.data){
23545         this.inlineData = config.data;
23546         delete config.data;
23547     }
23548
23549     Roo.apply(this, config);
23550     
23551     if(this.reader){ // reader passed
23552         this.reader = Roo.factory(this.reader, Roo.data);
23553         this.reader.xmodule = this.xmodule || false;
23554         if(!this.recordType){
23555             this.recordType = this.reader.recordType;
23556         }
23557         if(this.reader.onMetaChange){
23558             this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
23559         }
23560     }
23561
23562     if(this.recordType){
23563         this.fields = this.recordType.prototype.fields;
23564     }
23565     this.modified = [];
23566
23567     this.addEvents({
23568         /**
23569          * @event datachanged
23570          * Fires when the data cache has changed, and a widget which is using this Store
23571          * as a Record cache should refresh its view.
23572          * @param {Store} this
23573          */
23574         datachanged : true,
23575         /**
23576          * @event metachange
23577          * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
23578          * @param {Store} this
23579          * @param {Object} meta The JSON metadata
23580          */
23581         metachange : true,
23582         /**
23583          * @event add
23584          * Fires when Records have been added to the Store
23585          * @param {Store} this
23586          * @param {Roo.data.Record[]} records The array of Records added
23587          * @param {Number} index The index at which the record(s) were added
23588          */
23589         add : true,
23590         /**
23591          * @event remove
23592          * Fires when a Record has been removed from the Store
23593          * @param {Store} this
23594          * @param {Roo.data.Record} record The Record that was removed
23595          * @param {Number} index The index at which the record was removed
23596          */
23597         remove : true,
23598         /**
23599          * @event update
23600          * Fires when a Record has been updated
23601          * @param {Store} this
23602          * @param {Roo.data.Record} record The Record that was updated
23603          * @param {String} operation The update operation being performed.  Value may be one of:
23604          * <pre><code>
23605  Roo.data.Record.EDIT
23606  Roo.data.Record.REJECT
23607  Roo.data.Record.COMMIT
23608          * </code></pre>
23609          */
23610         update : true,
23611         /**
23612          * @event clear
23613          * Fires when the data cache has been cleared.
23614          * @param {Store} this
23615          */
23616         clear : true,
23617         /**
23618          * @event beforeload
23619          * Fires before a request is made for a new data object.  If the beforeload handler returns false
23620          * the load action will be canceled.
23621          * @param {Store} this
23622          * @param {Object} options The loading options that were specified (see {@link #load} for details)
23623          */
23624         beforeload : true,
23625         /**
23626          * @event beforeloadadd
23627          * Fires after a new set of Records has been loaded.
23628          * @param {Store} this
23629          * @param {Roo.data.Record[]} records The Records that were loaded
23630          * @param {Object} options The loading options that were specified (see {@link #load} for details)
23631          */
23632         beforeloadadd : true,
23633         /**
23634          * @event load
23635          * Fires after a new set of Records has been loaded, before they are added to the store.
23636          * @param {Store} this
23637          * @param {Roo.data.Record[]} records The Records that were loaded
23638          * @param {Object} options The loading options that were specified (see {@link #load} for details)
23639          * @params {Object} return from reader
23640          */
23641         load : true,
23642         /**
23643          * @event loadexception
23644          * Fires if an exception occurs in the Proxy during loading.
23645          * Called with the signature of the Proxy's "loadexception" event.
23646          * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
23647          * 
23648          * @param {Proxy} 
23649          * @param {Object} return from JsonData.reader() - success, totalRecords, records
23650          * @param {Object} load options 
23651          * @param {Object} jsonData from your request (normally this contains the Exception)
23652          */
23653         loadexception : true
23654     });
23655     
23656     if(this.proxy){
23657         this.proxy = Roo.factory(this.proxy, Roo.data);
23658         this.proxy.xmodule = this.xmodule || false;
23659         this.relayEvents(this.proxy,  ["loadexception"]);
23660     }
23661     this.sortToggle = {};
23662     this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
23663
23664     Roo.data.Store.superclass.constructor.call(this);
23665
23666     if(this.inlineData){
23667         this.loadData(this.inlineData);
23668         delete this.inlineData;
23669     }
23670 };
23671
23672 Roo.extend(Roo.data.Store, Roo.util.Observable, {
23673      /**
23674     * @cfg {boolean} isLocal   flag if data is locally available (and can be always looked up
23675     * without a remote query - used by combo/forms at present.
23676     */
23677     
23678     /**
23679     * @cfg {Roo.data.DataProxy} proxy [required] The Proxy object which provides access to a data object.
23680     */
23681     /**
23682     * @cfg {Array} data Inline data to be loaded when the store is initialized.
23683     */
23684     /**
23685     * @cfg {Roo.data.DataReader} reader [required]  The Reader object which processes the data object and returns
23686     * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
23687     */
23688     /**
23689     * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
23690     * on any HTTP request
23691     */
23692     /**
23693     * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
23694     */
23695     /**
23696     * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
23697     */
23698     multiSort: false,
23699     /**
23700     * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
23701     * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
23702     */
23703     remoteSort : false,
23704
23705     /**
23706     * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
23707      * loaded or when a record is removed. (defaults to false).
23708     */
23709     pruneModifiedRecords : false,
23710
23711     // private
23712     lastOptions : null,
23713
23714     /**
23715      * Add Records to the Store and fires the add event.
23716      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
23717      */
23718     add : function(records){
23719         records = [].concat(records);
23720         for(var i = 0, len = records.length; i < len; i++){
23721             records[i].join(this);
23722         }
23723         var index = this.data.length;
23724         this.data.addAll(records);
23725         this.fireEvent("add", this, records, index);
23726     },
23727
23728     /**
23729      * Remove a Record from the Store and fires the remove event.
23730      * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
23731      */
23732     remove : function(record){
23733         var index = this.data.indexOf(record);
23734         this.data.removeAt(index);
23735  
23736         if(this.pruneModifiedRecords){
23737             this.modified.remove(record);
23738         }
23739         this.fireEvent("remove", this, record, index);
23740     },
23741
23742     /**
23743      * Remove all Records from the Store and fires the clear event.
23744      */
23745     removeAll : function(){
23746         this.data.clear();
23747         if(this.pruneModifiedRecords){
23748             this.modified = [];
23749         }
23750         this.fireEvent("clear", this);
23751     },
23752
23753     /**
23754      * Inserts Records to the Store at the given index and fires the add event.
23755      * @param {Number} index The start index at which to insert the passed Records.
23756      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
23757      */
23758     insert : function(index, records){
23759         records = [].concat(records);
23760         for(var i = 0, len = records.length; i < len; i++){
23761             this.data.insert(index, records[i]);
23762             records[i].join(this);
23763         }
23764         this.fireEvent("add", this, records, index);
23765     },
23766
23767     /**
23768      * Get the index within the cache of the passed Record.
23769      * @param {Roo.data.Record} record The Roo.data.Record object to to find.
23770      * @return {Number} The index of the passed Record. Returns -1 if not found.
23771      */
23772     indexOf : function(record){
23773         return this.data.indexOf(record);
23774     },
23775
23776     /**
23777      * Get the index within the cache of the Record with the passed id.
23778      * @param {String} id The id of the Record to find.
23779      * @return {Number} The index of the Record. Returns -1 if not found.
23780      */
23781     indexOfId : function(id){
23782         return this.data.indexOfKey(id);
23783     },
23784
23785     /**
23786      * Get the Record with the specified id.
23787      * @param {String} id The id of the Record to find.
23788      * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
23789      */
23790     getById : function(id){
23791         return this.data.key(id);
23792     },
23793
23794     /**
23795      * Get the Record at the specified index.
23796      * @param {Number} index The index of the Record to find.
23797      * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
23798      */
23799     getAt : function(index){
23800         return this.data.itemAt(index);
23801     },
23802
23803     /**
23804      * Returns a range of Records between specified indices.
23805      * @param {Number} startIndex (optional) The starting index (defaults to 0)
23806      * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
23807      * @return {Roo.data.Record[]} An array of Records
23808      */
23809     getRange : function(start, end){
23810         return this.data.getRange(start, end);
23811     },
23812
23813     // private
23814     storeOptions : function(o){
23815         o = Roo.apply({}, o);
23816         delete o.callback;
23817         delete o.scope;
23818         this.lastOptions = o;
23819     },
23820
23821     /**
23822      * Loads the Record cache from the configured Proxy using the configured Reader.
23823      * <p>
23824      * If using remote paging, then the first load call must specify the <em>start</em>
23825      * and <em>limit</em> properties in the options.params property to establish the initial
23826      * position within the dataset, and the number of Records to cache on each read from the Proxy.
23827      * <p>
23828      * <strong>It is important to note that for remote data sources, loading is asynchronous,
23829      * and this call will return before the new data has been loaded. Perform any post-processing
23830      * in a callback function, or in a "load" event handler.</strong>
23831      * <p>
23832      * @param {Object} options An object containing properties which control loading options:<ul>
23833      * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
23834      * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
23835      * passed the following arguments:<ul>
23836      * <li>r : Roo.data.Record[]</li>
23837      * <li>options: Options object from the load call</li>
23838      * <li>success: Boolean success indicator</li></ul></li>
23839      * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
23840      * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
23841      * </ul>
23842      */
23843     load : function(options){
23844         options = options || {};
23845         if(this.fireEvent("beforeload", this, options) !== false){
23846             this.storeOptions(options);
23847             var p = Roo.apply(options.params || {}, this.baseParams);
23848             // if meta was not loaded from remote source.. try requesting it.
23849             if (!this.reader.metaFromRemote) {
23850                 p._requestMeta = 1;
23851             }
23852             if(this.sortInfo && this.remoteSort){
23853                 var pn = this.paramNames;
23854                 p[pn["sort"]] = this.sortInfo.field;
23855                 p[pn["dir"]] = this.sortInfo.direction;
23856             }
23857             if (this.multiSort) {
23858                 var pn = this.paramNames;
23859                 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
23860             }
23861             
23862             this.proxy.load(p, this.reader, this.loadRecords, this, options);
23863         }
23864     },
23865
23866     /**
23867      * Reloads the Record cache from the configured Proxy using the configured Reader and
23868      * the options from the last load operation performed.
23869      * @param {Object} options (optional) An object containing properties which may override the options
23870      * used in the last load operation. See {@link #load} for details (defaults to null, in which case
23871      * the most recently used options are reused).
23872      */
23873     reload : function(options){
23874         this.load(Roo.applyIf(options||{}, this.lastOptions));
23875     },
23876
23877     // private
23878     // Called as a callback by the Reader during a load operation.
23879     loadRecords : function(o, options, success){
23880         if(!o || success === false){
23881             if(success !== false){
23882                 this.fireEvent("load", this, [], options, o);
23883             }
23884             if(options.callback){
23885                 options.callback.call(options.scope || this, [], options, false);
23886             }
23887             return;
23888         }
23889         // if data returned failure - throw an exception.
23890         if (o.success === false) {
23891             // show a message if no listener is registered.
23892             if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
23893                     Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
23894             }
23895             // loadmask wil be hooked into this..
23896             this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
23897             return;
23898         }
23899         var r = o.records, t = o.totalRecords || r.length;
23900         
23901         this.fireEvent("beforeloadadd", this, r, options, o);
23902         
23903         if(!options || options.add !== true){
23904             if(this.pruneModifiedRecords){
23905                 this.modified = [];
23906             }
23907             for(var i = 0, len = r.length; i < len; i++){
23908                 r[i].join(this);
23909             }
23910             if(this.snapshot){
23911                 this.data = this.snapshot;
23912                 delete this.snapshot;
23913             }
23914             this.data.clear();
23915             this.data.addAll(r);
23916             this.totalLength = t;
23917             this.applySort();
23918             this.fireEvent("datachanged", this);
23919         }else{
23920             this.totalLength = Math.max(t, this.data.length+r.length);
23921             this.add(r);
23922         }
23923         
23924         if(this.parent && !Roo.isIOS && !this.useNativeIOS && this.parent.emptyTitle.length) {
23925                 
23926             var e = new Roo.data.Record({});
23927
23928             e.set(this.parent.displayField, this.parent.emptyTitle);
23929             e.set(this.parent.valueField, '');
23930
23931             this.insert(0, e);
23932         }
23933             
23934         this.fireEvent("load", this, r, options, o);
23935         if(options.callback){
23936             options.callback.call(options.scope || this, r, options, true);
23937         }
23938     },
23939
23940
23941     /**
23942      * Loads data from a passed data block. A Reader which understands the format of the data
23943      * must have been configured in the constructor.
23944      * @param {Object} data The data block from which to read the Records.  The format of the data expected
23945      * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
23946      * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
23947      */
23948     loadData : function(o, append){
23949         var r = this.reader.readRecords(o);
23950         this.loadRecords(r, {add: append}, true);
23951     },
23952     
23953      /**
23954      * using 'cn' the nested child reader read the child array into it's child stores.
23955      * @param {Object} rec The record with a 'children array
23956      */
23957     loadDataFromChildren : function(rec)
23958     {
23959         this.loadData(this.reader.toLoadData(rec));
23960     },
23961     
23962
23963     /**
23964      * Gets the number of cached records.
23965      * <p>
23966      * <em>If using paging, this may not be the total size of the dataset. If the data object
23967      * used by the Reader contains the dataset size, then the getTotalCount() function returns
23968      * the data set size</em>
23969      */
23970     getCount : function(){
23971         return this.data.length || 0;
23972     },
23973
23974     /**
23975      * Gets the total number of records in the dataset as returned by the server.
23976      * <p>
23977      * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
23978      * the dataset size</em>
23979      */
23980     getTotalCount : function(){
23981         return this.totalLength || 0;
23982     },
23983
23984     /**
23985      * Returns the sort state of the Store as an object with two properties:
23986      * <pre><code>
23987  field {String} The name of the field by which the Records are sorted
23988  direction {String} The sort order, "ASC" or "DESC"
23989      * </code></pre>
23990      */
23991     getSortState : function(){
23992         return this.sortInfo;
23993     },
23994
23995     // private
23996     applySort : function(){
23997         if(this.sortInfo && !this.remoteSort){
23998             var s = this.sortInfo, f = s.field;
23999             var st = this.fields.get(f).sortType;
24000             var fn = function(r1, r2){
24001                 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
24002                 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
24003             };
24004             this.data.sort(s.direction, fn);
24005             if(this.snapshot && this.snapshot != this.data){
24006                 this.snapshot.sort(s.direction, fn);
24007             }
24008         }
24009     },
24010
24011     /**
24012      * Sets the default sort column and order to be used by the next load operation.
24013      * @param {String} fieldName The name of the field to sort by.
24014      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
24015      */
24016     setDefaultSort : function(field, dir){
24017         this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
24018     },
24019
24020     /**
24021      * Sort the Records.
24022      * If remote sorting is used, the sort is performed on the server, and the cache is
24023      * reloaded. If local sorting is used, the cache is sorted internally.
24024      * @param {String} fieldName The name of the field to sort by.
24025      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
24026      */
24027     sort : function(fieldName, dir){
24028         var f = this.fields.get(fieldName);
24029         if(!dir){
24030             this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
24031             
24032             if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
24033                 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
24034             }else{
24035                 dir = f.sortDir;
24036             }
24037         }
24038         this.sortToggle[f.name] = dir;
24039         this.sortInfo = {field: f.name, direction: dir};
24040         if(!this.remoteSort){
24041             this.applySort();
24042             this.fireEvent("datachanged", this);
24043         }else{
24044             this.load(this.lastOptions);
24045         }
24046     },
24047
24048     /**
24049      * Calls the specified function for each of the Records in the cache.
24050      * @param {Function} fn The function to call. The Record is passed as the first parameter.
24051      * Returning <em>false</em> aborts and exits the iteration.
24052      * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
24053      */
24054     each : function(fn, scope){
24055         this.data.each(fn, scope);
24056     },
24057
24058     /**
24059      * Gets all records modified since the last commit.  Modified records are persisted across load operations
24060      * (e.g., during paging).
24061      * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
24062      */
24063     getModifiedRecords : function(){
24064         return this.modified;
24065     },
24066
24067     // private
24068     createFilterFn : function(property, value, anyMatch){
24069         if(!value.exec){ // not a regex
24070             value = String(value);
24071             if(value.length == 0){
24072                 return false;
24073             }
24074             value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
24075         }
24076         return function(r){
24077             return value.test(r.data[property]);
24078         };
24079     },
24080
24081     /**
24082      * Sums the value of <i>property</i> for each record between start and end and returns the result.
24083      * @param {String} property A field on your records
24084      * @param {Number} start The record index to start at (defaults to 0)
24085      * @param {Number} end The last record index to include (defaults to length - 1)
24086      * @return {Number} The sum
24087      */
24088     sum : function(property, start, end){
24089         var rs = this.data.items, v = 0;
24090         start = start || 0;
24091         end = (end || end === 0) ? end : rs.length-1;
24092
24093         for(var i = start; i <= end; i++){
24094             v += (rs[i].data[property] || 0);
24095         }
24096         return v;
24097     },
24098
24099     /**
24100      * Filter the records by a specified property.
24101      * @param {String} field A field on your records
24102      * @param {String/RegExp} value Either a string that the field
24103      * should start with or a RegExp to test against the field
24104      * @param {Boolean} anyMatch True to match any part not just the beginning
24105      */
24106     filter : function(property, value, anyMatch){
24107         var fn = this.createFilterFn(property, value, anyMatch);
24108         return fn ? this.filterBy(fn) : this.clearFilter();
24109     },
24110
24111     /**
24112      * Filter by a function. The specified function will be called with each
24113      * record in this data source. If the function returns true the record is included,
24114      * otherwise it is filtered.
24115      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
24116      * @param {Object} scope (optional) The scope of the function (defaults to this)
24117      */
24118     filterBy : function(fn, scope){
24119         this.snapshot = this.snapshot || this.data;
24120         this.data = this.queryBy(fn, scope||this);
24121         this.fireEvent("datachanged", this);
24122     },
24123
24124     /**
24125      * Query the records by a specified property.
24126      * @param {String} field A field on your records
24127      * @param {String/RegExp} value Either a string that the field
24128      * should start with or a RegExp to test against the field
24129      * @param {Boolean} anyMatch True to match any part not just the beginning
24130      * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
24131      */
24132     query : function(property, value, anyMatch){
24133         var fn = this.createFilterFn(property, value, anyMatch);
24134         return fn ? this.queryBy(fn) : this.data.clone();
24135     },
24136
24137     /**
24138      * Query by a function. The specified function will be called with each
24139      * record in this data source. If the function returns true the record is included
24140      * in the results.
24141      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
24142      * @param {Object} scope (optional) The scope of the function (defaults to this)
24143       @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
24144      **/
24145     queryBy : function(fn, scope){
24146         var data = this.snapshot || this.data;
24147         return data.filterBy(fn, scope||this);
24148     },
24149
24150     /**
24151      * Collects unique values for a particular dataIndex from this store.
24152      * @param {String} dataIndex The property to collect
24153      * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
24154      * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
24155      * @return {Array} An array of the unique values
24156      **/
24157     collect : function(dataIndex, allowNull, bypassFilter){
24158         var d = (bypassFilter === true && this.snapshot) ?
24159                 this.snapshot.items : this.data.items;
24160         var v, sv, r = [], l = {};
24161         for(var i = 0, len = d.length; i < len; i++){
24162             v = d[i].data[dataIndex];
24163             sv = String(v);
24164             if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
24165                 l[sv] = true;
24166                 r[r.length] = v;
24167             }
24168         }
24169         return r;
24170     },
24171
24172     /**
24173      * Revert to a view of the Record cache with no filtering applied.
24174      * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
24175      */
24176     clearFilter : function(suppressEvent){
24177         if(this.snapshot && this.snapshot != this.data){
24178             this.data = this.snapshot;
24179             delete this.snapshot;
24180             if(suppressEvent !== true){
24181                 this.fireEvent("datachanged", this);
24182             }
24183         }
24184     },
24185
24186     // private
24187     afterEdit : function(record){
24188         if(this.modified.indexOf(record) == -1){
24189             this.modified.push(record);
24190         }
24191         this.fireEvent("update", this, record, Roo.data.Record.EDIT);
24192     },
24193     
24194     // private
24195     afterReject : function(record){
24196         this.modified.remove(record);
24197         this.fireEvent("update", this, record, Roo.data.Record.REJECT);
24198     },
24199
24200     // private
24201     afterCommit : function(record){
24202         this.modified.remove(record);
24203         this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
24204     },
24205
24206     /**
24207      * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
24208      * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
24209      */
24210     commitChanges : function(){
24211         var m = this.modified.slice(0);
24212         this.modified = [];
24213         for(var i = 0, len = m.length; i < len; i++){
24214             m[i].commit();
24215         }
24216     },
24217
24218     /**
24219      * Cancel outstanding changes on all changed records.
24220      */
24221     rejectChanges : function(){
24222         var m = this.modified.slice(0);
24223         this.modified = [];
24224         for(var i = 0, len = m.length; i < len; i++){
24225             m[i].reject();
24226         }
24227     },
24228
24229     onMetaChange : function(meta, rtype, o){
24230         this.recordType = rtype;
24231         this.fields = rtype.prototype.fields;
24232         delete this.snapshot;
24233         this.sortInfo = meta.sortInfo || this.sortInfo;
24234         this.modified = [];
24235         this.fireEvent('metachange', this, this.reader.meta);
24236     },
24237     
24238     moveIndex : function(data, type)
24239     {
24240         var index = this.indexOf(data);
24241         
24242         var newIndex = index + type;
24243         
24244         this.remove(data);
24245         
24246         this.insert(newIndex, data);
24247         
24248     }
24249 });/*
24250  * Based on:
24251  * Ext JS Library 1.1.1
24252  * Copyright(c) 2006-2007, Ext JS, LLC.
24253  *
24254  * Originally Released Under LGPL - original licence link has changed is not relivant.
24255  *
24256  * Fork - LGPL
24257  * <script type="text/javascript">
24258  */
24259
24260 /**
24261  * @class Roo.data.SimpleStore
24262  * @extends Roo.data.Store
24263  * Small helper class to make creating Stores from Array data easier.
24264  * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
24265  * @cfg {Array} fields An array of field definition objects, or field name strings.
24266  * @cfg {Object} an existing reader (eg. copied from another store)
24267  * @cfg {Array} data The multi-dimensional array of data
24268  * @cfg {Roo.data.DataProxy} proxy [not-required]  
24269  * @cfg {Roo.data.Reader} reader  [not-required] 
24270  * @constructor
24271  * @param {Object} config
24272  */
24273 Roo.data.SimpleStore = function(config)
24274 {
24275     Roo.data.SimpleStore.superclass.constructor.call(this, {
24276         isLocal : true,
24277         reader: typeof(config.reader) != 'undefined' ? config.reader : new Roo.data.ArrayReader({
24278                 id: config.id
24279             },
24280             Roo.data.Record.create(config.fields)
24281         ),
24282         proxy : new Roo.data.MemoryProxy(config.data)
24283     });
24284     this.load();
24285 };
24286 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
24287  * Based on:
24288  * Ext JS Library 1.1.1
24289  * Copyright(c) 2006-2007, Ext JS, LLC.
24290  *
24291  * Originally Released Under LGPL - original licence link has changed is not relivant.
24292  *
24293  * Fork - LGPL
24294  * <script type="text/javascript">
24295  */
24296
24297 /**
24298 /**
24299  * @extends Roo.data.Store
24300  * @class Roo.data.JsonStore
24301  * Small helper class to make creating Stores for JSON data easier. <br/>
24302 <pre><code>
24303 var store = new Roo.data.JsonStore({
24304     url: 'get-images.php',
24305     root: 'images',
24306     fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
24307 });
24308 </code></pre>
24309  * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
24310  * JsonReader and HttpProxy (unless inline data is provided).</b>
24311  * @cfg {Array} fields An array of field definition objects, or field name strings.
24312  * @constructor
24313  * @param {Object} config
24314  */
24315 Roo.data.JsonStore = function(c){
24316     Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
24317         proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
24318         reader: new Roo.data.JsonReader(c, c.fields)
24319     }));
24320 };
24321 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
24322  * Based on:
24323  * Ext JS Library 1.1.1
24324  * Copyright(c) 2006-2007, Ext JS, LLC.
24325  *
24326  * Originally Released Under LGPL - original licence link has changed is not relivant.
24327  *
24328  * Fork - LGPL
24329  * <script type="text/javascript">
24330  */
24331
24332  
24333 Roo.data.Field = function(config){
24334     if(typeof config == "string"){
24335         config = {name: config};
24336     }
24337     Roo.apply(this, config);
24338     
24339     if(!this.type){
24340         this.type = "auto";
24341     }
24342     
24343     var st = Roo.data.SortTypes;
24344     // named sortTypes are supported, here we look them up
24345     if(typeof this.sortType == "string"){
24346         this.sortType = st[this.sortType];
24347     }
24348     
24349     // set default sortType for strings and dates
24350     if(!this.sortType){
24351         switch(this.type){
24352             case "string":
24353                 this.sortType = st.asUCString;
24354                 break;
24355             case "date":
24356                 this.sortType = st.asDate;
24357                 break;
24358             default:
24359                 this.sortType = st.none;
24360         }
24361     }
24362
24363     // define once
24364     var stripRe = /[\$,%]/g;
24365
24366     // prebuilt conversion function for this field, instead of
24367     // switching every time we're reading a value
24368     if(!this.convert){
24369         var cv, dateFormat = this.dateFormat;
24370         switch(this.type){
24371             case "":
24372             case "auto":
24373             case undefined:
24374                 cv = function(v){ return v; };
24375                 break;
24376             case "string":
24377                 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
24378                 break;
24379             case "int":
24380                 cv = function(v){
24381                     return v !== undefined && v !== null && v !== '' ?
24382                            parseInt(String(v).replace(stripRe, ""), 10) : '';
24383                     };
24384                 break;
24385             case "float":
24386                 cv = function(v){
24387                     return v !== undefined && v !== null && v !== '' ?
24388                            parseFloat(String(v).replace(stripRe, ""), 10) : ''; 
24389                     };
24390                 break;
24391             case "bool":
24392             case "boolean":
24393                 cv = function(v){ return v === true || v === "true" || v == 1; };
24394                 break;
24395             case "date":
24396                 cv = function(v){
24397                     if(!v){
24398                         return '';
24399                     }
24400                     if(v instanceof Date){
24401                         return v;
24402                     }
24403                     if(dateFormat){
24404                         if(dateFormat == "timestamp"){
24405                             return new Date(v*1000);
24406                         }
24407                         return Date.parseDate(v, dateFormat);
24408                     }
24409                     var parsed = Date.parse(v);
24410                     return parsed ? new Date(parsed) : null;
24411                 };
24412              break;
24413             
24414         }
24415         this.convert = cv;
24416     }
24417 };
24418
24419 Roo.data.Field.prototype = {
24420     dateFormat: null,
24421     defaultValue: "",
24422     mapping: null,
24423     sortType : null,
24424     sortDir : "ASC"
24425 };/*
24426  * Based on:
24427  * Ext JS Library 1.1.1
24428  * Copyright(c) 2006-2007, Ext JS, LLC.
24429  *
24430  * Originally Released Under LGPL - original licence link has changed is not relivant.
24431  *
24432  * Fork - LGPL
24433  * <script type="text/javascript">
24434  */
24435  
24436 // Base class for reading structured data from a data source.  This class is intended to be
24437 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
24438
24439 /**
24440  * @class Roo.data.DataReader
24441  * @abstract
24442  * Base class for reading structured data from a data source.  This class is intended to be
24443  * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
24444  */
24445
24446 Roo.data.DataReader = function(meta, recordType){
24447     
24448     this.meta = meta;
24449     
24450     this.recordType = recordType instanceof Array ? 
24451         Roo.data.Record.create(recordType) : recordType;
24452 };
24453
24454 Roo.data.DataReader.prototype = {
24455     
24456     
24457     readerType : 'Data',
24458      /**
24459      * Create an empty record
24460      * @param {Object} data (optional) - overlay some values
24461      * @return {Roo.data.Record} record created.
24462      */
24463     newRow :  function(d) {
24464         var da =  {};
24465         this.recordType.prototype.fields.each(function(c) {
24466             switch( c.type) {
24467                 case 'int' : da[c.name] = 0; break;
24468                 case 'date' : da[c.name] = new Date(); break;
24469                 case 'float' : da[c.name] = 0.0; break;
24470                 case 'boolean' : da[c.name] = false; break;
24471                 default : da[c.name] = ""; break;
24472             }
24473             
24474         });
24475         return new this.recordType(Roo.apply(da, d));
24476     }
24477     
24478     
24479 };/*
24480  * Based on:
24481  * Ext JS Library 1.1.1
24482  * Copyright(c) 2006-2007, Ext JS, LLC.
24483  *
24484  * Originally Released Under LGPL - original licence link has changed is not relivant.
24485  *
24486  * Fork - LGPL
24487  * <script type="text/javascript">
24488  */
24489
24490 /**
24491  * @class Roo.data.DataProxy
24492  * @extends Roo.util.Observable
24493  * @abstract
24494  * This class is an abstract base class for implementations which provide retrieval of
24495  * unformatted data objects.<br>
24496  * <p>
24497  * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
24498  * (of the appropriate type which knows how to parse the data object) to provide a block of
24499  * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
24500  * <p>
24501  * Custom implementations must implement the load method as described in
24502  * {@link Roo.data.HttpProxy#load}.
24503  */
24504 Roo.data.DataProxy = function(){
24505     this.addEvents({
24506         /**
24507          * @event beforeload
24508          * Fires before a network request is made to retrieve a data object.
24509          * @param {Object} This DataProxy object.
24510          * @param {Object} params The params parameter to the load function.
24511          */
24512         beforeload : true,
24513         /**
24514          * @event load
24515          * Fires before the load method's callback is called.
24516          * @param {Object} This DataProxy object.
24517          * @param {Object} o The data object.
24518          * @param {Object} arg The callback argument object passed to the load function.
24519          */
24520         load : true,
24521         /**
24522          * @event loadexception
24523          * Fires if an Exception occurs during data retrieval.
24524          * @param {Object} This DataProxy object.
24525          * @param {Object} o The data object.
24526          * @param {Object} arg The callback argument object passed to the load function.
24527          * @param {Object} e The Exception.
24528          */
24529         loadexception : true
24530     });
24531     Roo.data.DataProxy.superclass.constructor.call(this);
24532 };
24533
24534 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
24535
24536     /**
24537      * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
24538      */
24539 /*
24540  * Based on:
24541  * Ext JS Library 1.1.1
24542  * Copyright(c) 2006-2007, Ext JS, LLC.
24543  *
24544  * Originally Released Under LGPL - original licence link has changed is not relivant.
24545  *
24546  * Fork - LGPL
24547  * <script type="text/javascript">
24548  */
24549 /**
24550  * @class Roo.data.MemoryProxy
24551  * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
24552  * to the Reader when its load method is called.
24553  * @constructor
24554  * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
24555  */
24556 Roo.data.MemoryProxy = function(data){
24557     if (data.data) {
24558         data = data.data;
24559     }
24560     Roo.data.MemoryProxy.superclass.constructor.call(this);
24561     this.data = data;
24562 };
24563
24564 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
24565     
24566     /**
24567      * Load data from the requested source (in this case an in-memory
24568      * data object passed to the constructor), read the data object into
24569      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
24570      * process that block using the passed callback.
24571      * @param {Object} params This parameter is not used by the MemoryProxy class.
24572      * @param {Roo.data.DataReader} reader The Reader object which converts the data
24573      * object into a block of Roo.data.Records.
24574      * @param {Function} callback The function into which to pass the block of Roo.data.records.
24575      * The function must be passed <ul>
24576      * <li>The Record block object</li>
24577      * <li>The "arg" argument from the load function</li>
24578      * <li>A boolean success indicator</li>
24579      * </ul>
24580      * @param {Object} scope The scope in which to call the callback
24581      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
24582      */
24583     load : function(params, reader, callback, scope, arg){
24584         params = params || {};
24585         var result;
24586         try {
24587             result = reader.readRecords(params.data ? params.data :this.data);
24588         }catch(e){
24589             this.fireEvent("loadexception", this, arg, null, e);
24590             callback.call(scope, null, arg, false);
24591             return;
24592         }
24593         callback.call(scope, result, arg, true);
24594     },
24595     
24596     // private
24597     update : function(params, records){
24598         
24599     }
24600 });/*
24601  * Based on:
24602  * Ext JS Library 1.1.1
24603  * Copyright(c) 2006-2007, Ext JS, LLC.
24604  *
24605  * Originally Released Under LGPL - original licence link has changed is not relivant.
24606  *
24607  * Fork - LGPL
24608  * <script type="text/javascript">
24609  */
24610 /**
24611  * @class Roo.data.HttpProxy
24612  * @extends Roo.data.DataProxy
24613  * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
24614  * configured to reference a certain URL.<br><br>
24615  * <p>
24616  * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
24617  * from which the running page was served.<br><br>
24618  * <p>
24619  * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
24620  * <p>
24621  * Be aware that to enable the browser to parse an XML document, the server must set
24622  * the Content-Type header in the HTTP response to "text/xml".
24623  * @constructor
24624  * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
24625  * an {@link Roo.data.Connection} object.  If a Connection config is passed, the singleton {@link Roo.Ajax} object
24626  * will be used to make the request.
24627  */
24628 Roo.data.HttpProxy = function(conn){
24629     Roo.data.HttpProxy.superclass.constructor.call(this);
24630     // is conn a conn config or a real conn?
24631     this.conn = conn;
24632     this.useAjax = !conn || !conn.events;
24633   
24634 };
24635
24636 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
24637     // thse are take from connection...
24638     
24639     /**
24640      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
24641      */
24642     /**
24643      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
24644      * extra parameters to each request made by this object. (defaults to undefined)
24645      */
24646     /**
24647      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
24648      *  to each request made by this object. (defaults to undefined)
24649      */
24650     /**
24651      * @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)
24652      */
24653     /**
24654      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
24655      */
24656      /**
24657      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
24658      * @type Boolean
24659      */
24660   
24661
24662     /**
24663      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
24664      * @type Boolean
24665      */
24666     /**
24667      * Return the {@link Roo.data.Connection} object being used by this Proxy.
24668      * @return {Connection} The Connection object. This object may be used to subscribe to events on
24669      * a finer-grained basis than the DataProxy events.
24670      */
24671     getConnection : function(){
24672         return this.useAjax ? Roo.Ajax : this.conn;
24673     },
24674
24675     /**
24676      * Load data from the configured {@link Roo.data.Connection}, read the data object into
24677      * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
24678      * process that block using the passed callback.
24679      * @param {Object} params An object containing properties which are to be used as HTTP parameters
24680      * for the request to the remote server.
24681      * @param {Roo.data.DataReader} reader The Reader object which converts the data
24682      * object into a block of Roo.data.Records.
24683      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
24684      * The function must be passed <ul>
24685      * <li>The Record block object</li>
24686      * <li>The "arg" argument from the load function</li>
24687      * <li>A boolean success indicator</li>
24688      * </ul>
24689      * @param {Object} scope The scope in which to call the callback
24690      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
24691      */
24692     load : function(params, reader, callback, scope, arg){
24693         if(this.fireEvent("beforeload", this, params) !== false){
24694             var  o = {
24695                 params : params || {},
24696                 request: {
24697                     callback : callback,
24698                     scope : scope,
24699                     arg : arg
24700                 },
24701                 reader: reader,
24702                 callback : this.loadResponse,
24703                 scope: this
24704             };
24705             if(this.useAjax){
24706                 Roo.applyIf(o, this.conn);
24707                 if(this.activeRequest){
24708                     Roo.Ajax.abort(this.activeRequest);
24709                 }
24710                 this.activeRequest = Roo.Ajax.request(o);
24711             }else{
24712                 this.conn.request(o);
24713             }
24714         }else{
24715             callback.call(scope||this, null, arg, false);
24716         }
24717     },
24718
24719     // private
24720     loadResponse : function(o, success, response){
24721         delete this.activeRequest;
24722         if(!success){
24723             this.fireEvent("loadexception", this, o, response);
24724             o.request.callback.call(o.request.scope, null, o.request.arg, false);
24725             return;
24726         }
24727         var result;
24728         try {
24729             result = o.reader.read(response);
24730         }catch(e){
24731             this.fireEvent("loadexception", this, o, response, e);
24732             o.request.callback.call(o.request.scope, null, o.request.arg, false);
24733             return;
24734         }
24735         
24736         this.fireEvent("load", this, o, o.request.arg);
24737         o.request.callback.call(o.request.scope, result, o.request.arg, true);
24738     },
24739
24740     // private
24741     update : function(dataSet){
24742
24743     },
24744
24745     // private
24746     updateResponse : function(dataSet){
24747
24748     }
24749 });/*
24750  * Based on:
24751  * Ext JS Library 1.1.1
24752  * Copyright(c) 2006-2007, Ext JS, LLC.
24753  *
24754  * Originally Released Under LGPL - original licence link has changed is not relivant.
24755  *
24756  * Fork - LGPL
24757  * <script type="text/javascript">
24758  */
24759
24760 /**
24761  * @class Roo.data.ScriptTagProxy
24762  * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
24763  * other than the originating domain of the running page.<br><br>
24764  * <p>
24765  * <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
24766  * of the running page, you must use this class, rather than DataProxy.</em><br><br>
24767  * <p>
24768  * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
24769  * source code that is used as the source inside a &lt;script> tag.<br><br>
24770  * <p>
24771  * In order for the browser to process the returned data, the server must wrap the data object
24772  * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
24773  * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
24774  * depending on whether the callback name was passed:
24775  * <p>
24776  * <pre><code>
24777 boolean scriptTag = false;
24778 String cb = request.getParameter("callback");
24779 if (cb != null) {
24780     scriptTag = true;
24781     response.setContentType("text/javascript");
24782 } else {
24783     response.setContentType("application/x-json");
24784 }
24785 Writer out = response.getWriter();
24786 if (scriptTag) {
24787     out.write(cb + "(");
24788 }
24789 out.print(dataBlock.toJsonString());
24790 if (scriptTag) {
24791     out.write(");");
24792 }
24793 </pre></code>
24794  *
24795  * @constructor
24796  * @param {Object} config A configuration object.
24797  */
24798 Roo.data.ScriptTagProxy = function(config){
24799     Roo.data.ScriptTagProxy.superclass.constructor.call(this);
24800     Roo.apply(this, config);
24801     this.head = document.getElementsByTagName("head")[0];
24802 };
24803
24804 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
24805
24806 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
24807     /**
24808      * @cfg {String} url The URL from which to request the data object.
24809      */
24810     /**
24811      * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
24812      */
24813     timeout : 30000,
24814     /**
24815      * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
24816      * the server the name of the callback function set up by the load call to process the returned data object.
24817      * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
24818      * javascript output which calls this named function passing the data object as its only parameter.
24819      */
24820     callbackParam : "callback",
24821     /**
24822      *  @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
24823      * name to the request.
24824      */
24825     nocache : true,
24826
24827     /**
24828      * Load data from the configured URL, read the data object into
24829      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
24830      * process that block using the passed callback.
24831      * @param {Object} params An object containing properties which are to be used as HTTP parameters
24832      * for the request to the remote server.
24833      * @param {Roo.data.DataReader} reader The Reader object which converts the data
24834      * object into a block of Roo.data.Records.
24835      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
24836      * The function must be passed <ul>
24837      * <li>The Record block object</li>
24838      * <li>The "arg" argument from the load function</li>
24839      * <li>A boolean success indicator</li>
24840      * </ul>
24841      * @param {Object} scope The scope in which to call the callback
24842      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
24843      */
24844     load : function(params, reader, callback, scope, arg){
24845         if(this.fireEvent("beforeload", this, params) !== false){
24846
24847             var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
24848
24849             var url = this.url;
24850             url += (url.indexOf("?") != -1 ? "&" : "?") + p;
24851             if(this.nocache){
24852                 url += "&_dc=" + (new Date().getTime());
24853             }
24854             var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
24855             var trans = {
24856                 id : transId,
24857                 cb : "stcCallback"+transId,
24858                 scriptId : "stcScript"+transId,
24859                 params : params,
24860                 arg : arg,
24861                 url : url,
24862                 callback : callback,
24863                 scope : scope,
24864                 reader : reader
24865             };
24866             var conn = this;
24867
24868             window[trans.cb] = function(o){
24869                 conn.handleResponse(o, trans);
24870             };
24871
24872             url += String.format("&{0}={1}", this.callbackParam, trans.cb);
24873
24874             if(this.autoAbort !== false){
24875                 this.abort();
24876             }
24877
24878             trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
24879
24880             var script = document.createElement("script");
24881             script.setAttribute("src", url);
24882             script.setAttribute("type", "text/javascript");
24883             script.setAttribute("id", trans.scriptId);
24884             this.head.appendChild(script);
24885
24886             this.trans = trans;
24887         }else{
24888             callback.call(scope||this, null, arg, false);
24889         }
24890     },
24891
24892     // private
24893     isLoading : function(){
24894         return this.trans ? true : false;
24895     },
24896
24897     /**
24898      * Abort the current server request.
24899      */
24900     abort : function(){
24901         if(this.isLoading()){
24902             this.destroyTrans(this.trans);
24903         }
24904     },
24905
24906     // private
24907     destroyTrans : function(trans, isLoaded){
24908         this.head.removeChild(document.getElementById(trans.scriptId));
24909         clearTimeout(trans.timeoutId);
24910         if(isLoaded){
24911             window[trans.cb] = undefined;
24912             try{
24913                 delete window[trans.cb];
24914             }catch(e){}
24915         }else{
24916             // if hasn't been loaded, wait for load to remove it to prevent script error
24917             window[trans.cb] = function(){
24918                 window[trans.cb] = undefined;
24919                 try{
24920                     delete window[trans.cb];
24921                 }catch(e){}
24922             };
24923         }
24924     },
24925
24926     // private
24927     handleResponse : function(o, trans){
24928         this.trans = false;
24929         this.destroyTrans(trans, true);
24930         var result;
24931         try {
24932             result = trans.reader.readRecords(o);
24933         }catch(e){
24934             this.fireEvent("loadexception", this, o, trans.arg, e);
24935             trans.callback.call(trans.scope||window, null, trans.arg, false);
24936             return;
24937         }
24938         this.fireEvent("load", this, o, trans.arg);
24939         trans.callback.call(trans.scope||window, result, trans.arg, true);
24940     },
24941
24942     // private
24943     handleFailure : function(trans){
24944         this.trans = false;
24945         this.destroyTrans(trans, false);
24946         this.fireEvent("loadexception", this, null, trans.arg);
24947         trans.callback.call(trans.scope||window, null, trans.arg, false);
24948     }
24949 });/*
24950  * Based on:
24951  * Ext JS Library 1.1.1
24952  * Copyright(c) 2006-2007, Ext JS, LLC.
24953  *
24954  * Originally Released Under LGPL - original licence link has changed is not relivant.
24955  *
24956  * Fork - LGPL
24957  * <script type="text/javascript">
24958  */
24959
24960 /**
24961  * @class Roo.data.JsonReader
24962  * @extends Roo.data.DataReader
24963  * Data reader class to create an Array of Roo.data.Record objects from a JSON response
24964  * based on mappings in a provided Roo.data.Record constructor.
24965  * 
24966  * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
24967  * in the reply previously. 
24968  * 
24969  * <p>
24970  * Example code:
24971  * <pre><code>
24972 var RecordDef = Roo.data.Record.create([
24973     {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
24974     {name: 'occupation'}                 // This field will use "occupation" as the mapping.
24975 ]);
24976 var myReader = new Roo.data.JsonReader({
24977     totalProperty: "results",    // The property which contains the total dataset size (optional)
24978     root: "rows",                // The property which contains an Array of row objects
24979     id: "id"                     // The property within each row object that provides an ID for the record (optional)
24980 }, RecordDef);
24981 </code></pre>
24982  * <p>
24983  * This would consume a JSON file like this:
24984  * <pre><code>
24985 { 'results': 2, 'rows': [
24986     { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
24987     { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
24988 }
24989 </code></pre>
24990  * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
24991  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
24992  * paged from the remote server.
24993  * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
24994  * @cfg {String} root name of the property which contains the Array of row objects.
24995  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
24996  * @cfg {Array} fields Array of field definition objects
24997  * @constructor
24998  * Create a new JsonReader
24999  * @param {Object} meta Metadata configuration options
25000  * @param {Object} recordType Either an Array of field definition objects,
25001  * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
25002  */
25003 Roo.data.JsonReader = function(meta, recordType){
25004     
25005     meta = meta || {};
25006     // set some defaults:
25007     Roo.applyIf(meta, {
25008         totalProperty: 'total',
25009         successProperty : 'success',
25010         root : 'data',
25011         id : 'id'
25012     });
25013     
25014     Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
25015 };
25016 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
25017     
25018     readerType : 'Json',
25019     
25020     /**
25021      * @prop {Boolean} metaFromRemote  - if the meta data was loaded from the remote source.
25022      * Used by Store query builder to append _requestMeta to params.
25023      * 
25024      */
25025     metaFromRemote : false,
25026     /**
25027      * This method is only used by a DataProxy which has retrieved data from a remote server.
25028      * @param {Object} response The XHR object which contains the JSON data in its responseText.
25029      * @return {Object} data A data block which is used by an Roo.data.Store object as
25030      * a cache of Roo.data.Records.
25031      */
25032     read : function(response){
25033         var json = response.responseText;
25034        
25035         var o = /* eval:var:o */ eval("("+json+")");
25036         if(!o) {
25037             throw {message: "JsonReader.read: Json object not found"};
25038         }
25039         
25040         if(o.metaData){
25041             
25042             delete this.ef;
25043             this.metaFromRemote = true;
25044             this.meta = o.metaData;
25045             this.recordType = Roo.data.Record.create(o.metaData.fields);
25046             this.onMetaChange(this.meta, this.recordType, o);
25047         }
25048         return this.readRecords(o);
25049     },
25050
25051     // private function a store will implement
25052     onMetaChange : function(meta, recordType, o){
25053
25054     },
25055
25056     /**
25057          * @ignore
25058          */
25059     simpleAccess: function(obj, subsc) {
25060         return obj[subsc];
25061     },
25062
25063         /**
25064          * @ignore
25065          */
25066     getJsonAccessor: function(){
25067         var re = /[\[\.]/;
25068         return function(expr) {
25069             try {
25070                 return(re.test(expr))
25071                     ? new Function("obj", "return obj." + expr)
25072                     : function(obj){
25073                         return obj[expr];
25074                     };
25075             } catch(e){}
25076             return Roo.emptyFn;
25077         };
25078     }(),
25079
25080     /**
25081      * Create a data block containing Roo.data.Records from an XML document.
25082      * @param {Object} o An object which contains an Array of row objects in the property specified
25083      * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
25084      * which contains the total size of the dataset.
25085      * @return {Object} data A data block which is used by an Roo.data.Store object as
25086      * a cache of Roo.data.Records.
25087      */
25088     readRecords : function(o){
25089         /**
25090          * After any data loads, the raw JSON data is available for further custom processing.
25091          * @type Object
25092          */
25093         this.o = o;
25094         var s = this.meta, Record = this.recordType,
25095             f = Record ? Record.prototype.fields : null, fi = f ? f.items : [], fl = f ? f.length : 0;
25096
25097 //      Generate extraction functions for the totalProperty, the root, the id, and for each field
25098         if (!this.ef) {
25099             if(s.totalProperty) {
25100                     this.getTotal = this.getJsonAccessor(s.totalProperty);
25101                 }
25102                 if(s.successProperty) {
25103                     this.getSuccess = this.getJsonAccessor(s.successProperty);
25104                 }
25105                 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
25106                 if (s.id) {
25107                         var g = this.getJsonAccessor(s.id);
25108                         this.getId = function(rec) {
25109                                 var r = g(rec);  
25110                                 return (r === undefined || r === "") ? null : r;
25111                         };
25112                 } else {
25113                         this.getId = function(){return null;};
25114                 }
25115             this.ef = [];
25116             for(var jj = 0; jj < fl; jj++){
25117                 f = fi[jj];
25118                 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
25119                 this.ef[jj] = this.getJsonAccessor(map);
25120             }
25121         }
25122
25123         var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
25124         if(s.totalProperty){
25125             var vt = parseInt(this.getTotal(o), 10);
25126             if(!isNaN(vt)){
25127                 totalRecords = vt;
25128             }
25129         }
25130         if(s.successProperty){
25131             var vs = this.getSuccess(o);
25132             if(vs === false || vs === 'false'){
25133                 success = false;
25134             }
25135         }
25136         var records = [];
25137         for(var i = 0; i < c; i++){
25138                 var n = root[i];
25139             var values = {};
25140             var id = this.getId(n);
25141             for(var j = 0; j < fl; j++){
25142                 f = fi[j];
25143             var v = this.ef[j](n);
25144             if (!f.convert) {
25145                 Roo.log('missing convert for ' + f.name);
25146                 Roo.log(f);
25147                 continue;
25148             }
25149             values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
25150             }
25151             var record = new Record(values, id);
25152             record.json = n;
25153             records[i] = record;
25154         }
25155         return {
25156             raw : o,
25157             success : success,
25158             records : records,
25159             totalRecords : totalRecords
25160         };
25161     },
25162     // used when loading children.. @see loadDataFromChildren
25163     toLoadData: function(rec)
25164     {
25165         // expect rec just to be an array.. eg [a,b,c, [...] << cn ]
25166         var data = typeof(rec.data.cn) == 'undefined' ? [] : rec.data.cn;
25167         return { data : data, total : data.length };
25168         
25169     }
25170 });/*
25171  * Based on:
25172  * Ext JS Library 1.1.1
25173  * Copyright(c) 2006-2007, Ext JS, LLC.
25174  *
25175  * Originally Released Under LGPL - original licence link has changed is not relivant.
25176  *
25177  * Fork - LGPL
25178  * <script type="text/javascript">
25179  */
25180
25181 /**
25182  * @class Roo.data.XmlReader
25183  * @extends Roo.data.DataReader
25184  * Data reader class to create an Array of {@link Roo.data.Record} objects from an XML document
25185  * based on mappings in a provided Roo.data.Record constructor.<br><br>
25186  * <p>
25187  * <em>Note that in order for the browser to parse a returned XML document, the Content-Type
25188  * header in the HTTP response must be set to "text/xml".</em>
25189  * <p>
25190  * Example code:
25191  * <pre><code>
25192 var RecordDef = Roo.data.Record.create([
25193    {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
25194    {name: 'occupation'}                 // This field will use "occupation" as the mapping.
25195 ]);
25196 var myReader = new Roo.data.XmlReader({
25197    totalRecords: "results", // The element which contains the total dataset size (optional)
25198    record: "row",           // The repeated element which contains row information
25199    id: "id"                 // The element within the row that provides an ID for the record (optional)
25200 }, RecordDef);
25201 </code></pre>
25202  * <p>
25203  * This would consume an XML file like this:
25204  * <pre><code>
25205 &lt;?xml?>
25206 &lt;dataset>
25207  &lt;results>2&lt;/results>
25208  &lt;row>
25209    &lt;id>1&lt;/id>
25210    &lt;name>Bill&lt;/name>
25211    &lt;occupation>Gardener&lt;/occupation>
25212  &lt;/row>
25213  &lt;row>
25214    &lt;id>2&lt;/id>
25215    &lt;name>Ben&lt;/name>
25216    &lt;occupation>Horticulturalist&lt;/occupation>
25217  &lt;/row>
25218 &lt;/dataset>
25219 </code></pre>
25220  * @cfg {String} totalRecords The DomQuery path from which to retrieve the total number of records
25221  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
25222  * paged from the remote server.
25223  * @cfg {String} record The DomQuery path to the repeated element which contains record information.
25224  * @cfg {String} success The DomQuery path to the success attribute used by forms.
25225  * @cfg {String} id The DomQuery path relative from the record element to the element that contains
25226  * a record identifier value.
25227  * @constructor
25228  * Create a new XmlReader
25229  * @param {Object} meta Metadata configuration options
25230  * @param {Mixed} recordType The definition of the data record type to produce.  This can be either a valid
25231  * Record subclass created with {@link Roo.data.Record#create}, or an array of objects with which to call
25232  * Roo.data.Record.create.  See the {@link Roo.data.Record} class for more details.
25233  */
25234 Roo.data.XmlReader = function(meta, recordType){
25235     meta = meta || {};
25236     Roo.data.XmlReader.superclass.constructor.call(this, meta, recordType||meta.fields);
25237 };
25238 Roo.extend(Roo.data.XmlReader, Roo.data.DataReader, {
25239     
25240     readerType : 'Xml',
25241     
25242     /**
25243      * This method is only used by a DataProxy which has retrieved data from a remote server.
25244          * @param {Object} response The XHR object which contains the parsed XML document.  The response is expected
25245          * to contain a method called 'responseXML' that returns an XML document object.
25246      * @return {Object} records A data block which is used by an {@link Roo.data.Store} as
25247      * a cache of Roo.data.Records.
25248      */
25249     read : function(response){
25250         var doc = response.responseXML;
25251         if(!doc) {
25252             throw {message: "XmlReader.read: XML Document not available"};
25253         }
25254         return this.readRecords(doc);
25255     },
25256
25257     /**
25258      * Create a data block containing Roo.data.Records from an XML document.
25259          * @param {Object} doc A parsed XML document.
25260      * @return {Object} records A data block which is used by an {@link Roo.data.Store} as
25261      * a cache of Roo.data.Records.
25262      */
25263     readRecords : function(doc){
25264         /**
25265          * After any data loads/reads, the raw XML Document is available for further custom processing.
25266          * @type XMLDocument
25267          */
25268         this.xmlData = doc;
25269         var root = doc.documentElement || doc;
25270         var q = Roo.DomQuery;
25271         var recordType = this.recordType, fields = recordType.prototype.fields;
25272         var sid = this.meta.id;
25273         var totalRecords = 0, success = true;
25274         if(this.meta.totalRecords){
25275             totalRecords = q.selectNumber(this.meta.totalRecords, root, 0);
25276         }
25277         
25278         if(this.meta.success){
25279             var sv = q.selectValue(this.meta.success, root, true);
25280             success = sv !== false && sv !== 'false';
25281         }
25282         var records = [];
25283         var ns = q.select(this.meta.record, root);
25284         for(var i = 0, len = ns.length; i < len; i++) {
25285                 var n = ns[i];
25286                 var values = {};
25287                 var id = sid ? q.selectValue(sid, n) : undefined;
25288                 for(var j = 0, jlen = fields.length; j < jlen; j++){
25289                     var f = fields.items[j];
25290                 var v = q.selectValue(f.mapping || f.name, n, f.defaultValue);
25291                     v = f.convert(v);
25292                     values[f.name] = v;
25293                 }
25294                 var record = new recordType(values, id);
25295                 record.node = n;
25296                 records[records.length] = record;
25297             }
25298
25299             return {
25300                 success : success,
25301                 records : records,
25302                 totalRecords : totalRecords || records.length
25303             };
25304     }
25305 });/*
25306  * Based on:
25307  * Ext JS Library 1.1.1
25308  * Copyright(c) 2006-2007, Ext JS, LLC.
25309  *
25310  * Originally Released Under LGPL - original licence link has changed is not relivant.
25311  *
25312  * Fork - LGPL
25313  * <script type="text/javascript">
25314  */
25315
25316 /**
25317  * @class Roo.data.ArrayReader
25318  * @extends Roo.data.DataReader
25319  * Data reader class to create an Array of Roo.data.Record objects from an Array.
25320  * Each element of that Array represents a row of data fields. The
25321  * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
25322  * of the field definition if it exists, or the field's ordinal position in the definition.<br>
25323  * <p>
25324  * Example code:.
25325  * <pre><code>
25326 var RecordDef = Roo.data.Record.create([
25327     {name: 'name', mapping: 1},         // "mapping" only needed if an "id" field is present which
25328     {name: 'occupation', mapping: 2}    // precludes using the ordinal position as the index.
25329 ]);
25330 var myReader = new Roo.data.ArrayReader({
25331     id: 0                     // The subscript within row Array that provides an ID for the Record (optional)
25332 }, RecordDef);
25333 </code></pre>
25334  * <p>
25335  * This would consume an Array like this:
25336  * <pre><code>
25337 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
25338   </code></pre>
25339  
25340  * @constructor
25341  * Create a new JsonReader
25342  * @param {Object} meta Metadata configuration options.
25343  * @param {Object|Array} recordType Either an Array of field definition objects
25344  * 
25345  * @cfg {Array} fields Array of field definition objects
25346  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
25347  * as specified to {@link Roo.data.Record#create},
25348  * or an {@link Roo.data.Record} object
25349  *
25350  * 
25351  * created using {@link Roo.data.Record#create}.
25352  */
25353 Roo.data.ArrayReader = function(meta, recordType)
25354 {    
25355     Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType||meta.fields);
25356 };
25357
25358 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
25359     
25360       /**
25361      * Create a data block containing Roo.data.Records from an XML document.
25362      * @param {Object} o An Array of row objects which represents the dataset.
25363      * @return {Object} A data block which is used by an {@link Roo.data.Store} object as
25364      * a cache of Roo.data.Records.
25365      */
25366     readRecords : function(o)
25367     {
25368         var sid = this.meta ? this.meta.id : null;
25369         var recordType = this.recordType, fields = recordType.prototype.fields;
25370         var records = [];
25371         var root = o;
25372         for(var i = 0; i < root.length; i++){
25373             var n = root[i];
25374             var values = {};
25375             var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
25376             for(var j = 0, jlen = fields.length; j < jlen; j++){
25377                 var f = fields.items[j];
25378                 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
25379                 var v = n[k] !== undefined ? n[k] : f.defaultValue;
25380                 v = f.convert(v);
25381                 values[f.name] = v;
25382             }
25383             var record = new recordType(values, id);
25384             record.json = n;
25385             records[records.length] = record;
25386         }
25387         return {
25388             records : records,
25389             totalRecords : records.length
25390         };
25391     },
25392     // used when loading children.. @see loadDataFromChildren
25393     toLoadData: function(rec)
25394     {
25395         // expect rec just to be an array.. eg [a,b,c, [...] << cn ]
25396         return typeof(rec.data.cn) == 'undefined' ? [] : rec.data.cn;
25397         
25398     }
25399     
25400     
25401 });/*
25402  * Based on:
25403  * Ext JS Library 1.1.1
25404  * Copyright(c) 2006-2007, Ext JS, LLC.
25405  *
25406  * Originally Released Under LGPL - original licence link has changed is not relivant.
25407  *
25408  * Fork - LGPL
25409  * <script type="text/javascript">
25410  */
25411
25412
25413 /**
25414  * @class Roo.data.Tree
25415  * @extends Roo.util.Observable
25416  * Represents a tree data structure and bubbles all the events for its nodes. The nodes
25417  * in the tree have most standard DOM functionality.
25418  * @constructor
25419  * @param {Node} root (optional) The root node
25420  */
25421 Roo.data.Tree = function(root){
25422    this.nodeHash = {};
25423    /**
25424     * The root node for this tree
25425     * @type Node
25426     */
25427    this.root = null;
25428    if(root){
25429        this.setRootNode(root);
25430    }
25431    this.addEvents({
25432        /**
25433         * @event append
25434         * Fires when a new child node is appended to a node in this tree.
25435         * @param {Tree} tree The owner tree
25436         * @param {Node} parent The parent node
25437         * @param {Node} node The newly appended node
25438         * @param {Number} index The index of the newly appended node
25439         */
25440        "append" : true,
25441        /**
25442         * @event remove
25443         * Fires when a child node is removed from a node in this tree.
25444         * @param {Tree} tree The owner tree
25445         * @param {Node} parent The parent node
25446         * @param {Node} node The child node removed
25447         */
25448        "remove" : true,
25449        /**
25450         * @event move
25451         * Fires when a node is moved to a new location in the tree
25452         * @param {Tree} tree The owner tree
25453         * @param {Node} node The node moved
25454         * @param {Node} oldParent The old parent of this node
25455         * @param {Node} newParent The new parent of this node
25456         * @param {Number} index The index it was moved to
25457         */
25458        "move" : true,
25459        /**
25460         * @event insert
25461         * Fires when a new child node is inserted in a node in this tree.
25462         * @param {Tree} tree The owner tree
25463         * @param {Node} parent The parent node
25464         * @param {Node} node The child node inserted
25465         * @param {Node} refNode The child node the node was inserted before
25466         */
25467        "insert" : true,
25468        /**
25469         * @event beforeappend
25470         * Fires before a new child is appended to a node in this tree, return false to cancel the append.
25471         * @param {Tree} tree The owner tree
25472         * @param {Node} parent The parent node
25473         * @param {Node} node The child node to be appended
25474         */
25475        "beforeappend" : true,
25476        /**
25477         * @event beforeremove
25478         * Fires before a child is removed from a node in this tree, return false to cancel the remove.
25479         * @param {Tree} tree The owner tree
25480         * @param {Node} parent The parent node
25481         * @param {Node} node The child node to be removed
25482         */
25483        "beforeremove" : true,
25484        /**
25485         * @event beforemove
25486         * Fires before a node is moved to a new location in the tree. Return false to cancel the move.
25487         * @param {Tree} tree The owner tree
25488         * @param {Node} node The node being moved
25489         * @param {Node} oldParent The parent of the node
25490         * @param {Node} newParent The new parent the node is moving to
25491         * @param {Number} index The index it is being moved to
25492         */
25493        "beforemove" : true,
25494        /**
25495         * @event beforeinsert
25496         * Fires before a new child is inserted in a node in this tree, return false to cancel the insert.
25497         * @param {Tree} tree The owner tree
25498         * @param {Node} parent The parent node
25499         * @param {Node} node The child node to be inserted
25500         * @param {Node} refNode The child node the node is being inserted before
25501         */
25502        "beforeinsert" : true
25503    });
25504
25505     Roo.data.Tree.superclass.constructor.call(this);
25506 };
25507
25508 Roo.extend(Roo.data.Tree, Roo.util.Observable, {
25509     pathSeparator: "/",
25510
25511     proxyNodeEvent : function(){
25512         return this.fireEvent.apply(this, arguments);
25513     },
25514
25515     /**
25516      * Returns the root node for this tree.
25517      * @return {Node}
25518      */
25519     getRootNode : function(){
25520         return this.root;
25521     },
25522
25523     /**
25524      * Sets the root node for this tree.
25525      * @param {Node} node
25526      * @return {Node}
25527      */
25528     setRootNode : function(node){
25529         this.root = node;
25530         node.ownerTree = this;
25531         node.isRoot = true;
25532         this.registerNode(node);
25533         return node;
25534     },
25535
25536     /**
25537      * Gets a node in this tree by its id.
25538      * @param {String} id
25539      * @return {Node}
25540      */
25541     getNodeById : function(id){
25542         return this.nodeHash[id];
25543     },
25544
25545     registerNode : function(node){
25546         this.nodeHash[node.id] = node;
25547     },
25548
25549     unregisterNode : function(node){
25550         delete this.nodeHash[node.id];
25551     },
25552
25553     toString : function(){
25554         return "[Tree"+(this.id?" "+this.id:"")+"]";
25555     }
25556 });
25557
25558 /**
25559  * @class Roo.data.Node
25560  * @extends Roo.util.Observable
25561  * @cfg {Boolean} leaf true if this node is a leaf and does not have children
25562  * @cfg {String} id The id for this node. If one is not specified, one is generated.
25563  * @constructor
25564  * @param {Object} attributes The attributes/config for the node
25565  */
25566 Roo.data.Node = function(attributes){
25567     /**
25568      * The attributes supplied for the node. You can use this property to access any custom attributes you supplied.
25569      * @type {Object}
25570      */
25571     this.attributes = attributes || {};
25572     this.leaf = this.attributes.leaf;
25573     /**
25574      * The node id. @type String
25575      */
25576     this.id = this.attributes.id;
25577     if(!this.id){
25578         this.id = Roo.id(null, "ynode-");
25579         this.attributes.id = this.id;
25580     }
25581      
25582     
25583     /**
25584      * All child nodes of this node. @type Array
25585      */
25586     this.childNodes = [];
25587     if(!this.childNodes.indexOf){ // indexOf is a must
25588         this.childNodes.indexOf = function(o){
25589             for(var i = 0, len = this.length; i < len; i++){
25590                 if(this[i] == o) {
25591                     return i;
25592                 }
25593             }
25594             return -1;
25595         };
25596     }
25597     /**
25598      * The parent node for this node. @type Node
25599      */
25600     this.parentNode = null;
25601     /**
25602      * The first direct child node of this node, or null if this node has no child nodes. @type Node
25603      */
25604     this.firstChild = null;
25605     /**
25606      * The last direct child node of this node, or null if this node has no child nodes. @type Node
25607      */
25608     this.lastChild = null;
25609     /**
25610      * The node immediately preceding this node in the tree, or null if there is no sibling node. @type Node
25611      */
25612     this.previousSibling = null;
25613     /**
25614      * The node immediately following this node in the tree, or null if there is no sibling node. @type Node
25615      */
25616     this.nextSibling = null;
25617
25618     this.addEvents({
25619        /**
25620         * @event append
25621         * Fires when a new child node is appended
25622         * @param {Tree} tree The owner tree
25623         * @param {Node} this This node
25624         * @param {Node} node The newly appended node
25625         * @param {Number} index The index of the newly appended node
25626         */
25627        "append" : true,
25628        /**
25629         * @event remove
25630         * Fires when a child node is removed
25631         * @param {Tree} tree The owner tree
25632         * @param {Node} this This node
25633         * @param {Node} node The removed node
25634         */
25635        "remove" : true,
25636        /**
25637         * @event move
25638         * Fires when this node is moved to a new location in the tree
25639         * @param {Tree} tree The owner tree
25640         * @param {Node} this This node
25641         * @param {Node} oldParent The old parent of this node
25642         * @param {Node} newParent The new parent of this node
25643         * @param {Number} index The index it was moved to
25644         */
25645        "move" : true,
25646        /**
25647         * @event insert
25648         * Fires when a new child node is inserted.
25649         * @param {Tree} tree The owner tree
25650         * @param {Node} this This node
25651         * @param {Node} node The child node inserted
25652         * @param {Node} refNode The child node the node was inserted before
25653         */
25654        "insert" : true,
25655        /**
25656         * @event beforeappend
25657         * Fires before a new child is appended, return false to cancel the append.
25658         * @param {Tree} tree The owner tree
25659         * @param {Node} this This node
25660         * @param {Node} node The child node to be appended
25661         */
25662        "beforeappend" : true,
25663        /**
25664         * @event beforeremove
25665         * Fires before a child is removed, return false to cancel the remove.
25666         * @param {Tree} tree The owner tree
25667         * @param {Node} this This node
25668         * @param {Node} node The child node to be removed
25669         */
25670        "beforeremove" : true,
25671        /**
25672         * @event beforemove
25673         * Fires before this node is moved to a new location in the tree. Return false to cancel the move.
25674         * @param {Tree} tree The owner tree
25675         * @param {Node} this This node
25676         * @param {Node} oldParent The parent of this node
25677         * @param {Node} newParent The new parent this node is moving to
25678         * @param {Number} index The index it is being moved to
25679         */
25680        "beforemove" : true,
25681        /**
25682         * @event beforeinsert
25683         * Fires before a new child is inserted, return false to cancel the insert.
25684         * @param {Tree} tree The owner tree
25685         * @param {Node} this This node
25686         * @param {Node} node The child node to be inserted
25687         * @param {Node} refNode The child node the node is being inserted before
25688         */
25689        "beforeinsert" : true
25690    });
25691     this.listeners = this.attributes.listeners;
25692     Roo.data.Node.superclass.constructor.call(this);
25693 };
25694
25695 Roo.extend(Roo.data.Node, Roo.util.Observable, {
25696     fireEvent : function(evtName){
25697         // first do standard event for this node
25698         if(Roo.data.Node.superclass.fireEvent.apply(this, arguments) === false){
25699             return false;
25700         }
25701         // then bubble it up to the tree if the event wasn't cancelled
25702         var ot = this.getOwnerTree();
25703         if(ot){
25704             if(ot.proxyNodeEvent.apply(ot, arguments) === false){
25705                 return false;
25706             }
25707         }
25708         return true;
25709     },
25710
25711     /**
25712      * Returns true if this node is a leaf
25713      * @return {Boolean}
25714      */
25715     isLeaf : function(){
25716         return this.leaf === true;
25717     },
25718
25719     // private
25720     setFirstChild : function(node){
25721         this.firstChild = node;
25722     },
25723
25724     //private
25725     setLastChild : function(node){
25726         this.lastChild = node;
25727     },
25728
25729
25730     /**
25731      * Returns true if this node is the last child of its parent
25732      * @return {Boolean}
25733      */
25734     isLast : function(){
25735        return (!this.parentNode ? true : this.parentNode.lastChild == this);
25736     },
25737
25738     /**
25739      * Returns true if this node is the first child of its parent
25740      * @return {Boolean}
25741      */
25742     isFirst : function(){
25743        return (!this.parentNode ? true : this.parentNode.firstChild == this);
25744     },
25745
25746     hasChildNodes : function(){
25747         return !this.isLeaf() && this.childNodes.length > 0;
25748     },
25749
25750     /**
25751      * Insert node(s) as the last child node of this node.
25752      * @param {Node/Array} node The node or Array of nodes to append
25753      * @return {Node} The appended node if single append, or null if an array was passed
25754      */
25755     appendChild : function(node){
25756         var multi = false;
25757         if(node instanceof Array){
25758             multi = node;
25759         }else if(arguments.length > 1){
25760             multi = arguments;
25761         }
25762         
25763         // if passed an array or multiple args do them one by one
25764         if(multi){
25765             for(var i = 0, len = multi.length; i < len; i++) {
25766                 this.appendChild(multi[i]);
25767             }
25768         }else{
25769             if(this.fireEvent("beforeappend", this.ownerTree, this, node) === false){
25770                 return false;
25771             }
25772             var index = this.childNodes.length;
25773             var oldParent = node.parentNode;
25774             // it's a move, make sure we move it cleanly
25775             if(oldParent){
25776                 if(node.fireEvent("beforemove", node.getOwnerTree(), node, oldParent, this, index) === false){
25777                     return false;
25778                 }
25779                 oldParent.removeChild(node);
25780             }
25781             
25782             index = this.childNodes.length;
25783             if(index == 0){
25784                 this.setFirstChild(node);
25785             }
25786             this.childNodes.push(node);
25787             node.parentNode = this;
25788             var ps = this.childNodes[index-1];
25789             if(ps){
25790                 node.previousSibling = ps;
25791                 ps.nextSibling = node;
25792             }else{
25793                 node.previousSibling = null;
25794             }
25795             node.nextSibling = null;
25796             this.setLastChild(node);
25797             node.setOwnerTree(this.getOwnerTree());
25798             this.fireEvent("append", this.ownerTree, this, node, index);
25799             if(this.ownerTree) {
25800                 this.ownerTree.fireEvent("appendnode", this, node, index);
25801             }
25802             if(oldParent){
25803                 node.fireEvent("move", this.ownerTree, node, oldParent, this, index);
25804             }
25805             return node;
25806         }
25807     },
25808
25809     /**
25810      * Removes a child node from this node.
25811      * @param {Node} node The node to remove
25812      * @return {Node} The removed node
25813      */
25814     removeChild : function(node){
25815         var index = this.childNodes.indexOf(node);
25816         if(index == -1){
25817             return false;
25818         }
25819         if(this.fireEvent("beforeremove", this.ownerTree, this, node) === false){
25820             return false;
25821         }
25822
25823         // remove it from childNodes collection
25824         this.childNodes.splice(index, 1);
25825
25826         // update siblings
25827         if(node.previousSibling){
25828             node.previousSibling.nextSibling = node.nextSibling;
25829         }
25830         if(node.nextSibling){
25831             node.nextSibling.previousSibling = node.previousSibling;
25832         }
25833
25834         // update child refs
25835         if(this.firstChild == node){
25836             this.setFirstChild(node.nextSibling);
25837         }
25838         if(this.lastChild == node){
25839             this.setLastChild(node.previousSibling);
25840         }
25841
25842         node.setOwnerTree(null);
25843         // clear any references from the node
25844         node.parentNode = null;
25845         node.previousSibling = null;
25846         node.nextSibling = null;
25847         this.fireEvent("remove", this.ownerTree, this, node);
25848         return node;
25849     },
25850
25851     /**
25852      * Inserts the first node before the second node in this nodes childNodes collection.
25853      * @param {Node} node The node to insert
25854      * @param {Node} refNode The node to insert before (if null the node is appended)
25855      * @return {Node} The inserted node
25856      */
25857     insertBefore : function(node, refNode){
25858         if(!refNode){ // like standard Dom, refNode can be null for append
25859             return this.appendChild(node);
25860         }
25861         // nothing to do
25862         if(node == refNode){
25863             return false;
25864         }
25865
25866         if(this.fireEvent("beforeinsert", this.ownerTree, this, node, refNode) === false){
25867             return false;
25868         }
25869         var index = this.childNodes.indexOf(refNode);
25870         var oldParent = node.parentNode;
25871         var refIndex = index;
25872
25873         // when moving internally, indexes will change after remove
25874         if(oldParent == this && this.childNodes.indexOf(node) < index){
25875             refIndex--;
25876         }
25877
25878         // it's a move, make sure we move it cleanly
25879         if(oldParent){
25880             if(node.fireEvent("beforemove", node.getOwnerTree(), node, oldParent, this, index, refNode) === false){
25881                 return false;
25882             }
25883             oldParent.removeChild(node);
25884         }
25885         if(refIndex == 0){
25886             this.setFirstChild(node);
25887         }
25888         this.childNodes.splice(refIndex, 0, node);
25889         node.parentNode = this;
25890         var ps = this.childNodes[refIndex-1];
25891         if(ps){
25892             node.previousSibling = ps;
25893             ps.nextSibling = node;
25894         }else{
25895             node.previousSibling = null;
25896         }
25897         node.nextSibling = refNode;
25898         refNode.previousSibling = node;
25899         node.setOwnerTree(this.getOwnerTree());
25900         this.fireEvent("insert", this.ownerTree, this, node, refNode);
25901         if(oldParent){
25902             node.fireEvent("move", this.ownerTree, node, oldParent, this, refIndex, refNode);
25903         }
25904         return node;
25905     },
25906
25907     /**
25908      * Returns the child node at the specified index.
25909      * @param {Number} index
25910      * @return {Node}
25911      */
25912     item : function(index){
25913         return this.childNodes[index];
25914     },
25915
25916     /**
25917      * Replaces one child node in this node with another.
25918      * @param {Node} newChild The replacement node
25919      * @param {Node} oldChild The node to replace
25920      * @return {Node} The replaced node
25921      */
25922     replaceChild : function(newChild, oldChild){
25923         this.insertBefore(newChild, oldChild);
25924         this.removeChild(oldChild);
25925         return oldChild;
25926     },
25927
25928     /**
25929      * Returns the index of a child node
25930      * @param {Node} node
25931      * @return {Number} The index of the node or -1 if it was not found
25932      */
25933     indexOf : function(child){
25934         return this.childNodes.indexOf(child);
25935     },
25936
25937     /**
25938      * Returns the tree this node is in.
25939      * @return {Tree}
25940      */
25941     getOwnerTree : function(){
25942         // if it doesn't have one, look for one
25943         if(!this.ownerTree){
25944             var p = this;
25945             while(p){
25946                 if(p.ownerTree){
25947                     this.ownerTree = p.ownerTree;
25948                     break;
25949                 }
25950                 p = p.parentNode;
25951             }
25952         }
25953         return this.ownerTree;
25954     },
25955
25956     /**
25957      * Returns depth of this node (the root node has a depth of 0)
25958      * @return {Number}
25959      */
25960     getDepth : function(){
25961         var depth = 0;
25962         var p = this;
25963         while(p.parentNode){
25964             ++depth;
25965             p = p.parentNode;
25966         }
25967         return depth;
25968     },
25969
25970     // private
25971     setOwnerTree : function(tree){
25972         // if it's move, we need to update everyone
25973         if(tree != this.ownerTree){
25974             if(this.ownerTree){
25975                 this.ownerTree.unregisterNode(this);
25976             }
25977             this.ownerTree = tree;
25978             var cs = this.childNodes;
25979             for(var i = 0, len = cs.length; i < len; i++) {
25980                 cs[i].setOwnerTree(tree);
25981             }
25982             if(tree){
25983                 tree.registerNode(this);
25984             }
25985         }
25986     },
25987
25988     /**
25989      * Returns the path for this node. The path can be used to expand or select this node programmatically.
25990      * @param {String} attr (optional) The attr to use for the path (defaults to the node's id)
25991      * @return {String} The path
25992      */
25993     getPath : function(attr){
25994         attr = attr || "id";
25995         var p = this.parentNode;
25996         var b = [this.attributes[attr]];
25997         while(p){
25998             b.unshift(p.attributes[attr]);
25999             p = p.parentNode;
26000         }
26001         var sep = this.getOwnerTree().pathSeparator;
26002         return sep + b.join(sep);
26003     },
26004
26005     /**
26006      * Bubbles up the tree from this node, calling the specified function with each node. The scope (<i>this</i>) of
26007      * function call will be the scope provided or the current node. The arguments to the function
26008      * will be the args provided or the current node. If the function returns false at any point,
26009      * the bubble is stopped.
26010      * @param {Function} fn The function to call
26011      * @param {Object} scope (optional) The scope of the function (defaults to current node)
26012      * @param {Array} args (optional) The args to call the function with (default to passing the current node)
26013      */
26014     bubble : function(fn, scope, args){
26015         var p = this;
26016         while(p){
26017             if(fn.call(scope || p, args || p) === false){
26018                 break;
26019             }
26020             p = p.parentNode;
26021         }
26022     },
26023
26024     /**
26025      * Cascades down the tree from this node, calling the specified function with each node. The scope (<i>this</i>) of
26026      * function call will be the scope provided or the current node. The arguments to the function
26027      * will be the args provided or the current node. If the function returns false at any point,
26028      * the cascade is stopped on that branch.
26029      * @param {Function} fn The function to call
26030      * @param {Object} scope (optional) The scope of the function (defaults to current node)
26031      * @param {Array} args (optional) The args to call the function with (default to passing the current node)
26032      */
26033     cascade : function(fn, scope, args){
26034         if(fn.call(scope || this, args || this) !== false){
26035             var cs = this.childNodes;
26036             for(var i = 0, len = cs.length; i < len; i++) {
26037                 cs[i].cascade(fn, scope, args);
26038             }
26039         }
26040     },
26041
26042     /**
26043      * Interates the child nodes of this node, calling the specified function with each node. The scope (<i>this</i>) of
26044      * function call will be the scope provided or the current node. The arguments to the function
26045      * will be the args provided or the current node. If the function returns false at any point,
26046      * the iteration stops.
26047      * @param {Function} fn The function to call
26048      * @param {Object} scope (optional) The scope of the function (defaults to current node)
26049      * @param {Array} args (optional) The args to call the function with (default to passing the current node)
26050      */
26051     eachChild : function(fn, scope, args){
26052         var cs = this.childNodes;
26053         for(var i = 0, len = cs.length; i < len; i++) {
26054                 if(fn.call(scope || this, args || cs[i]) === false){
26055                     break;
26056                 }
26057         }
26058     },
26059
26060     /**
26061      * Finds the first child that has the attribute with the specified value.
26062      * @param {String} attribute The attribute name
26063      * @param {Mixed} value The value to search for
26064      * @return {Node} The found child or null if none was found
26065      */
26066     findChild : function(attribute, value){
26067         var cs = this.childNodes;
26068         for(var i = 0, len = cs.length; i < len; i++) {
26069                 if(cs[i].attributes[attribute] == value){
26070                     return cs[i];
26071                 }
26072         }
26073         return null;
26074     },
26075
26076     /**
26077      * Finds the first child by a custom function. The child matches if the function passed
26078      * returns true.
26079      * @param {Function} fn
26080      * @param {Object} scope (optional)
26081      * @return {Node} The found child or null if none was found
26082      */
26083     findChildBy : function(fn, scope){
26084         var cs = this.childNodes;
26085         for(var i = 0, len = cs.length; i < len; i++) {
26086                 if(fn.call(scope||cs[i], cs[i]) === true){
26087                     return cs[i];
26088                 }
26089         }
26090         return null;
26091     },
26092
26093     /**
26094      * Sorts this nodes children using the supplied sort function
26095      * @param {Function} fn
26096      * @param {Object} scope (optional)
26097      */
26098     sort : function(fn, scope){
26099         var cs = this.childNodes;
26100         var len = cs.length;
26101         if(len > 0){
26102             var sortFn = scope ? function(){fn.apply(scope, arguments);} : fn;
26103             cs.sort(sortFn);
26104             for(var i = 0; i < len; i++){
26105                 var n = cs[i];
26106                 n.previousSibling = cs[i-1];
26107                 n.nextSibling = cs[i+1];
26108                 if(i == 0){
26109                     this.setFirstChild(n);
26110                 }
26111                 if(i == len-1){
26112                     this.setLastChild(n);
26113                 }
26114             }
26115         }
26116     },
26117
26118     /**
26119      * Returns true if this node is an ancestor (at any point) of the passed node.
26120      * @param {Node} node
26121      * @return {Boolean}
26122      */
26123     contains : function(node){
26124         return node.isAncestor(this);
26125     },
26126
26127     /**
26128      * Returns true if the passed node is an ancestor (at any point) of this node.
26129      * @param {Node} node
26130      * @return {Boolean}
26131      */
26132     isAncestor : function(node){
26133         var p = this.parentNode;
26134         while(p){
26135             if(p == node){
26136                 return true;
26137             }
26138             p = p.parentNode;
26139         }
26140         return false;
26141     },
26142
26143     toString : function(){
26144         return "[Node"+(this.id?" "+this.id:"")+"]";
26145     }
26146 });/*
26147  * Based on:
26148  * Ext JS Library 1.1.1
26149  * Copyright(c) 2006-2007, Ext JS, LLC.
26150  *
26151  * Originally Released Under LGPL - original licence link has changed is not relivant.
26152  *
26153  * Fork - LGPL
26154  * <script type="text/javascript">
26155  */
26156
26157
26158 /**
26159  * @class Roo.Shadow
26160  * Simple class that can provide a shadow effect for any element.  Note that the element MUST be absolutely positioned,
26161  * and the shadow does not provide any shimming.  This should be used only in simple cases -- for more advanced
26162  * functionality that can also provide the same shadow effect, see the {@link Roo.Layer} class.
26163  * @constructor
26164  * Create a new Shadow
26165  * @param {Object} config The config object
26166  */
26167 Roo.Shadow = function(config){
26168     Roo.apply(this, config);
26169     if(typeof this.mode != "string"){
26170         this.mode = this.defaultMode;
26171     }
26172     var o = this.offset, a = {h: 0};
26173     var rad = Math.floor(this.offset/2);
26174     switch(this.mode.toLowerCase()){ // all this hideous nonsense calculates the various offsets for shadows
26175         case "drop":
26176             a.w = 0;
26177             a.l = a.t = o;
26178             a.t -= 1;
26179             if(Roo.isIE){
26180                 a.l -= this.offset + rad;
26181                 a.t -= this.offset + rad;
26182                 a.w -= rad;
26183                 a.h -= rad;
26184                 a.t += 1;
26185             }
26186         break;
26187         case "sides":
26188             a.w = (o*2);
26189             a.l = -o;
26190             a.t = o-1;
26191             if(Roo.isIE){
26192                 a.l -= (this.offset - rad);
26193                 a.t -= this.offset + rad;
26194                 a.l += 1;
26195                 a.w -= (this.offset - rad)*2;
26196                 a.w -= rad + 1;
26197                 a.h -= 1;
26198             }
26199         break;
26200         case "frame":
26201             a.w = a.h = (o*2);
26202             a.l = a.t = -o;
26203             a.t += 1;
26204             a.h -= 2;
26205             if(Roo.isIE){
26206                 a.l -= (this.offset - rad);
26207                 a.t -= (this.offset - rad);
26208                 a.l += 1;
26209                 a.w -= (this.offset + rad + 1);
26210                 a.h -= (this.offset + rad);
26211                 a.h += 1;
26212             }
26213         break;
26214     };
26215
26216     this.adjusts = a;
26217 };
26218
26219 Roo.Shadow.prototype = {
26220     /**
26221      * @cfg {String} mode
26222      * The shadow display mode.  Supports the following options:<br />
26223      * sides: Shadow displays on both sides and bottom only<br />
26224      * frame: Shadow displays equally on all four sides<br />
26225      * drop: Traditional bottom-right drop shadow (default)
26226      */
26227     mode: false,
26228     /**
26229      * @cfg {String} offset
26230      * The number of pixels to offset the shadow from the element (defaults to 4)
26231      */
26232     offset: 4,
26233
26234     // private
26235     defaultMode: "drop",
26236
26237     /**
26238      * Displays the shadow under the target element
26239      * @param {String/HTMLElement/Element} targetEl The id or element under which the shadow should display
26240      */
26241     show : function(target){
26242         target = Roo.get(target);
26243         if(!this.el){
26244             this.el = Roo.Shadow.Pool.pull();
26245             if(this.el.dom.nextSibling != target.dom){
26246                 this.el.insertBefore(target);
26247             }
26248         }
26249         this.el.setStyle("z-index", this.zIndex || parseInt(target.getStyle("z-index"), 10)-1);
26250         if(Roo.isIE){
26251             this.el.dom.style.filter="progid:DXImageTransform.Microsoft.alpha(opacity=50) progid:DXImageTransform.Microsoft.Blur(pixelradius="+(this.offset)+")";
26252         }
26253         this.realign(
26254             target.getLeft(true),
26255             target.getTop(true),
26256             target.getWidth(),
26257             target.getHeight()
26258         );
26259         this.el.dom.style.display = "block";
26260     },
26261
26262     /**
26263      * Returns true if the shadow is visible, else false
26264      */
26265     isVisible : function(){
26266         return this.el ? true : false;  
26267     },
26268
26269     /**
26270      * Direct alignment when values are already available. Show must be called at least once before
26271      * calling this method to ensure it is initialized.
26272      * @param {Number} left The target element left position
26273      * @param {Number} top The target element top position
26274      * @param {Number} width The target element width
26275      * @param {Number} height The target element height
26276      */
26277     realign : function(l, t, w, h){
26278         if(!this.el){
26279             return;
26280         }
26281         var a = this.adjusts, d = this.el.dom, s = d.style;
26282         var iea = 0;
26283         s.left = (l+a.l)+"px";
26284         s.top = (t+a.t)+"px";
26285         var sw = (w+a.w), sh = (h+a.h), sws = sw +"px", shs = sh + "px";
26286  
26287         if(s.width != sws || s.height != shs){
26288             s.width = sws;
26289             s.height = shs;
26290             if(!Roo.isIE){
26291                 var cn = d.childNodes;
26292                 var sww = Math.max(0, (sw-12))+"px";
26293                 cn[0].childNodes[1].style.width = sww;
26294                 cn[1].childNodes[1].style.width = sww;
26295                 cn[2].childNodes[1].style.width = sww;
26296                 cn[1].style.height = Math.max(0, (sh-12))+"px";
26297             }
26298         }
26299     },
26300
26301     /**
26302      * Hides this shadow
26303      */
26304     hide : function(){
26305         if(this.el){
26306             this.el.dom.style.display = "none";
26307             Roo.Shadow.Pool.push(this.el);
26308             delete this.el;
26309         }
26310     },
26311
26312     /**
26313      * Adjust the z-index of this shadow
26314      * @param {Number} zindex The new z-index
26315      */
26316     setZIndex : function(z){
26317         this.zIndex = z;
26318         if(this.el){
26319             this.el.setStyle("z-index", z);
26320         }
26321     }
26322 };
26323
26324 // Private utility class that manages the internal Shadow cache
26325 Roo.Shadow.Pool = function(){
26326     var p = [];
26327     var markup = Roo.isIE ?
26328                  '<div class="x-ie-shadow"></div>' :
26329                  '<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>';
26330     return {
26331         pull : function(){
26332             var sh = p.shift();
26333             if(!sh){
26334                 sh = Roo.get(Roo.DomHelper.insertHtml("beforeBegin", document.body.firstChild, markup));
26335                 sh.autoBoxAdjust = false;
26336             }
26337             return sh;
26338         },
26339
26340         push : function(sh){
26341             p.push(sh);
26342         }
26343     };
26344 }();/*
26345  * Based on:
26346  * Ext JS Library 1.1.1
26347  * Copyright(c) 2006-2007, Ext JS, LLC.
26348  *
26349  * Originally Released Under LGPL - original licence link has changed is not relivant.
26350  *
26351  * Fork - LGPL
26352  * <script type="text/javascript">
26353  */
26354
26355
26356 /**
26357  * @class Roo.SplitBar
26358  * @extends Roo.util.Observable
26359  * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
26360  * <br><br>
26361  * Usage:
26362  * <pre><code>
26363 var split = new Roo.SplitBar("elementToDrag", "elementToSize",
26364                    Roo.SplitBar.HORIZONTAL, Roo.SplitBar.LEFT);
26365 split.setAdapter(new Roo.SplitBar.AbsoluteLayoutAdapter("container"));
26366 split.minSize = 100;
26367 split.maxSize = 600;
26368 split.animate = true;
26369 split.on('moved', splitterMoved);
26370 </code></pre>
26371  * @constructor
26372  * Create a new SplitBar
26373  * @param {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar. 
26374  * @param {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged 
26375  * @param {Number} orientation (optional) Either Roo.SplitBar.HORIZONTAL or Roo.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
26376  * @param {Number} placement (optional) Either Roo.SplitBar.LEFT or Roo.SplitBar.RIGHT for horizontal or  
26377                         Roo.SplitBar.TOP or Roo.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
26378                         position of the SplitBar).
26379  */
26380 Roo.SplitBar = function(dragElement, resizingElement, orientation, placement, existingProxy){
26381     
26382     /** @private */
26383     this.el = Roo.get(dragElement, true);
26384     this.el.dom.unselectable = "on";
26385     /** @private */
26386     this.resizingEl = Roo.get(resizingElement, true);
26387
26388     /**
26389      * @private
26390      * The orientation of the split. Either Roo.SplitBar.HORIZONTAL or Roo.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
26391      * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
26392      * @type Number
26393      */
26394     this.orientation = orientation || Roo.SplitBar.HORIZONTAL;
26395     
26396     /**
26397      * The minimum size of the resizing element. (Defaults to 0)
26398      * @type Number
26399      */
26400     this.minSize = 0;
26401     
26402     /**
26403      * The maximum size of the resizing element. (Defaults to 2000)
26404      * @type Number
26405      */
26406     this.maxSize = 2000;
26407     
26408     /**
26409      * Whether to animate the transition to the new size
26410      * @type Boolean
26411      */
26412     this.animate = false;
26413     
26414     /**
26415      * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
26416      * @type Boolean
26417      */
26418     this.useShim = false;
26419     
26420     /** @private */
26421     this.shim = null;
26422     
26423     if(!existingProxy){
26424         /** @private */
26425         this.proxy = Roo.SplitBar.createProxy(this.orientation);
26426     }else{
26427         this.proxy = Roo.get(existingProxy).dom;
26428     }
26429     /** @private */
26430     this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
26431     
26432     /** @private */
26433     this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
26434     
26435     /** @private */
26436     this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
26437     
26438     /** @private */
26439     this.dragSpecs = {};
26440     
26441     /**
26442      * @private The adapter to use to positon and resize elements
26443      */
26444     this.adapter = new Roo.SplitBar.BasicLayoutAdapter();
26445     this.adapter.init(this);
26446     
26447     if(this.orientation == Roo.SplitBar.HORIZONTAL){
26448         /** @private */
26449         this.placement = placement || (this.el.getX() > this.resizingEl.getX() ? Roo.SplitBar.LEFT : Roo.SplitBar.RIGHT);
26450         this.el.addClass("x-splitbar-h");
26451     }else{
26452         /** @private */
26453         this.placement = placement || (this.el.getY() > this.resizingEl.getY() ? Roo.SplitBar.TOP : Roo.SplitBar.BOTTOM);
26454         this.el.addClass("x-splitbar-v");
26455     }
26456     
26457     this.addEvents({
26458         /**
26459          * @event resize
26460          * Fires when the splitter is moved (alias for {@link #event-moved})
26461          * @param {Roo.SplitBar} this
26462          * @param {Number} newSize the new width or height
26463          */
26464         "resize" : true,
26465         /**
26466          * @event moved
26467          * Fires when the splitter is moved
26468          * @param {Roo.SplitBar} this
26469          * @param {Number} newSize the new width or height
26470          */
26471         "moved" : true,
26472         /**
26473          * @event beforeresize
26474          * Fires before the splitter is dragged
26475          * @param {Roo.SplitBar} this
26476          */
26477         "beforeresize" : true,
26478
26479         "beforeapply" : true
26480     });
26481
26482     Roo.util.Observable.call(this);
26483 };
26484
26485 Roo.extend(Roo.SplitBar, Roo.util.Observable, {
26486     onStartProxyDrag : function(x, y){
26487         this.fireEvent("beforeresize", this);
26488         if(!this.overlay){
26489             var o = Roo.DomHelper.insertFirst(document.body,  {cls: "x-drag-overlay", html: "&#160;"}, true);
26490             o.unselectable();
26491             o.enableDisplayMode("block");
26492             // all splitbars share the same overlay
26493             Roo.SplitBar.prototype.overlay = o;
26494         }
26495         this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
26496         this.overlay.show();
26497         Roo.get(this.proxy).setDisplayed("block");
26498         var size = this.adapter.getElementSize(this);
26499         this.activeMinSize = this.getMinimumSize();;
26500         this.activeMaxSize = this.getMaximumSize();;
26501         var c1 = size - this.activeMinSize;
26502         var c2 = Math.max(this.activeMaxSize - size, 0);
26503         if(this.orientation == Roo.SplitBar.HORIZONTAL){
26504             this.dd.resetConstraints();
26505             this.dd.setXConstraint(
26506                 this.placement == Roo.SplitBar.LEFT ? c1 : c2, 
26507                 this.placement == Roo.SplitBar.LEFT ? c2 : c1
26508             );
26509             this.dd.setYConstraint(0, 0);
26510         }else{
26511             this.dd.resetConstraints();
26512             this.dd.setXConstraint(0, 0);
26513             this.dd.setYConstraint(
26514                 this.placement == Roo.SplitBar.TOP ? c1 : c2, 
26515                 this.placement == Roo.SplitBar.TOP ? c2 : c1
26516             );
26517          }
26518         this.dragSpecs.startSize = size;
26519         this.dragSpecs.startPoint = [x, y];
26520         Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
26521     },
26522     
26523     /** 
26524      * @private Called after the drag operation by the DDProxy
26525      */
26526     onEndProxyDrag : function(e){
26527         Roo.get(this.proxy).setDisplayed(false);
26528         var endPoint = Roo.lib.Event.getXY(e);
26529         if(this.overlay){
26530             this.overlay.hide();
26531         }
26532         var newSize;
26533         if(this.orientation == Roo.SplitBar.HORIZONTAL){
26534             newSize = this.dragSpecs.startSize + 
26535                 (this.placement == Roo.SplitBar.LEFT ?
26536                     endPoint[0] - this.dragSpecs.startPoint[0] :
26537                     this.dragSpecs.startPoint[0] - endPoint[0]
26538                 );
26539         }else{
26540             newSize = this.dragSpecs.startSize + 
26541                 (this.placement == Roo.SplitBar.TOP ?
26542                     endPoint[1] - this.dragSpecs.startPoint[1] :
26543                     this.dragSpecs.startPoint[1] - endPoint[1]
26544                 );
26545         }
26546         newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
26547         if(newSize != this.dragSpecs.startSize){
26548             if(this.fireEvent('beforeapply', this, newSize) !== false){
26549                 this.adapter.setElementSize(this, newSize);
26550                 this.fireEvent("moved", this, newSize);
26551                 this.fireEvent("resize", this, newSize);
26552             }
26553         }
26554     },
26555     
26556     /**
26557      * Get the adapter this SplitBar uses
26558      * @return The adapter object
26559      */
26560     getAdapter : function(){
26561         return this.adapter;
26562     },
26563     
26564     /**
26565      * Set the adapter this SplitBar uses
26566      * @param {Object} adapter A SplitBar adapter object
26567      */
26568     setAdapter : function(adapter){
26569         this.adapter = adapter;
26570         this.adapter.init(this);
26571     },
26572     
26573     /**
26574      * Gets the minimum size for the resizing element
26575      * @return {Number} The minimum size
26576      */
26577     getMinimumSize : function(){
26578         return this.minSize;
26579     },
26580     
26581     /**
26582      * Sets the minimum size for the resizing element
26583      * @param {Number} minSize The minimum size
26584      */
26585     setMinimumSize : function(minSize){
26586         this.minSize = minSize;
26587     },
26588     
26589     /**
26590      * Gets the maximum size for the resizing element
26591      * @return {Number} The maximum size
26592      */
26593     getMaximumSize : function(){
26594         return this.maxSize;
26595     },
26596     
26597     /**
26598      * Sets the maximum size for the resizing element
26599      * @param {Number} maxSize The maximum size
26600      */
26601     setMaximumSize : function(maxSize){
26602         this.maxSize = maxSize;
26603     },
26604     
26605     /**
26606      * Sets the initialize size for the resizing element
26607      * @param {Number} size The initial size
26608      */
26609     setCurrentSize : function(size){
26610         var oldAnimate = this.animate;
26611         this.animate = false;
26612         this.adapter.setElementSize(this, size);
26613         this.animate = oldAnimate;
26614     },
26615     
26616     /**
26617      * Destroy this splitbar. 
26618      * @param {Boolean} removeEl True to remove the element
26619      */
26620     destroy : function(removeEl){
26621         if(this.shim){
26622             this.shim.remove();
26623         }
26624         this.dd.unreg();
26625         this.proxy.parentNode.removeChild(this.proxy);
26626         if(removeEl){
26627             this.el.remove();
26628         }
26629     }
26630 });
26631
26632 /**
26633  * @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.
26634  */
26635 Roo.SplitBar.createProxy = function(dir){
26636     var proxy = new Roo.Element(document.createElement("div"));
26637     proxy.unselectable();
26638     var cls = 'x-splitbar-proxy';
26639     proxy.addClass(cls + ' ' + (dir == Roo.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
26640     document.body.appendChild(proxy.dom);
26641     return proxy.dom;
26642 };
26643
26644 /** 
26645  * @class Roo.SplitBar.BasicLayoutAdapter
26646  * Default Adapter. It assumes the splitter and resizing element are not positioned
26647  * elements and only gets/sets the width of the element. Generally used for table based layouts.
26648  */
26649 Roo.SplitBar.BasicLayoutAdapter = function(){
26650 };
26651
26652 Roo.SplitBar.BasicLayoutAdapter.prototype = {
26653     // do nothing for now
26654     init : function(s){
26655     
26656     },
26657     /**
26658      * Called before drag operations to get the current size of the resizing element. 
26659      * @param {Roo.SplitBar} s The SplitBar using this adapter
26660      */
26661      getElementSize : function(s){
26662         if(s.orientation == Roo.SplitBar.HORIZONTAL){
26663             return s.resizingEl.getWidth();
26664         }else{
26665             return s.resizingEl.getHeight();
26666         }
26667     },
26668     
26669     /**
26670      * Called after drag operations to set the size of the resizing element.
26671      * @param {Roo.SplitBar} s The SplitBar using this adapter
26672      * @param {Number} newSize The new size to set
26673      * @param {Function} onComplete A function to be invoked when resizing is complete
26674      */
26675     setElementSize : function(s, newSize, onComplete){
26676         if(s.orientation == Roo.SplitBar.HORIZONTAL){
26677             if(!s.animate){
26678                 s.resizingEl.setWidth(newSize);
26679                 if(onComplete){
26680                     onComplete(s, newSize);
26681                 }
26682             }else{
26683                 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
26684             }
26685         }else{
26686             
26687             if(!s.animate){
26688                 s.resizingEl.setHeight(newSize);
26689                 if(onComplete){
26690                     onComplete(s, newSize);
26691                 }
26692             }else{
26693                 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
26694             }
26695         }
26696     }
26697 };
26698
26699 /** 
26700  *@class Roo.SplitBar.AbsoluteLayoutAdapter
26701  * @extends Roo.SplitBar.BasicLayoutAdapter
26702  * Adapter that  moves the splitter element to align with the resized sizing element. 
26703  * Used with an absolute positioned SplitBar.
26704  * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
26705  * document.body, make sure you assign an id to the body element.
26706  */
26707 Roo.SplitBar.AbsoluteLayoutAdapter = function(container){
26708     this.basic = new Roo.SplitBar.BasicLayoutAdapter();
26709     this.container = Roo.get(container);
26710 };
26711
26712 Roo.SplitBar.AbsoluteLayoutAdapter.prototype = {
26713     init : function(s){
26714         this.basic.init(s);
26715     },
26716     
26717     getElementSize : function(s){
26718         return this.basic.getElementSize(s);
26719     },
26720     
26721     setElementSize : function(s, newSize, onComplete){
26722         this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
26723     },
26724     
26725     moveSplitter : function(s){
26726         var yes = Roo.SplitBar;
26727         switch(s.placement){
26728             case yes.LEFT:
26729                 s.el.setX(s.resizingEl.getRight());
26730                 break;
26731             case yes.RIGHT:
26732                 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
26733                 break;
26734             case yes.TOP:
26735                 s.el.setY(s.resizingEl.getBottom());
26736                 break;
26737             case yes.BOTTOM:
26738                 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
26739                 break;
26740         }
26741     }
26742 };
26743
26744 /**
26745  * Orientation constant - Create a vertical SplitBar
26746  * @static
26747  * @type Number
26748  */
26749 Roo.SplitBar.VERTICAL = 1;
26750
26751 /**
26752  * Orientation constant - Create a horizontal SplitBar
26753  * @static
26754  * @type Number
26755  */
26756 Roo.SplitBar.HORIZONTAL = 2;
26757
26758 /**
26759  * Placement constant - The resizing element is to the left of the splitter element
26760  * @static
26761  * @type Number
26762  */
26763 Roo.SplitBar.LEFT = 1;
26764
26765 /**
26766  * Placement constant - The resizing element is to the right of the splitter element
26767  * @static
26768  * @type Number
26769  */
26770 Roo.SplitBar.RIGHT = 2;
26771
26772 /**
26773  * Placement constant - The resizing element is positioned above the splitter element
26774  * @static
26775  * @type Number
26776  */
26777 Roo.SplitBar.TOP = 3;
26778
26779 /**
26780  * Placement constant - The resizing element is positioned under splitter element
26781  * @static
26782  * @type Number
26783  */
26784 Roo.SplitBar.BOTTOM = 4;
26785 /*
26786  * Based on:
26787  * Ext JS Library 1.1.1
26788  * Copyright(c) 2006-2007, Ext JS, LLC.
26789  *
26790  * Originally Released Under LGPL - original licence link has changed is not relivant.
26791  *
26792  * Fork - LGPL
26793  * <script type="text/javascript">
26794  */
26795
26796 /**
26797  * @class Roo.View
26798  * @extends Roo.util.Observable
26799  * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template. 
26800  * This class also supports single and multi selection modes. <br>
26801  * Create a data model bound view:
26802  <pre><code>
26803  var store = new Roo.data.Store(...);
26804
26805  var view = new Roo.View({
26806     el : "my-element",
26807     tpl : '&lt;div id="{0}"&gt;{2} - {1}&lt;/div&gt;', // auto create template
26808  
26809     singleSelect: true,
26810     selectedClass: "ydataview-selected",
26811     store: store
26812  });
26813
26814  // listen for node click?
26815  view.on("click", function(vw, index, node, e){
26816  alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
26817  });
26818
26819  // load XML data
26820  dataModel.load("foobar.xml");
26821  </code></pre>
26822  For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
26823  * <br><br>
26824  * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
26825  * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
26826  * 
26827  * Note: old style constructor is still suported (container, template, config)
26828  * 
26829  * @constructor
26830  * Create a new View
26831  * @param {Object} config The config object
26832  * 
26833  */
26834 Roo.View = function(config, depreciated_tpl, depreciated_config){
26835     
26836     this.parent = false;
26837     
26838     if (typeof(depreciated_tpl) == 'undefined') {
26839         // new way.. - universal constructor.
26840         Roo.apply(this, config);
26841         this.el  = Roo.get(this.el);
26842     } else {
26843         // old format..
26844         this.el  = Roo.get(config);
26845         this.tpl = depreciated_tpl;
26846         Roo.apply(this, depreciated_config);
26847     }
26848     this.wrapEl  = this.el.wrap().wrap();
26849     ///this.el = this.wrapEla.appendChild(document.createElement("div"));
26850     
26851     
26852     if(typeof(this.tpl) == "string"){
26853         this.tpl = new Roo.Template(this.tpl);
26854     } else {
26855         // support xtype ctors..
26856         this.tpl = new Roo.factory(this.tpl, Roo);
26857     }
26858     
26859     
26860     this.tpl.compile();
26861     
26862     /** @private */
26863     this.addEvents({
26864         /**
26865          * @event beforeclick
26866          * Fires before a click is processed. Returns false to cancel the default action.
26867          * @param {Roo.View} this
26868          * @param {Number} index The index of the target node
26869          * @param {HTMLElement} node The target node
26870          * @param {Roo.EventObject} e The raw event object
26871          */
26872             "beforeclick" : true,
26873         /**
26874          * @event click
26875          * Fires when a template node is clicked.
26876          * @param {Roo.View} this
26877          * @param {Number} index The index of the target node
26878          * @param {HTMLElement} node The target node
26879          * @param {Roo.EventObject} e The raw event object
26880          */
26881             "click" : true,
26882         /**
26883          * @event dblclick
26884          * Fires when a template node is double clicked.
26885          * @param {Roo.View} this
26886          * @param {Number} index The index of the target node
26887          * @param {HTMLElement} node The target node
26888          * @param {Roo.EventObject} e The raw event object
26889          */
26890             "dblclick" : true,
26891         /**
26892          * @event contextmenu
26893          * Fires when a template node is right clicked.
26894          * @param {Roo.View} this
26895          * @param {Number} index The index of the target node
26896          * @param {HTMLElement} node The target node
26897          * @param {Roo.EventObject} e The raw event object
26898          */
26899             "contextmenu" : true,
26900         /**
26901          * @event selectionchange
26902          * Fires when the selected nodes change.
26903          * @param {Roo.View} this
26904          * @param {Array} selections Array of the selected nodes
26905          */
26906             "selectionchange" : true,
26907     
26908         /**
26909          * @event beforeselect
26910          * Fires before a selection is made. If any handlers return false, the selection is cancelled.
26911          * @param {Roo.View} this
26912          * @param {HTMLElement} node The node to be selected
26913          * @param {Array} selections Array of currently selected nodes
26914          */
26915             "beforeselect" : true,
26916         /**
26917          * @event preparedata
26918          * Fires on every row to render, to allow you to change the data.
26919          * @param {Roo.View} this
26920          * @param {Object} data to be rendered (change this)
26921          */
26922           "preparedata" : true
26923           
26924           
26925         });
26926
26927
26928
26929     this.el.on({
26930         "click": this.onClick,
26931         "dblclick": this.onDblClick,
26932         "contextmenu": this.onContextMenu,
26933         scope:this
26934     });
26935
26936     this.selections = [];
26937     this.nodes = [];
26938     this.cmp = new Roo.CompositeElementLite([]);
26939     if(this.store){
26940         this.store = Roo.factory(this.store, Roo.data);
26941         this.setStore(this.store, true);
26942     }
26943     
26944     if ( this.footer && this.footer.xtype) {
26945            
26946          var fctr = this.wrapEl.appendChild(document.createElement("div"));
26947         
26948         this.footer.dataSource = this.store;
26949         this.footer.container = fctr;
26950         this.footer = Roo.factory(this.footer, Roo);
26951         fctr.insertFirst(this.el);
26952         
26953         // this is a bit insane - as the paging toolbar seems to detach the el..
26954 //        dom.parentNode.parentNode.parentNode
26955          // they get detached?
26956     }
26957     
26958     
26959     Roo.View.superclass.constructor.call(this);
26960     
26961     
26962 };
26963
26964 Roo.extend(Roo.View, Roo.util.Observable, {
26965     
26966      /**
26967      * @cfg {Roo.data.Store} store Data store to load data from.
26968      */
26969     store : false,
26970     
26971     /**
26972      * @cfg {String|Roo.Element} el The container element.
26973      */
26974     el : '',
26975     
26976     /**
26977      * @cfg {String|Roo.Template} tpl The template used by this View 
26978      */
26979     tpl : false,
26980     /**
26981      * @cfg {String} dataName the named area of the template to use as the data area
26982      *                          Works with domtemplates roo-name="name"
26983      */
26984     dataName: false,
26985     /**
26986      * @cfg {String} selectedClass The css class to add to selected nodes
26987      */
26988     selectedClass : "x-view-selected",
26989      /**
26990      * @cfg {String} emptyText The empty text to show when nothing is loaded.
26991      */
26992     emptyText : "",
26993     
26994     /**
26995      * @cfg {String} text to display on mask (default Loading)
26996      */
26997     mask : false,
26998     /**
26999      * @cfg {Boolean} multiSelect Allow multiple selection
27000      */
27001     multiSelect : false,
27002     /**
27003      * @cfg {Boolean} singleSelect Allow single selection
27004      */
27005     singleSelect:  false,
27006     
27007     /**
27008      * @cfg {Boolean} toggleSelect - selecting 
27009      */
27010     toggleSelect : false,
27011     
27012     /**
27013      * @cfg {Boolean} tickable - selecting 
27014      */
27015     tickable : false,
27016     
27017     /**
27018      * Returns the element this view is bound to.
27019      * @return {Roo.Element}
27020      */
27021     getEl : function(){
27022         return this.wrapEl;
27023     },
27024     
27025     
27026
27027     /**
27028      * Refreshes the view. - called by datachanged on the store. - do not call directly.
27029      */
27030     refresh : function(){
27031         //Roo.log('refresh');
27032         var t = this.tpl;
27033         
27034         // if we are using something like 'domtemplate', then
27035         // the what gets used is:
27036         // t.applySubtemplate(NAME, data, wrapping data..)
27037         // the outer template then get' applied with
27038         //     the store 'extra data'
27039         // and the body get's added to the
27040         //      roo-name="data" node?
27041         //      <span class='roo-tpl-{name}'></span> ?????
27042         
27043         
27044         
27045         this.clearSelections();
27046         this.el.update("");
27047         var html = [];
27048         var records = this.store.getRange();
27049         if(records.length < 1) {
27050             
27051             // is this valid??  = should it render a template??
27052             
27053             this.el.update(this.emptyText);
27054             return;
27055         }
27056         var el = this.el;
27057         if (this.dataName) {
27058             this.el.update(t.apply(this.store.meta)); //????
27059             el = this.el.child('.roo-tpl-' + this.dataName);
27060         }
27061         
27062         for(var i = 0, len = records.length; i < len; i++){
27063             var data = this.prepareData(records[i].data, i, records[i]);
27064             this.fireEvent("preparedata", this, data, i, records[i]);
27065             
27066             var d = Roo.apply({}, data);
27067             
27068             if(this.tickable){
27069                 Roo.apply(d, {'roo-id' : Roo.id()});
27070                 
27071                 var _this = this;
27072             
27073                 Roo.each(this.parent.item, function(item){
27074                     if(item[_this.parent.valueField] != data[_this.parent.valueField]){
27075                         return;
27076                     }
27077                     Roo.apply(d, {'roo-data-checked' : 'checked'});
27078                 });
27079             }
27080             
27081             html[html.length] = Roo.util.Format.trim(
27082                 this.dataName ?
27083                     t.applySubtemplate(this.dataName, d, this.store.meta) :
27084                     t.apply(d)
27085             );
27086         }
27087         
27088         
27089         
27090         el.update(html.join(""));
27091         this.nodes = el.dom.childNodes;
27092         this.updateIndexes(0);
27093     },
27094     
27095
27096     /**
27097      * Function to override to reformat the data that is sent to
27098      * the template for each node.
27099      * DEPRICATED - use the preparedata event handler.
27100      * @param {Array/Object} data The raw data (array of colData for a data model bound view or
27101      * a JSON object for an UpdateManager bound view).
27102      */
27103     prepareData : function(data, index, record)
27104     {
27105         this.fireEvent("preparedata", this, data, index, record);
27106         return data;
27107     },
27108
27109     onUpdate : function(ds, record){
27110         // Roo.log('on update');   
27111         this.clearSelections();
27112         var index = this.store.indexOf(record);
27113         var n = this.nodes[index];
27114         this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
27115         n.parentNode.removeChild(n);
27116         this.updateIndexes(index, index);
27117     },
27118
27119     
27120     
27121 // --------- FIXME     
27122     onAdd : function(ds, records, index)
27123     {
27124         //Roo.log(['on Add', ds, records, index] );        
27125         this.clearSelections();
27126         if(this.nodes.length == 0){
27127             this.refresh();
27128             return;
27129         }
27130         var n = this.nodes[index];
27131         for(var i = 0, len = records.length; i < len; i++){
27132             var d = this.prepareData(records[i].data, i, records[i]);
27133             if(n){
27134                 this.tpl.insertBefore(n, d);
27135             }else{
27136                 
27137                 this.tpl.append(this.el, d);
27138             }
27139         }
27140         this.updateIndexes(index);
27141     },
27142
27143     onRemove : function(ds, record, index){
27144        // Roo.log('onRemove');
27145         this.clearSelections();
27146         var el = this.dataName  ?
27147             this.el.child('.roo-tpl-' + this.dataName) :
27148             this.el; 
27149         
27150         el.dom.removeChild(this.nodes[index]);
27151         this.updateIndexes(index);
27152     },
27153
27154     /**
27155      * Refresh an individual node.
27156      * @param {Number} index
27157      */
27158     refreshNode : function(index){
27159         this.onUpdate(this.store, this.store.getAt(index));
27160     },
27161
27162     updateIndexes : function(startIndex, endIndex){
27163         var ns = this.nodes;
27164         startIndex = startIndex || 0;
27165         endIndex = endIndex || ns.length - 1;
27166         for(var i = startIndex; i <= endIndex; i++){
27167             ns[i].nodeIndex = i;
27168         }
27169     },
27170
27171     /**
27172      * Changes the data store this view uses and refresh the view.
27173      * @param {Store} store
27174      */
27175     setStore : function(store, initial){
27176         if(!initial && this.store){
27177             this.store.un("datachanged", this.refresh);
27178             this.store.un("add", this.onAdd);
27179             this.store.un("remove", this.onRemove);
27180             this.store.un("update", this.onUpdate);
27181             this.store.un("clear", this.refresh);
27182             this.store.un("beforeload", this.onBeforeLoad);
27183             this.store.un("load", this.onLoad);
27184             this.store.un("loadexception", this.onLoad);
27185         }
27186         if(store){
27187           
27188             store.on("datachanged", this.refresh, this);
27189             store.on("add", this.onAdd, this);
27190             store.on("remove", this.onRemove, this);
27191             store.on("update", this.onUpdate, this);
27192             store.on("clear", this.refresh, this);
27193             store.on("beforeload", this.onBeforeLoad, this);
27194             store.on("load", this.onLoad, this);
27195             store.on("loadexception", this.onLoad, this);
27196         }
27197         
27198         if(store){
27199             this.refresh();
27200         }
27201     },
27202     /**
27203      * onbeforeLoad - masks the loading area.
27204      *
27205      */
27206     onBeforeLoad : function(store,opts)
27207     {
27208          //Roo.log('onBeforeLoad');   
27209         if (!opts.add) {
27210             this.el.update("");
27211         }
27212         this.el.mask(this.mask ? this.mask : "Loading" ); 
27213     },
27214     onLoad : function ()
27215     {
27216         this.el.unmask();
27217     },
27218     
27219
27220     /**
27221      * Returns the template node the passed child belongs to or null if it doesn't belong to one.
27222      * @param {HTMLElement} node
27223      * @return {HTMLElement} The template node
27224      */
27225     findItemFromChild : function(node){
27226         var el = this.dataName  ?
27227             this.el.child('.roo-tpl-' + this.dataName,true) :
27228             this.el.dom; 
27229         
27230         if(!node || node.parentNode == el){
27231                     return node;
27232             }
27233             var p = node.parentNode;
27234             while(p && p != el){
27235             if(p.parentNode == el){
27236                 return p;
27237             }
27238             p = p.parentNode;
27239         }
27240             return null;
27241     },
27242
27243     /** @ignore */
27244     onClick : function(e){
27245         var item = this.findItemFromChild(e.getTarget());
27246         if(item){
27247             var index = this.indexOf(item);
27248             if(this.onItemClick(item, index, e) !== false){
27249                 this.fireEvent("click", this, index, item, e);
27250             }
27251         }else{
27252             this.clearSelections();
27253         }
27254     },
27255
27256     /** @ignore */
27257     onContextMenu : function(e){
27258         var item = this.findItemFromChild(e.getTarget());
27259         if(item){
27260             this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
27261         }
27262     },
27263
27264     /** @ignore */
27265     onDblClick : function(e){
27266         var item = this.findItemFromChild(e.getTarget());
27267         if(item){
27268             this.fireEvent("dblclick", this, this.indexOf(item), item, e);
27269         }
27270     },
27271
27272     onItemClick : function(item, index, e)
27273     {
27274         if(this.fireEvent("beforeclick", this, index, item, e) === false){
27275             return false;
27276         }
27277         if (this.toggleSelect) {
27278             var m = this.isSelected(item) ? 'unselect' : 'select';
27279             //Roo.log(m);
27280             var _t = this;
27281             _t[m](item, true, false);
27282             return true;
27283         }
27284         if(this.multiSelect || this.singleSelect){
27285             if(this.multiSelect && e.shiftKey && this.lastSelection){
27286                 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
27287             }else{
27288                 this.select(item, this.multiSelect && e.ctrlKey);
27289                 this.lastSelection = item;
27290             }
27291             
27292             if(!this.tickable){
27293                 e.preventDefault();
27294             }
27295             
27296         }
27297         return true;
27298     },
27299
27300     /**
27301      * Get the number of selected nodes.
27302      * @return {Number}
27303      */
27304     getSelectionCount : function(){
27305         return this.selections.length;
27306     },
27307
27308     /**
27309      * Get the currently selected nodes.
27310      * @return {Array} An array of HTMLElements
27311      */
27312     getSelectedNodes : function(){
27313         return this.selections;
27314     },
27315
27316     /**
27317      * Get the indexes of the selected nodes.
27318      * @return {Array}
27319      */
27320     getSelectedIndexes : function(){
27321         var indexes = [], s = this.selections;
27322         for(var i = 0, len = s.length; i < len; i++){
27323             indexes.push(s[i].nodeIndex);
27324         }
27325         return indexes;
27326     },
27327
27328     /**
27329      * Clear all selections
27330      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
27331      */
27332     clearSelections : function(suppressEvent){
27333         if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
27334             this.cmp.elements = this.selections;
27335             this.cmp.removeClass(this.selectedClass);
27336             this.selections = [];
27337             if(!suppressEvent){
27338                 this.fireEvent("selectionchange", this, this.selections);
27339             }
27340         }
27341     },
27342
27343     /**
27344      * Returns true if the passed node is selected
27345      * @param {HTMLElement/Number} node The node or node index
27346      * @return {Boolean}
27347      */
27348     isSelected : function(node){
27349         var s = this.selections;
27350         if(s.length < 1){
27351             return false;
27352         }
27353         node = this.getNode(node);
27354         return s.indexOf(node) !== -1;
27355     },
27356
27357     /**
27358      * Selects nodes.
27359      * @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
27360      * @param {Boolean} keepExisting (optional) true to keep existing selections
27361      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
27362      */
27363     select : function(nodeInfo, keepExisting, suppressEvent){
27364         if(nodeInfo instanceof Array){
27365             if(!keepExisting){
27366                 this.clearSelections(true);
27367             }
27368             for(var i = 0, len = nodeInfo.length; i < len; i++){
27369                 this.select(nodeInfo[i], true, true);
27370             }
27371             return;
27372         } 
27373         var node = this.getNode(nodeInfo);
27374         if(!node || this.isSelected(node)){
27375             return; // already selected.
27376         }
27377         if(!keepExisting){
27378             this.clearSelections(true);
27379         }
27380         
27381         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
27382             Roo.fly(node).addClass(this.selectedClass);
27383             this.selections.push(node);
27384             if(!suppressEvent){
27385                 this.fireEvent("selectionchange", this, this.selections);
27386             }
27387         }
27388         
27389         
27390     },
27391       /**
27392      * Unselects nodes.
27393      * @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
27394      * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
27395      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
27396      */
27397     unselect : function(nodeInfo, keepExisting, suppressEvent)
27398     {
27399         if(nodeInfo instanceof Array){
27400             Roo.each(this.selections, function(s) {
27401                 this.unselect(s, nodeInfo);
27402             }, this);
27403             return;
27404         }
27405         var node = this.getNode(nodeInfo);
27406         if(!node || !this.isSelected(node)){
27407             //Roo.log("not selected");
27408             return; // not selected.
27409         }
27410         // fireevent???
27411         var ns = [];
27412         Roo.each(this.selections, function(s) {
27413             if (s == node ) {
27414                 Roo.fly(node).removeClass(this.selectedClass);
27415
27416                 return;
27417             }
27418             ns.push(s);
27419         },this);
27420         
27421         this.selections= ns;
27422         this.fireEvent("selectionchange", this, this.selections);
27423     },
27424
27425     /**
27426      * Gets a template node.
27427      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
27428      * @return {HTMLElement} The node or null if it wasn't found
27429      */
27430     getNode : function(nodeInfo){
27431         if(typeof nodeInfo == "string"){
27432             return document.getElementById(nodeInfo);
27433         }else if(typeof nodeInfo == "number"){
27434             return this.nodes[nodeInfo];
27435         }
27436         return nodeInfo;
27437     },
27438
27439     /**
27440      * Gets a range template nodes.
27441      * @param {Number} startIndex
27442      * @param {Number} endIndex
27443      * @return {Array} An array of nodes
27444      */
27445     getNodes : function(start, end){
27446         var ns = this.nodes;
27447         start = start || 0;
27448         end = typeof end == "undefined" ? ns.length - 1 : end;
27449         var nodes = [];
27450         if(start <= end){
27451             for(var i = start; i <= end; i++){
27452                 nodes.push(ns[i]);
27453             }
27454         } else{
27455             for(var i = start; i >= end; i--){
27456                 nodes.push(ns[i]);
27457             }
27458         }
27459         return nodes;
27460     },
27461
27462     /**
27463      * Finds the index of the passed node
27464      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
27465      * @return {Number} The index of the node or -1
27466      */
27467     indexOf : function(node){
27468         node = this.getNode(node);
27469         if(typeof node.nodeIndex == "number"){
27470             return node.nodeIndex;
27471         }
27472         var ns = this.nodes;
27473         for(var i = 0, len = ns.length; i < len; i++){
27474             if(ns[i] == node){
27475                 return i;
27476             }
27477         }
27478         return -1;
27479     }
27480 });
27481 /*
27482  * Based on:
27483  * Ext JS Library 1.1.1
27484  * Copyright(c) 2006-2007, Ext JS, LLC.
27485  *
27486  * Originally Released Under LGPL - original licence link has changed is not relivant.
27487  *
27488  * Fork - LGPL
27489  * <script type="text/javascript">
27490  */
27491
27492 /**
27493  * @class Roo.JsonView
27494  * @extends Roo.View
27495  * Shortcut class to create a JSON + {@link Roo.UpdateManager} template view. Usage:
27496 <pre><code>
27497 var view = new Roo.JsonView({
27498     container: "my-element",
27499     tpl: '&lt;div id="{id}"&gt;{foo} - {bar}&lt;/div&gt;', // auto create template
27500     multiSelect: true, 
27501     jsonRoot: "data" 
27502 });
27503
27504 // listen for node click?
27505 view.on("click", function(vw, index, node, e){
27506     alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
27507 });
27508
27509 // direct load of JSON data
27510 view.load("foobar.php");
27511
27512 // Example from my blog list
27513 var tpl = new Roo.Template(
27514     '&lt;div class="entry"&gt;' +
27515     '&lt;a class="entry-title" href="{link}"&gt;{title}&lt;/a&gt;' +
27516     "&lt;h4&gt;{date} by {author} | {comments} Comments&lt;/h4&gt;{description}" +
27517     "&lt;/div&gt;&lt;hr /&gt;"
27518 );
27519
27520 var moreView = new Roo.JsonView({
27521     container :  "entry-list", 
27522     template : tpl,
27523     jsonRoot: "posts"
27524 });
27525 moreView.on("beforerender", this.sortEntries, this);
27526 moreView.load({
27527     url: "/blog/get-posts.php",
27528     params: "allposts=true",
27529     text: "Loading Blog Entries..."
27530 });
27531 </code></pre>
27532
27533 * Note: old code is supported with arguments : (container, template, config)
27534
27535
27536  * @constructor
27537  * Create a new JsonView
27538  * 
27539  * @param {Object} config The config object
27540  * 
27541  */
27542 Roo.JsonView = function(config, depreciated_tpl, depreciated_config){
27543     
27544     
27545     Roo.JsonView.superclass.constructor.call(this, config, depreciated_tpl, depreciated_config);
27546
27547     var um = this.el.getUpdateManager();
27548     um.setRenderer(this);
27549     um.on("update", this.onLoad, this);
27550     um.on("failure", this.onLoadException, this);
27551
27552     /**
27553      * @event beforerender
27554      * Fires before rendering of the downloaded JSON data.
27555      * @param {Roo.JsonView} this
27556      * @param {Object} data The JSON data loaded
27557      */
27558     /**
27559      * @event load
27560      * Fires when data is loaded.
27561      * @param {Roo.JsonView} this
27562      * @param {Object} data The JSON data loaded
27563      * @param {Object} response The raw Connect response object
27564      */
27565     /**
27566      * @event loadexception
27567      * Fires when loading fails.
27568      * @param {Roo.JsonView} this
27569      * @param {Object} response The raw Connect response object
27570      */
27571     this.addEvents({
27572         'beforerender' : true,
27573         'load' : true,
27574         'loadexception' : true
27575     });
27576 };
27577 Roo.extend(Roo.JsonView, Roo.View, {
27578     /**
27579      * @type {String} The root property in the loaded JSON object that contains the data
27580      */
27581     jsonRoot : "",
27582
27583     /**
27584      * Refreshes the view.
27585      */
27586     refresh : function(){
27587         this.clearSelections();
27588         this.el.update("");
27589         var html = [];
27590         var o = this.jsonData;
27591         if(o && o.length > 0){
27592             for(var i = 0, len = o.length; i < len; i++){
27593                 var data = this.prepareData(o[i], i, o);
27594                 html[html.length] = this.tpl.apply(data);
27595             }
27596         }else{
27597             html.push(this.emptyText);
27598         }
27599         this.el.update(html.join(""));
27600         this.nodes = this.el.dom.childNodes;
27601         this.updateIndexes(0);
27602     },
27603
27604     /**
27605      * 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.
27606      * @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:
27607      <pre><code>
27608      view.load({
27609          url: "your-url.php",
27610          params: {param1: "foo", param2: "bar"}, // or a URL encoded string
27611          callback: yourFunction,
27612          scope: yourObject, //(optional scope)
27613          discardUrl: false,
27614          nocache: false,
27615          text: "Loading...",
27616          timeout: 30,
27617          scripts: false
27618      });
27619      </code></pre>
27620      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
27621      * 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.
27622      * @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}
27623      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess)
27624      * @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.
27625      */
27626     load : function(){
27627         var um = this.el.getUpdateManager();
27628         um.update.apply(um, arguments);
27629     },
27630
27631     // note - render is a standard framework call...
27632     // using it for the response is really flaky... - it's called by UpdateManager normally, except when called by the XComponent/addXtype.
27633     render : function(el, response){
27634         
27635         this.clearSelections();
27636         this.el.update("");
27637         var o;
27638         try{
27639             if (response != '') {
27640                 o = Roo.util.JSON.decode(response.responseText);
27641                 if(this.jsonRoot){
27642                     
27643                     o = o[this.jsonRoot];
27644                 }
27645             }
27646         } catch(e){
27647         }
27648         /**
27649          * The current JSON data or null
27650          */
27651         this.jsonData = o;
27652         this.beforeRender();
27653         this.refresh();
27654     },
27655
27656 /**
27657  * Get the number of records in the current JSON dataset
27658  * @return {Number}
27659  */
27660     getCount : function(){
27661         return this.jsonData ? this.jsonData.length : 0;
27662     },
27663
27664 /**
27665  * Returns the JSON object for the specified node(s)
27666  * @param {HTMLElement/Array} node The node or an array of nodes
27667  * @return {Object/Array} If you pass in an array, you get an array back, otherwise
27668  * you get the JSON object for the node
27669  */
27670     getNodeData : function(node){
27671         if(node instanceof Array){
27672             var data = [];
27673             for(var i = 0, len = node.length; i < len; i++){
27674                 data.push(this.getNodeData(node[i]));
27675             }
27676             return data;
27677         }
27678         return this.jsonData[this.indexOf(node)] || null;
27679     },
27680
27681     beforeRender : function(){
27682         this.snapshot = this.jsonData;
27683         if(this.sortInfo){
27684             this.sort.apply(this, this.sortInfo);
27685         }
27686         this.fireEvent("beforerender", this, this.jsonData);
27687     },
27688
27689     onLoad : function(el, o){
27690         this.fireEvent("load", this, this.jsonData, o);
27691     },
27692
27693     onLoadException : function(el, o){
27694         this.fireEvent("loadexception", this, o);
27695     },
27696
27697 /**
27698  * Filter the data by a specific property.
27699  * @param {String} property A property on your JSON objects
27700  * @param {String/RegExp} value Either string that the property values
27701  * should start with, or a RegExp to test against the property
27702  */
27703     filter : function(property, value){
27704         if(this.jsonData){
27705             var data = [];
27706             var ss = this.snapshot;
27707             if(typeof value == "string"){
27708                 var vlen = value.length;
27709                 if(vlen == 0){
27710                     this.clearFilter();
27711                     return;
27712                 }
27713                 value = value.toLowerCase();
27714                 for(var i = 0, len = ss.length; i < len; i++){
27715                     var o = ss[i];
27716                     if(o[property].substr(0, vlen).toLowerCase() == value){
27717                         data.push(o);
27718                     }
27719                 }
27720             } else if(value.exec){ // regex?
27721                 for(var i = 0, len = ss.length; i < len; i++){
27722                     var o = ss[i];
27723                     if(value.test(o[property])){
27724                         data.push(o);
27725                     }
27726                 }
27727             } else{
27728                 return;
27729             }
27730             this.jsonData = data;
27731             this.refresh();
27732         }
27733     },
27734
27735 /**
27736  * Filter by a function. The passed function will be called with each
27737  * object in the current dataset. If the function returns true the value is kept,
27738  * otherwise it is filtered.
27739  * @param {Function} fn
27740  * @param {Object} scope (optional) The scope of the function (defaults to this JsonView)
27741  */
27742     filterBy : function(fn, scope){
27743         if(this.jsonData){
27744             var data = [];
27745             var ss = this.snapshot;
27746             for(var i = 0, len = ss.length; i < len; i++){
27747                 var o = ss[i];
27748                 if(fn.call(scope || this, o)){
27749                     data.push(o);
27750                 }
27751             }
27752             this.jsonData = data;
27753             this.refresh();
27754         }
27755     },
27756
27757 /**
27758  * Clears the current filter.
27759  */
27760     clearFilter : function(){
27761         if(this.snapshot && this.jsonData != this.snapshot){
27762             this.jsonData = this.snapshot;
27763             this.refresh();
27764         }
27765     },
27766
27767
27768 /**
27769  * Sorts the data for this view and refreshes it.
27770  * @param {String} property A property on your JSON objects to sort on
27771  * @param {String} direction (optional) "desc" or "asc" (defaults to "asc")
27772  * @param {Function} sortType (optional) A function to call to convert the data to a sortable value.
27773  */
27774     sort : function(property, dir, sortType){
27775         this.sortInfo = Array.prototype.slice.call(arguments, 0);
27776         if(this.jsonData){
27777             var p = property;
27778             var dsc = dir && dir.toLowerCase() == "desc";
27779             var f = function(o1, o2){
27780                 var v1 = sortType ? sortType(o1[p]) : o1[p];
27781                 var v2 = sortType ? sortType(o2[p]) : o2[p];
27782                 ;
27783                 if(v1 < v2){
27784                     return dsc ? +1 : -1;
27785                 } else if(v1 > v2){
27786                     return dsc ? -1 : +1;
27787                 } else{
27788                     return 0;
27789                 }
27790             };
27791             this.jsonData.sort(f);
27792             this.refresh();
27793             if(this.jsonData != this.snapshot){
27794                 this.snapshot.sort(f);
27795             }
27796         }
27797     }
27798 });/*
27799  * Based on:
27800  * Ext JS Library 1.1.1
27801  * Copyright(c) 2006-2007, Ext JS, LLC.
27802  *
27803  * Originally Released Under LGPL - original licence link has changed is not relivant.
27804  *
27805  * Fork - LGPL
27806  * <script type="text/javascript">
27807  */
27808  
27809
27810 /**
27811  * @class Roo.ColorPalette
27812  * @extends Roo.Component
27813  * Simple color palette class for choosing colors.  The palette can be rendered to any container.<br />
27814  * Here's an example of typical usage:
27815  * <pre><code>
27816 var cp = new Roo.ColorPalette({value:'993300'});  // initial selected color
27817 cp.render('my-div');
27818
27819 cp.on('select', function(palette, selColor){
27820     // do something with selColor
27821 });
27822 </code></pre>
27823  * @constructor
27824  * Create a new ColorPalette
27825  * @param {Object} config The config object
27826  */
27827 Roo.ColorPalette = function(config){
27828     Roo.ColorPalette.superclass.constructor.call(this, config);
27829     this.addEvents({
27830         /**
27831              * @event select
27832              * Fires when a color is selected
27833              * @param {ColorPalette} this
27834              * @param {String} color The 6-digit color hex code (without the # symbol)
27835              */
27836         select: true
27837     });
27838
27839     if(this.handler){
27840         this.on("select", this.handler, this.scope, true);
27841     }
27842 };
27843 Roo.extend(Roo.ColorPalette, Roo.Component, {
27844     /**
27845      * @cfg {String} itemCls
27846      * The CSS class to apply to the containing element (defaults to "x-color-palette")
27847      */
27848     itemCls : "x-color-palette",
27849     /**
27850      * @cfg {String} value
27851      * The initial color to highlight (should be a valid 6-digit color hex code without the # symbol).  Note that
27852      * the hex codes are case-sensitive.
27853      */
27854     value : null,
27855     clickEvent:'click',
27856     // private
27857     ctype: "Roo.ColorPalette",
27858
27859     /**
27860      * @cfg {Boolean} allowReselect If set to true then reselecting a color that is already selected fires the selection event
27861      */
27862     allowReselect : false,
27863
27864     /**
27865      * <p>An array of 6-digit color hex code strings (without the # symbol).  This array can contain any number
27866      * of colors, and each hex code should be unique.  The width of the palette is controlled via CSS by adjusting
27867      * the width property of the 'x-color-palette' class (or assigning a custom class), so you can balance the number
27868      * of colors with the width setting until the box is symmetrical.</p>
27869      * <p>You can override individual colors if needed:</p>
27870      * <pre><code>
27871 var cp = new Roo.ColorPalette();
27872 cp.colors[0] = "FF0000";  // change the first box to red
27873 </code></pre>
27874
27875 Or you can provide a custom array of your own for complete control:
27876 <pre><code>
27877 var cp = new Roo.ColorPalette();
27878 cp.colors = ["000000", "993300", "333300"];
27879 </code></pre>
27880      * @type Array
27881      */
27882     colors : [
27883         "000000", "993300", "333300", "003300", "003366", "000080", "333399", "333333",
27884         "800000", "FF6600", "808000", "008000", "008080", "0000FF", "666699", "808080",
27885         "FF0000", "FF9900", "99CC00", "339966", "33CCCC", "3366FF", "800080", "969696",
27886         "FF00FF", "FFCC00", "FFFF00", "00FF00", "00FFFF", "00CCFF", "993366", "C0C0C0",
27887         "FF99CC", "FFCC99", "FFFF99", "CCFFCC", "CCFFFF", "99CCFF", "CC99FF", "FFFFFF"
27888     ],
27889
27890     // private
27891     onRender : function(container, position){
27892         var t = new Roo.MasterTemplate(
27893             '<tpl><a href="#" class="color-{0}" hidefocus="on"><em><span style="background:#{0}" unselectable="on">&#160;</span></em></a></tpl>'
27894         );
27895         var c = this.colors;
27896         for(var i = 0, len = c.length; i < len; i++){
27897             t.add([c[i]]);
27898         }
27899         var el = document.createElement("div");
27900         el.className = this.itemCls;
27901         t.overwrite(el);
27902         container.dom.insertBefore(el, position);
27903         this.el = Roo.get(el);
27904         this.el.on(this.clickEvent, this.handleClick,  this, {delegate: "a"});
27905         if(this.clickEvent != 'click'){
27906             this.el.on('click', Roo.emptyFn,  this, {delegate: "a", preventDefault:true});
27907         }
27908     },
27909
27910     // private
27911     afterRender : function(){
27912         Roo.ColorPalette.superclass.afterRender.call(this);
27913         if(this.value){
27914             var s = this.value;
27915             this.value = null;
27916             this.select(s);
27917         }
27918     },
27919
27920     // private
27921     handleClick : function(e, t){
27922         e.preventDefault();
27923         if(!this.disabled){
27924             var c = t.className.match(/(?:^|\s)color-(.{6})(?:\s|$)/)[1];
27925             this.select(c.toUpperCase());
27926         }
27927     },
27928
27929     /**
27930      * Selects the specified color in the palette (fires the select event)
27931      * @param {String} color A valid 6-digit color hex code (# will be stripped if included)
27932      */
27933     select : function(color){
27934         color = color.replace("#", "");
27935         if(color != this.value || this.allowReselect){
27936             var el = this.el;
27937             if(this.value){
27938                 el.child("a.color-"+this.value).removeClass("x-color-palette-sel");
27939             }
27940             el.child("a.color-"+color).addClass("x-color-palette-sel");
27941             this.value = color;
27942             this.fireEvent("select", this, color);
27943         }
27944     }
27945 });/*
27946  * Based on:
27947  * Ext JS Library 1.1.1
27948  * Copyright(c) 2006-2007, Ext JS, LLC.
27949  *
27950  * Originally Released Under LGPL - original licence link has changed is not relivant.
27951  *
27952  * Fork - LGPL
27953  * <script type="text/javascript">
27954  */
27955  
27956 /**
27957  * @class Roo.DatePicker
27958  * @extends Roo.Component
27959  * Simple date picker class.
27960  * @constructor
27961  * Create a new DatePicker
27962  * @param {Object} config The config object
27963  */
27964 Roo.DatePicker = function(config){
27965     Roo.DatePicker.superclass.constructor.call(this, config);
27966
27967     this.value = config && config.value ?
27968                  config.value.clearTime() : new Date().clearTime();
27969
27970     this.addEvents({
27971         /**
27972              * @event select
27973              * Fires when a date is selected
27974              * @param {DatePicker} this
27975              * @param {Date} date The selected date
27976              */
27977         'select': true,
27978         /**
27979              * @event monthchange
27980              * Fires when the displayed month changes 
27981              * @param {DatePicker} this
27982              * @param {Date} date The selected month
27983              */
27984         'monthchange': true
27985     });
27986
27987     if(this.handler){
27988         this.on("select", this.handler,  this.scope || this);
27989     }
27990     // build the disabledDatesRE
27991     if(!this.disabledDatesRE && this.disabledDates){
27992         var dd = this.disabledDates;
27993         var re = "(?:";
27994         for(var i = 0; i < dd.length; i++){
27995             re += dd[i];
27996             if(i != dd.length-1) {
27997                 re += "|";
27998             }
27999         }
28000         this.disabledDatesRE = new RegExp(re + ")");
28001     }
28002 };
28003
28004 Roo.extend(Roo.DatePicker, Roo.Component, {
28005     /**
28006      * @cfg {String} todayText
28007      * The text to display on the button that selects the current date (defaults to "Today")
28008      */
28009     todayText : "Today",
28010     /**
28011      * @cfg {String} okText
28012      * The text to display on the ok button
28013      */
28014     okText : "&#160;OK&#160;", // &#160; to give the user extra clicking room
28015     /**
28016      * @cfg {String} cancelText
28017      * The text to display on the cancel button
28018      */
28019     cancelText : "Cancel",
28020     /**
28021      * @cfg {String} todayTip
28022      * The tooltip to display for the button that selects the current date (defaults to "{current date} (Spacebar)")
28023      */
28024     todayTip : "{0} (Spacebar)",
28025     /**
28026      * @cfg {Date} minDate
28027      * Minimum allowable date (JavaScript date object, defaults to null)
28028      */
28029     minDate : null,
28030     /**
28031      * @cfg {Date} maxDate
28032      * Maximum allowable date (JavaScript date object, defaults to null)
28033      */
28034     maxDate : null,
28035     /**
28036      * @cfg {String} minText
28037      * The error text to display if the minDate validation fails (defaults to "This date is before the minimum date")
28038      */
28039     minText : "This date is before the minimum date",
28040     /**
28041      * @cfg {String} maxText
28042      * The error text to display if the maxDate validation fails (defaults to "This date is after the maximum date")
28043      */
28044     maxText : "This date is after the maximum date",
28045     /**
28046      * @cfg {String} format
28047      * The default date format string which can be overriden for localization support.  The format must be
28048      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
28049      */
28050     format : "m/d/y",
28051     /**
28052      * @cfg {Array} disabledDays
28053      * An array of days to disable, 0-based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
28054      */
28055     disabledDays : null,
28056     /**
28057      * @cfg {String} disabledDaysText
28058      * The tooltip to display when the date falls on a disabled day (defaults to "")
28059      */
28060     disabledDaysText : "",
28061     /**
28062      * @cfg {RegExp} disabledDatesRE
28063      * JavaScript regular expression used to disable a pattern of dates (defaults to null)
28064      */
28065     disabledDatesRE : null,
28066     /**
28067      * @cfg {String} disabledDatesText
28068      * The tooltip text to display when the date falls on a disabled date (defaults to "")
28069      */
28070     disabledDatesText : "",
28071     /**
28072      * @cfg {Boolean} constrainToViewport
28073      * True to constrain the date picker to the viewport (defaults to true)
28074      */
28075     constrainToViewport : true,
28076     /**
28077      * @cfg {Array} monthNames
28078      * An array of textual month names which can be overriden for localization support (defaults to Date.monthNames)
28079      */
28080     monthNames : Date.monthNames,
28081     /**
28082      * @cfg {Array} dayNames
28083      * An array of textual day names which can be overriden for localization support (defaults to Date.dayNames)
28084      */
28085     dayNames : Date.dayNames,
28086     /**
28087      * @cfg {String} nextText
28088      * The next month navigation button tooltip (defaults to 'Next Month (Control+Right)')
28089      */
28090     nextText: 'Next Month (Control+Right)',
28091     /**
28092      * @cfg {String} prevText
28093      * The previous month navigation button tooltip (defaults to 'Previous Month (Control+Left)')
28094      */
28095     prevText: 'Previous Month (Control+Left)',
28096     /**
28097      * @cfg {String} monthYearText
28098      * The header month selector tooltip (defaults to 'Choose a month (Control+Up/Down to move years)')
28099      */
28100     monthYearText: 'Choose a month (Control+Up/Down to move years)',
28101     /**
28102      * @cfg {Number} startDay
28103      * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
28104      */
28105     startDay : 0,
28106     /**
28107      * @cfg {Bool} showClear
28108      * Show a clear button (usefull for date form elements that can be blank.)
28109      */
28110     
28111     showClear: false,
28112     
28113     /**
28114      * Sets the value of the date field
28115      * @param {Date} value The date to set
28116      */
28117     setValue : function(value){
28118         var old = this.value;
28119         
28120         if (typeof(value) == 'string') {
28121          
28122             value = Date.parseDate(value, this.format);
28123         }
28124         if (!value) {
28125             value = new Date();
28126         }
28127         
28128         this.value = value.clearTime(true);
28129         if(this.el){
28130             this.update(this.value);
28131         }
28132     },
28133
28134     /**
28135      * Gets the current selected value of the date field
28136      * @return {Date} The selected date
28137      */
28138     getValue : function(){
28139         return this.value;
28140     },
28141
28142     // private
28143     focus : function(){
28144         if(this.el){
28145             this.update(this.activeDate);
28146         }
28147     },
28148
28149     // privateval
28150     onRender : function(container, position){
28151         
28152         var m = [
28153              '<table cellspacing="0">',
28154                 '<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>',
28155                 '<tr><td colspan="3"><table class="x-date-inner" cellspacing="0"><thead><tr>'];
28156         var dn = this.dayNames;
28157         for(var i = 0; i < 7; i++){
28158             var d = this.startDay+i;
28159             if(d > 6){
28160                 d = d-7;
28161             }
28162             m.push("<th><span>", dn[d].substr(0,1), "</span></th>");
28163         }
28164         m[m.length] = "</tr></thead><tbody><tr>";
28165         for(var i = 0; i < 42; i++) {
28166             if(i % 7 == 0 && i != 0){
28167                 m[m.length] = "</tr><tr>";
28168             }
28169             m[m.length] = '<td><a href="#" hidefocus="on" class="x-date-date" tabIndex="1"><em><span></span></em></a></td>';
28170         }
28171         m[m.length] = '</tr></tbody></table></td></tr><tr>'+
28172             '<td colspan="3" class="x-date-bottom" align="center"></td></tr></table><div class="x-date-mp"></div>';
28173
28174         var el = document.createElement("div");
28175         el.className = "x-date-picker";
28176         el.innerHTML = m.join("");
28177
28178         container.dom.insertBefore(el, position);
28179
28180         this.el = Roo.get(el);
28181         this.eventEl = Roo.get(el.firstChild);
28182
28183         new Roo.util.ClickRepeater(this.el.child("td.x-date-left a"), {
28184             handler: this.showPrevMonth,
28185             scope: this,
28186             preventDefault:true,
28187             stopDefault:true
28188         });
28189
28190         new Roo.util.ClickRepeater(this.el.child("td.x-date-right a"), {
28191             handler: this.showNextMonth,
28192             scope: this,
28193             preventDefault:true,
28194             stopDefault:true
28195         });
28196
28197         this.eventEl.on("mousewheel", this.handleMouseWheel,  this);
28198
28199         this.monthPicker = this.el.down('div.x-date-mp');
28200         this.monthPicker.enableDisplayMode('block');
28201         
28202         var kn = new Roo.KeyNav(this.eventEl, {
28203             "left" : function(e){
28204                 e.ctrlKey ?
28205                     this.showPrevMonth() :
28206                     this.update(this.activeDate.add("d", -1));
28207             },
28208
28209             "right" : function(e){
28210                 e.ctrlKey ?
28211                     this.showNextMonth() :
28212                     this.update(this.activeDate.add("d", 1));
28213             },
28214
28215             "up" : function(e){
28216                 e.ctrlKey ?
28217                     this.showNextYear() :
28218                     this.update(this.activeDate.add("d", -7));
28219             },
28220
28221             "down" : function(e){
28222                 e.ctrlKey ?
28223                     this.showPrevYear() :
28224                     this.update(this.activeDate.add("d", 7));
28225             },
28226
28227             "pageUp" : function(e){
28228                 this.showNextMonth();
28229             },
28230
28231             "pageDown" : function(e){
28232                 this.showPrevMonth();
28233             },
28234
28235             "enter" : function(e){
28236                 e.stopPropagation();
28237                 return true;
28238             },
28239
28240             scope : this
28241         });
28242
28243         this.eventEl.on("click", this.handleDateClick,  this, {delegate: "a.x-date-date"});
28244
28245         this.eventEl.addKeyListener(Roo.EventObject.SPACE, this.selectToday,  this);
28246
28247         this.el.unselectable();
28248         
28249         this.cells = this.el.select("table.x-date-inner tbody td");
28250         this.textNodes = this.el.query("table.x-date-inner tbody span");
28251
28252         this.mbtn = new Roo.Button(this.el.child("td.x-date-middle", true), {
28253             text: "&#160;",
28254             tooltip: this.monthYearText
28255         });
28256
28257         this.mbtn.on('click', this.showMonthPicker, this);
28258         this.mbtn.el.child(this.mbtn.menuClassTarget).addClass("x-btn-with-menu");
28259
28260
28261         var today = (new Date()).dateFormat(this.format);
28262         
28263         var baseTb = new Roo.Toolbar(this.el.child("td.x-date-bottom", true));
28264         if (this.showClear) {
28265             baseTb.add( new Roo.Toolbar.Fill());
28266         }
28267         baseTb.add({
28268             text: String.format(this.todayText, today),
28269             tooltip: String.format(this.todayTip, today),
28270             handler: this.selectToday,
28271             scope: this
28272         });
28273         
28274         //var todayBtn = new Roo.Button(this.el.child("td.x-date-bottom", true), {
28275             
28276         //});
28277         if (this.showClear) {
28278             
28279             baseTb.add( new Roo.Toolbar.Fill());
28280             baseTb.add({
28281                 text: '&#160;',
28282                 cls: 'x-btn-icon x-btn-clear',
28283                 handler: function() {
28284                     //this.value = '';
28285                     this.fireEvent("select", this, '');
28286                 },
28287                 scope: this
28288             });
28289         }
28290         
28291         
28292         if(Roo.isIE){
28293             this.el.repaint();
28294         }
28295         this.update(this.value);
28296     },
28297
28298     createMonthPicker : function(){
28299         if(!this.monthPicker.dom.firstChild){
28300             var buf = ['<table border="0" cellspacing="0">'];
28301             for(var i = 0; i < 6; i++){
28302                 buf.push(
28303                     '<tr><td class="x-date-mp-month"><a href="#">', this.monthNames[i].substr(0, 3), '</a></td>',
28304                     '<td class="x-date-mp-month x-date-mp-sep"><a href="#">', this.monthNames[i+6].substr(0, 3), '</a></td>',
28305                     i == 0 ?
28306                     '<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>' :
28307                     '<td class="x-date-mp-year"><a href="#"></a></td><td class="x-date-mp-year"><a href="#"></a></td></tr>'
28308                 );
28309             }
28310             buf.push(
28311                 '<tr class="x-date-mp-btns"><td colspan="4"><button type="button" class="x-date-mp-ok">',
28312                     this.okText,
28313                     '</button><button type="button" class="x-date-mp-cancel">',
28314                     this.cancelText,
28315                     '</button></td></tr>',
28316                 '</table>'
28317             );
28318             this.monthPicker.update(buf.join(''));
28319             this.monthPicker.on('click', this.onMonthClick, this);
28320             this.monthPicker.on('dblclick', this.onMonthDblClick, this);
28321
28322             this.mpMonths = this.monthPicker.select('td.x-date-mp-month');
28323             this.mpYears = this.monthPicker.select('td.x-date-mp-year');
28324
28325             this.mpMonths.each(function(m, a, i){
28326                 i += 1;
28327                 if((i%2) == 0){
28328                     m.dom.xmonth = 5 + Math.round(i * .5);
28329                 }else{
28330                     m.dom.xmonth = Math.round((i-1) * .5);
28331                 }
28332             });
28333         }
28334     },
28335
28336     showMonthPicker : function(){
28337         this.createMonthPicker();
28338         var size = this.el.getSize();
28339         this.monthPicker.setSize(size);
28340         this.monthPicker.child('table').setSize(size);
28341
28342         this.mpSelMonth = (this.activeDate || this.value).getMonth();
28343         this.updateMPMonth(this.mpSelMonth);
28344         this.mpSelYear = (this.activeDate || this.value).getFullYear();
28345         this.updateMPYear(this.mpSelYear);
28346
28347         this.monthPicker.slideIn('t', {duration:.2});
28348     },
28349
28350     updateMPYear : function(y){
28351         this.mpyear = y;
28352         var ys = this.mpYears.elements;
28353         for(var i = 1; i <= 10; i++){
28354             var td = ys[i-1], y2;
28355             if((i%2) == 0){
28356                 y2 = y + Math.round(i * .5);
28357                 td.firstChild.innerHTML = y2;
28358                 td.xyear = y2;
28359             }else{
28360                 y2 = y - (5-Math.round(i * .5));
28361                 td.firstChild.innerHTML = y2;
28362                 td.xyear = y2;
28363             }
28364             this.mpYears.item(i-1)[y2 == this.mpSelYear ? 'addClass' : 'removeClass']('x-date-mp-sel');
28365         }
28366     },
28367
28368     updateMPMonth : function(sm){
28369         this.mpMonths.each(function(m, a, i){
28370             m[m.dom.xmonth == sm ? 'addClass' : 'removeClass']('x-date-mp-sel');
28371         });
28372     },
28373
28374     selectMPMonth: function(m){
28375         
28376     },
28377
28378     onMonthClick : function(e, t){
28379         e.stopEvent();
28380         var el = new Roo.Element(t), pn;
28381         if(el.is('button.x-date-mp-cancel')){
28382             this.hideMonthPicker();
28383         }
28384         else if(el.is('button.x-date-mp-ok')){
28385             this.update(new Date(this.mpSelYear, this.mpSelMonth, (this.activeDate || this.value).getDate()));
28386             this.hideMonthPicker();
28387         }
28388         else if(pn = el.up('td.x-date-mp-month', 2)){
28389             this.mpMonths.removeClass('x-date-mp-sel');
28390             pn.addClass('x-date-mp-sel');
28391             this.mpSelMonth = pn.dom.xmonth;
28392         }
28393         else if(pn = el.up('td.x-date-mp-year', 2)){
28394             this.mpYears.removeClass('x-date-mp-sel');
28395             pn.addClass('x-date-mp-sel');
28396             this.mpSelYear = pn.dom.xyear;
28397         }
28398         else if(el.is('a.x-date-mp-prev')){
28399             this.updateMPYear(this.mpyear-10);
28400         }
28401         else if(el.is('a.x-date-mp-next')){
28402             this.updateMPYear(this.mpyear+10);
28403         }
28404     },
28405
28406     onMonthDblClick : function(e, t){
28407         e.stopEvent();
28408         var el = new Roo.Element(t), pn;
28409         if(pn = el.up('td.x-date-mp-month', 2)){
28410             this.update(new Date(this.mpSelYear, pn.dom.xmonth, (this.activeDate || this.value).getDate()));
28411             this.hideMonthPicker();
28412         }
28413         else if(pn = el.up('td.x-date-mp-year', 2)){
28414             this.update(new Date(pn.dom.xyear, this.mpSelMonth, (this.activeDate || this.value).getDate()));
28415             this.hideMonthPicker();
28416         }
28417     },
28418
28419     hideMonthPicker : function(disableAnim){
28420         if(this.monthPicker){
28421             if(disableAnim === true){
28422                 this.monthPicker.hide();
28423             }else{
28424                 this.monthPicker.slideOut('t', {duration:.2});
28425             }
28426         }
28427     },
28428
28429     // private
28430     showPrevMonth : function(e){
28431         this.update(this.activeDate.add("mo", -1));
28432     },
28433
28434     // private
28435     showNextMonth : function(e){
28436         this.update(this.activeDate.add("mo", 1));
28437     },
28438
28439     // private
28440     showPrevYear : function(){
28441         this.update(this.activeDate.add("y", -1));
28442     },
28443
28444     // private
28445     showNextYear : function(){
28446         this.update(this.activeDate.add("y", 1));
28447     },
28448
28449     // private
28450     handleMouseWheel : function(e){
28451         var delta = e.getWheelDelta();
28452         if(delta > 0){
28453             this.showPrevMonth();
28454             e.stopEvent();
28455         } else if(delta < 0){
28456             this.showNextMonth();
28457             e.stopEvent();
28458         }
28459     },
28460
28461     // private
28462     handleDateClick : function(e, t){
28463         e.stopEvent();
28464         if(t.dateValue && !Roo.fly(t.parentNode).hasClass("x-date-disabled")){
28465             this.setValue(new Date(t.dateValue));
28466             this.fireEvent("select", this, this.value);
28467         }
28468     },
28469
28470     // private
28471     selectToday : function(){
28472         this.setValue(new Date().clearTime());
28473         this.fireEvent("select", this, this.value);
28474     },
28475
28476     // private
28477     update : function(date)
28478     {
28479         var vd = this.activeDate;
28480         this.activeDate = date;
28481         if(vd && this.el){
28482             var t = date.getTime();
28483             if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
28484                 this.cells.removeClass("x-date-selected");
28485                 this.cells.each(function(c){
28486                    if(c.dom.firstChild.dateValue == t){
28487                        c.addClass("x-date-selected");
28488                        setTimeout(function(){
28489                             try{c.dom.firstChild.focus();}catch(e){}
28490                        }, 50);
28491                        return false;
28492                    }
28493                 });
28494                 return;
28495             }
28496         }
28497         
28498         var days = date.getDaysInMonth();
28499         var firstOfMonth = date.getFirstDateOfMonth();
28500         var startingPos = firstOfMonth.getDay()-this.startDay;
28501
28502         if(startingPos <= this.startDay){
28503             startingPos += 7;
28504         }
28505
28506         var pm = date.add("mo", -1);
28507         var prevStart = pm.getDaysInMonth()-startingPos;
28508
28509         var cells = this.cells.elements;
28510         var textEls = this.textNodes;
28511         days += startingPos;
28512
28513         // convert everything to numbers so it's fast
28514         var day = 86400000;
28515         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
28516         var today = new Date().clearTime().getTime();
28517         var sel = date.clearTime().getTime();
28518         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
28519         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
28520         var ddMatch = this.disabledDatesRE;
28521         var ddText = this.disabledDatesText;
28522         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
28523         var ddaysText = this.disabledDaysText;
28524         var format = this.format;
28525
28526         var setCellClass = function(cal, cell){
28527             cell.title = "";
28528             var t = d.getTime();
28529             cell.firstChild.dateValue = t;
28530             if(t == today){
28531                 cell.className += " x-date-today";
28532                 cell.title = cal.todayText;
28533             }
28534             if(t == sel){
28535                 cell.className += " x-date-selected";
28536                 setTimeout(function(){
28537                     try{cell.firstChild.focus();}catch(e){}
28538                 }, 50);
28539             }
28540             // disabling
28541             if(t < min) {
28542                 cell.className = " x-date-disabled";
28543                 cell.title = cal.minText;
28544                 return;
28545             }
28546             if(t > max) {
28547                 cell.className = " x-date-disabled";
28548                 cell.title = cal.maxText;
28549                 return;
28550             }
28551             if(ddays){
28552                 if(ddays.indexOf(d.getDay()) != -1){
28553                     cell.title = ddaysText;
28554                     cell.className = " x-date-disabled";
28555                 }
28556             }
28557             if(ddMatch && format){
28558                 var fvalue = d.dateFormat(format);
28559                 if(ddMatch.test(fvalue)){
28560                     cell.title = ddText.replace("%0", fvalue);
28561                     cell.className = " x-date-disabled";
28562                 }
28563             }
28564         };
28565
28566         var i = 0;
28567         for(; i < startingPos; i++) {
28568             textEls[i].innerHTML = (++prevStart);
28569             d.setDate(d.getDate()+1);
28570             cells[i].className = "x-date-prevday";
28571             setCellClass(this, cells[i]);
28572         }
28573         for(; i < days; i++){
28574             intDay = i - startingPos + 1;
28575             textEls[i].innerHTML = (intDay);
28576             d.setDate(d.getDate()+1);
28577             cells[i].className = "x-date-active";
28578             setCellClass(this, cells[i]);
28579         }
28580         var extraDays = 0;
28581         for(; i < 42; i++) {
28582              textEls[i].innerHTML = (++extraDays);
28583              d.setDate(d.getDate()+1);
28584              cells[i].className = "x-date-nextday";
28585              setCellClass(this, cells[i]);
28586         }
28587
28588         this.mbtn.setText(this.monthNames[date.getMonth()] + " " + date.getFullYear());
28589         this.fireEvent('monthchange', this, date);
28590         
28591         if(!this.internalRender){
28592             var main = this.el.dom.firstChild;
28593             var w = main.offsetWidth;
28594             this.el.setWidth(w + this.el.getBorderWidth("lr"));
28595             Roo.fly(main).setWidth(w);
28596             this.internalRender = true;
28597             // opera does not respect the auto grow header center column
28598             // then, after it gets a width opera refuses to recalculate
28599             // without a second pass
28600             if(Roo.isOpera && !this.secondPass){
28601                 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
28602                 this.secondPass = true;
28603                 this.update.defer(10, this, [date]);
28604             }
28605         }
28606         
28607         
28608     }
28609 });        /*
28610  * Based on:
28611  * Ext JS Library 1.1.1
28612  * Copyright(c) 2006-2007, Ext JS, LLC.
28613  *
28614  * Originally Released Under LGPL - original licence link has changed is not relivant.
28615  *
28616  * Fork - LGPL
28617  * <script type="text/javascript">
28618  */
28619 /**
28620  * @class Roo.TabPanel
28621  * @extends Roo.util.Observable
28622  * A lightweight tab container.
28623  * <br><br>
28624  * Usage:
28625  * <pre><code>
28626 // basic tabs 1, built from existing content
28627 var tabs = new Roo.TabPanel("tabs1");
28628 tabs.addTab("script", "View Script");
28629 tabs.addTab("markup", "View Markup");
28630 tabs.activate("script");
28631
28632 // more advanced tabs, built from javascript
28633 var jtabs = new Roo.TabPanel("jtabs");
28634 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
28635
28636 // set up the UpdateManager
28637 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
28638 var updater = tab2.getUpdateManager();
28639 updater.setDefaultUrl("ajax1.htm");
28640 tab2.on('activate', updater.refresh, updater, true);
28641
28642 // Use setUrl for Ajax loading
28643 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
28644 tab3.setUrl("ajax2.htm", null, true);
28645
28646 // Disabled tab
28647 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
28648 tab4.disable();
28649
28650 jtabs.activate("jtabs-1");
28651  * </code></pre>
28652  * @constructor
28653  * Create a new TabPanel.
28654  * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
28655  * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
28656  */
28657 Roo.TabPanel = function(container, config){
28658     /**
28659     * The container element for this TabPanel.
28660     * @type Roo.Element
28661     */
28662     this.el = Roo.get(container, true);
28663     if(config){
28664         if(typeof config == "boolean"){
28665             this.tabPosition = config ? "bottom" : "top";
28666         }else{
28667             Roo.apply(this, config);
28668         }
28669     }
28670     if(this.tabPosition == "bottom"){
28671         this.bodyEl = Roo.get(this.createBody(this.el.dom));
28672         this.el.addClass("x-tabs-bottom");
28673     }
28674     this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
28675     this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
28676     this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
28677     if(Roo.isIE){
28678         Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
28679     }
28680     if(this.tabPosition != "bottom"){
28681         /** The body element that contains {@link Roo.TabPanelItem} bodies. +
28682          * @type Roo.Element
28683          */
28684         this.bodyEl = Roo.get(this.createBody(this.el.dom));
28685         this.el.addClass("x-tabs-top");
28686     }
28687     this.items = [];
28688
28689     this.bodyEl.setStyle("position", "relative");
28690
28691     this.active = null;
28692     this.activateDelegate = this.activate.createDelegate(this);
28693
28694     this.addEvents({
28695         /**
28696          * @event tabchange
28697          * Fires when the active tab changes
28698          * @param {Roo.TabPanel} this
28699          * @param {Roo.TabPanelItem} activePanel The new active tab
28700          */
28701         "tabchange": true,
28702         /**
28703          * @event beforetabchange
28704          * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
28705          * @param {Roo.TabPanel} this
28706          * @param {Object} e Set cancel to true on this object to cancel the tab change
28707          * @param {Roo.TabPanelItem} tab The tab being changed to
28708          */
28709         "beforetabchange" : true
28710     });
28711
28712     Roo.EventManager.onWindowResize(this.onResize, this);
28713     this.cpad = this.el.getPadding("lr");
28714     this.hiddenCount = 0;
28715
28716
28717     // toolbar on the tabbar support...
28718     if (this.toolbar) {
28719         var tcfg = this.toolbar;
28720         tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');  
28721         this.toolbar = new Roo.Toolbar(tcfg);
28722         if (Roo.isSafari) {
28723             var tbl = tcfg.container.child('table', true);
28724             tbl.setAttribute('width', '100%');
28725         }
28726         
28727     }
28728    
28729
28730
28731     Roo.TabPanel.superclass.constructor.call(this);
28732 };
28733
28734 Roo.extend(Roo.TabPanel, Roo.util.Observable, {
28735     /*
28736      *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
28737      */
28738     tabPosition : "top",
28739     /*
28740      *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
28741      */
28742     currentTabWidth : 0,
28743     /*
28744      *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
28745      */
28746     minTabWidth : 40,
28747     /*
28748      *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
28749      */
28750     maxTabWidth : 250,
28751     /*
28752      *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
28753      */
28754     preferredTabWidth : 175,
28755     /*
28756      *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
28757      */
28758     resizeTabs : false,
28759     /*
28760      *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
28761      */
28762     monitorResize : true,
28763     /*
28764      *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar. 
28765      */
28766     toolbar : false,
28767
28768     /**
28769      * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
28770      * @param {String} id The id of the div to use <b>or create</b>
28771      * @param {String} text The text for the tab
28772      * @param {String} content (optional) Content to put in the TabPanelItem body
28773      * @param {Boolean} closable (optional) True to create a close icon on the tab
28774      * @return {Roo.TabPanelItem} The created TabPanelItem
28775      */
28776     addTab : function(id, text, content, closable){
28777         var item = new Roo.TabPanelItem(this, id, text, closable);
28778         this.addTabItem(item);
28779         if(content){
28780             item.setContent(content);
28781         }
28782         return item;
28783     },
28784
28785     /**
28786      * Returns the {@link Roo.TabPanelItem} with the specified id/index
28787      * @param {String/Number} id The id or index of the TabPanelItem to fetch.
28788      * @return {Roo.TabPanelItem}
28789      */
28790     getTab : function(id){
28791         return this.items[id];
28792     },
28793
28794     /**
28795      * Hides the {@link Roo.TabPanelItem} with the specified id/index
28796      * @param {String/Number} id The id or index of the TabPanelItem to hide.
28797      */
28798     hideTab : function(id){
28799         var t = this.items[id];
28800         if(!t.isHidden()){
28801            t.setHidden(true);
28802            this.hiddenCount++;
28803            this.autoSizeTabs();
28804         }
28805     },
28806
28807     /**
28808      * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
28809      * @param {String/Number} id The id or index of the TabPanelItem to unhide.
28810      */
28811     unhideTab : function(id){
28812         var t = this.items[id];
28813         if(t.isHidden()){
28814            t.setHidden(false);
28815            this.hiddenCount--;
28816            this.autoSizeTabs();
28817         }
28818     },
28819
28820     /**
28821      * Adds an existing {@link Roo.TabPanelItem}.
28822      * @param {Roo.TabPanelItem} item The TabPanelItem to add
28823      */
28824     addTabItem : function(item){
28825         this.items[item.id] = item;
28826         this.items.push(item);
28827         if(this.resizeTabs){
28828            item.setWidth(this.currentTabWidth || this.preferredTabWidth);
28829            this.autoSizeTabs();
28830         }else{
28831             item.autoSize();
28832         }
28833     },
28834
28835     /**
28836      * Removes a {@link Roo.TabPanelItem}.
28837      * @param {String/Number} id The id or index of the TabPanelItem to remove.
28838      */
28839     removeTab : function(id){
28840         var items = this.items;
28841         var tab = items[id];
28842         if(!tab) { return; }
28843         var index = items.indexOf(tab);
28844         if(this.active == tab && items.length > 1){
28845             var newTab = this.getNextAvailable(index);
28846             if(newTab) {
28847                 newTab.activate();
28848             }
28849         }
28850         this.stripEl.dom.removeChild(tab.pnode.dom);
28851         if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
28852             this.bodyEl.dom.removeChild(tab.bodyEl.dom);
28853         }
28854         items.splice(index, 1);
28855         delete this.items[tab.id];
28856         tab.fireEvent("close", tab);
28857         tab.purgeListeners();
28858         this.autoSizeTabs();
28859     },
28860
28861     getNextAvailable : function(start){
28862         var items = this.items;
28863         var index = start;
28864         // look for a next tab that will slide over to
28865         // replace the one being removed
28866         while(index < items.length){
28867             var item = items[++index];
28868             if(item && !item.isHidden()){
28869                 return item;
28870             }
28871         }
28872         // if one isn't found select the previous tab (on the left)
28873         index = start;
28874         while(index >= 0){
28875             var item = items[--index];
28876             if(item && !item.isHidden()){
28877                 return item;
28878             }
28879         }
28880         return null;
28881     },
28882
28883     /**
28884      * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
28885      * @param {String/Number} id The id or index of the TabPanelItem to disable.
28886      */
28887     disableTab : function(id){
28888         var tab = this.items[id];
28889         if(tab && this.active != tab){
28890             tab.disable();
28891         }
28892     },
28893
28894     /**
28895      * Enables a {@link Roo.TabPanelItem} that is disabled.
28896      * @param {String/Number} id The id or index of the TabPanelItem to enable.
28897      */
28898     enableTab : function(id){
28899         var tab = this.items[id];
28900         tab.enable();
28901     },
28902
28903     /**
28904      * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
28905      * @param {String/Number} id The id or index of the TabPanelItem to activate.
28906      * @return {Roo.TabPanelItem} The TabPanelItem.
28907      */
28908     activate : function(id){
28909         var tab = this.items[id];
28910         if(!tab){
28911             return null;
28912         }
28913         if(tab == this.active || tab.disabled){
28914             return tab;
28915         }
28916         var e = {};
28917         this.fireEvent("beforetabchange", this, e, tab);
28918         if(e.cancel !== true && !tab.disabled){
28919             if(this.active){
28920                 this.active.hide();
28921             }
28922             this.active = this.items[id];
28923             this.active.show();
28924             this.fireEvent("tabchange", this, this.active);
28925         }
28926         return tab;
28927     },
28928
28929     /**
28930      * Gets the active {@link Roo.TabPanelItem}.
28931      * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
28932      */
28933     getActiveTab : function(){
28934         return this.active;
28935     },
28936
28937     /**
28938      * Updates the tab body element to fit the height of the container element
28939      * for overflow scrolling
28940      * @param {Number} targetHeight (optional) Override the starting height from the elements height
28941      */
28942     syncHeight : function(targetHeight){
28943         var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
28944         var bm = this.bodyEl.getMargins();
28945         var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
28946         this.bodyEl.setHeight(newHeight);
28947         return newHeight;
28948     },
28949
28950     onResize : function(){
28951         if(this.monitorResize){
28952             this.autoSizeTabs();
28953         }
28954     },
28955
28956     /**
28957      * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
28958      */
28959     beginUpdate : function(){
28960         this.updating = true;
28961     },
28962
28963     /**
28964      * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
28965      */
28966     endUpdate : function(){
28967         this.updating = false;
28968         this.autoSizeTabs();
28969     },
28970
28971     /**
28972      * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
28973      */
28974     autoSizeTabs : function(){
28975         var count = this.items.length;
28976         var vcount = count - this.hiddenCount;
28977         if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) {
28978             return;
28979         }
28980         var w = Math.max(this.el.getWidth() - this.cpad, 10);
28981         var availWidth = Math.floor(w / vcount);
28982         var b = this.stripBody;
28983         if(b.getWidth() > w){
28984             var tabs = this.items;
28985             this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
28986             if(availWidth < this.minTabWidth){
28987                 /*if(!this.sleft){    // incomplete scrolling code
28988                     this.createScrollButtons();
28989                 }
28990                 this.showScroll();
28991                 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
28992             }
28993         }else{
28994             if(this.currentTabWidth < this.preferredTabWidth){
28995                 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
28996             }
28997         }
28998     },
28999
29000     /**
29001      * Returns the number of tabs in this TabPanel.
29002      * @return {Number}
29003      */
29004      getCount : function(){
29005          return this.items.length;
29006      },
29007
29008     /**
29009      * Resizes all the tabs to the passed width
29010      * @param {Number} The new width
29011      */
29012     setTabWidth : function(width){
29013         this.currentTabWidth = width;
29014         for(var i = 0, len = this.items.length; i < len; i++) {
29015                 if(!this.items[i].isHidden()) {
29016                 this.items[i].setWidth(width);
29017             }
29018         }
29019     },
29020
29021     /**
29022      * Destroys this TabPanel
29023      * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
29024      */
29025     destroy : function(removeEl){
29026         Roo.EventManager.removeResizeListener(this.onResize, this);
29027         for(var i = 0, len = this.items.length; i < len; i++){
29028             this.items[i].purgeListeners();
29029         }
29030         if(removeEl === true){
29031             this.el.update("");
29032             this.el.remove();
29033         }
29034     }
29035 });
29036
29037 /**
29038  * @class Roo.TabPanelItem
29039  * @extends Roo.util.Observable
29040  * Represents an individual item (tab plus body) in a TabPanel.
29041  * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
29042  * @param {String} id The id of this TabPanelItem
29043  * @param {String} text The text for the tab of this TabPanelItem
29044  * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
29045  */
29046 Roo.TabPanelItem = function(tabPanel, id, text, closable){
29047     /**
29048      * The {@link Roo.TabPanel} this TabPanelItem belongs to
29049      * @type Roo.TabPanel
29050      */
29051     this.tabPanel = tabPanel;
29052     /**
29053      * The id for this TabPanelItem
29054      * @type String
29055      */
29056     this.id = id;
29057     /** @private */
29058     this.disabled = false;
29059     /** @private */
29060     this.text = text;
29061     /** @private */
29062     this.loaded = false;
29063     this.closable = closable;
29064
29065     /**
29066      * The body element for this TabPanelItem.
29067      * @type Roo.Element
29068      */
29069     this.bodyEl = Roo.get(tabPanel.createItemBody(tabPanel.bodyEl.dom, id));
29070     this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
29071     this.bodyEl.setStyle("display", "block");
29072     this.bodyEl.setStyle("zoom", "1");
29073     this.hideAction();
29074
29075     var els = tabPanel.createStripElements(tabPanel.stripEl.dom, text, closable);
29076     /** @private */
29077     this.el = Roo.get(els.el, true);
29078     this.inner = Roo.get(els.inner, true);
29079     this.textEl = Roo.get(this.el.dom.firstChild.firstChild.firstChild, true);
29080     this.pnode = Roo.get(els.el.parentNode, true);
29081     this.el.on("mousedown", this.onTabMouseDown, this);
29082     this.el.on("click", this.onTabClick, this);
29083     /** @private */
29084     if(closable){
29085         var c = Roo.get(els.close, true);
29086         c.dom.title = this.closeText;
29087         c.addClassOnOver("close-over");
29088         c.on("click", this.closeClick, this);
29089      }
29090
29091     this.addEvents({
29092          /**
29093          * @event activate
29094          * Fires when this tab becomes the active tab.
29095          * @param {Roo.TabPanel} tabPanel The parent TabPanel
29096          * @param {Roo.TabPanelItem} this
29097          */
29098         "activate": true,
29099         /**
29100          * @event beforeclose
29101          * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
29102          * @param {Roo.TabPanelItem} this
29103          * @param {Object} e Set cancel to true on this object to cancel the close.
29104          */
29105         "beforeclose": true,
29106         /**
29107          * @event close
29108          * Fires when this tab is closed.
29109          * @param {Roo.TabPanelItem} this
29110          */
29111          "close": true,
29112         /**
29113          * @event deactivate
29114          * Fires when this tab is no longer the active tab.
29115          * @param {Roo.TabPanel} tabPanel The parent TabPanel
29116          * @param {Roo.TabPanelItem} this
29117          */
29118          "deactivate" : true
29119     });
29120     this.hidden = false;
29121
29122     Roo.TabPanelItem.superclass.constructor.call(this);
29123 };
29124
29125 Roo.extend(Roo.TabPanelItem, Roo.util.Observable, {
29126     purgeListeners : function(){
29127        Roo.util.Observable.prototype.purgeListeners.call(this);
29128        this.el.removeAllListeners();
29129     },
29130     /**
29131      * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
29132      */
29133     show : function(){
29134         this.pnode.addClass("on");
29135         this.showAction();
29136         if(Roo.isOpera){
29137             this.tabPanel.stripWrap.repaint();
29138         }
29139         this.fireEvent("activate", this.tabPanel, this);
29140     },
29141
29142     /**
29143      * Returns true if this tab is the active tab.
29144      * @return {Boolean}
29145      */
29146     isActive : function(){
29147         return this.tabPanel.getActiveTab() == this;
29148     },
29149
29150     /**
29151      * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
29152      */
29153     hide : function(){
29154         this.pnode.removeClass("on");
29155         this.hideAction();
29156         this.fireEvent("deactivate", this.tabPanel, this);
29157     },
29158
29159     hideAction : function(){
29160         this.bodyEl.hide();
29161         this.bodyEl.setStyle("position", "absolute");
29162         this.bodyEl.setLeft("-20000px");
29163         this.bodyEl.setTop("-20000px");
29164     },
29165
29166     showAction : function(){
29167         this.bodyEl.setStyle("position", "relative");
29168         this.bodyEl.setTop("");
29169         this.bodyEl.setLeft("");
29170         this.bodyEl.show();
29171     },
29172
29173     /**
29174      * Set the tooltip for the tab.
29175      * @param {String} tooltip The tab's tooltip
29176      */
29177     setTooltip : function(text){
29178         if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
29179             this.textEl.dom.qtip = text;
29180             this.textEl.dom.removeAttribute('title');
29181         }else{
29182             this.textEl.dom.title = text;
29183         }
29184     },
29185
29186     onTabClick : function(e){
29187         e.preventDefault();
29188         this.tabPanel.activate(this.id);
29189     },
29190
29191     onTabMouseDown : function(e){
29192         e.preventDefault();
29193         this.tabPanel.activate(this.id);
29194     },
29195
29196     getWidth : function(){
29197         return this.inner.getWidth();
29198     },
29199
29200     setWidth : function(width){
29201         var iwidth = width - this.pnode.getPadding("lr");
29202         this.inner.setWidth(iwidth);
29203         this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
29204         this.pnode.setWidth(width);
29205     },
29206
29207     /**
29208      * Show or hide the tab
29209      * @param {Boolean} hidden True to hide or false to show.
29210      */
29211     setHidden : function(hidden){
29212         this.hidden = hidden;
29213         this.pnode.setStyle("display", hidden ? "none" : "");
29214     },
29215
29216     /**
29217      * Returns true if this tab is "hidden"
29218      * @return {Boolean}
29219      */
29220     isHidden : function(){
29221         return this.hidden;
29222     },
29223
29224     /**
29225      * Returns the text for this tab
29226      * @return {String}
29227      */
29228     getText : function(){
29229         return this.text;
29230     },
29231
29232     autoSize : function(){
29233         //this.el.beginMeasure();
29234         this.textEl.setWidth(1);
29235         /*
29236          *  #2804 [new] Tabs in Roojs
29237          *  increase the width by 2-4 pixels to prevent the ellipssis showing in chrome
29238          */
29239         this.setWidth(this.textEl.dom.scrollWidth+this.pnode.getPadding("lr")+this.inner.getPadding("lr") + 2);
29240         //this.el.endMeasure();
29241     },
29242
29243     /**
29244      * Sets the text for the tab (Note: this also sets the tooltip text)
29245      * @param {String} text The tab's text and tooltip
29246      */
29247     setText : function(text){
29248         this.text = text;
29249         this.textEl.update(text);
29250         this.setTooltip(text);
29251         if(!this.tabPanel.resizeTabs){
29252             this.autoSize();
29253         }
29254     },
29255     /**
29256      * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
29257      */
29258     activate : function(){
29259         this.tabPanel.activate(this.id);
29260     },
29261
29262     /**
29263      * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
29264      */
29265     disable : function(){
29266         if(this.tabPanel.active != this){
29267             this.disabled = true;
29268             this.pnode.addClass("disabled");
29269         }
29270     },
29271
29272     /**
29273      * Enables this TabPanelItem if it was previously disabled.
29274      */
29275     enable : function(){
29276         this.disabled = false;
29277         this.pnode.removeClass("disabled");
29278     },
29279
29280     /**
29281      * Sets the content for this TabPanelItem.
29282      * @param {String} content The content
29283      * @param {Boolean} loadScripts true to look for and load scripts
29284      */
29285     setContent : function(content, loadScripts){
29286         this.bodyEl.update(content, loadScripts);
29287     },
29288
29289     /**
29290      * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
29291      * @return {Roo.UpdateManager} The UpdateManager
29292      */
29293     getUpdateManager : function(){
29294         return this.bodyEl.getUpdateManager();
29295     },
29296
29297     /**
29298      * Set a URL to be used to load the content for this TabPanelItem.
29299      * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
29300      * @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)
29301      * @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)
29302      * @return {Roo.UpdateManager} The UpdateManager
29303      */
29304     setUrl : function(url, params, loadOnce){
29305         if(this.refreshDelegate){
29306             this.un('activate', this.refreshDelegate);
29307         }
29308         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
29309         this.on("activate", this.refreshDelegate);
29310         return this.bodyEl.getUpdateManager();
29311     },
29312
29313     /** @private */
29314     _handleRefresh : function(url, params, loadOnce){
29315         if(!loadOnce || !this.loaded){
29316             var updater = this.bodyEl.getUpdateManager();
29317             updater.update(url, params, this._setLoaded.createDelegate(this));
29318         }
29319     },
29320
29321     /**
29322      *   Forces a content refresh from the URL specified in the {@link #setUrl} method.
29323      *   Will fail silently if the setUrl method has not been called.
29324      *   This does not activate the panel, just updates its content.
29325      */
29326     refresh : function(){
29327         if(this.refreshDelegate){
29328            this.loaded = false;
29329            this.refreshDelegate();
29330         }
29331     },
29332
29333     /** @private */
29334     _setLoaded : function(){
29335         this.loaded = true;
29336     },
29337
29338     /** @private */
29339     closeClick : function(e){
29340         var o = {};
29341         e.stopEvent();
29342         this.fireEvent("beforeclose", this, o);
29343         if(o.cancel !== true){
29344             this.tabPanel.removeTab(this.id);
29345         }
29346     },
29347     /**
29348      * The text displayed in the tooltip for the close icon.
29349      * @type String
29350      */
29351     closeText : "Close this tab"
29352 });
29353
29354 /** @private */
29355 Roo.TabPanel.prototype.createStrip = function(container){
29356     var strip = document.createElement("div");
29357     strip.className = "x-tabs-wrap";
29358     container.appendChild(strip);
29359     return strip;
29360 };
29361 /** @private */
29362 Roo.TabPanel.prototype.createStripList = function(strip){
29363     // div wrapper for retard IE
29364     // returns the "tr" element.
29365     strip.innerHTML = '<div class="x-tabs-strip-wrap">'+
29366         '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
29367         '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
29368     return strip.firstChild.firstChild.firstChild.firstChild;
29369 };
29370 /** @private */
29371 Roo.TabPanel.prototype.createBody = function(container){
29372     var body = document.createElement("div");
29373     Roo.id(body, "tab-body");
29374     Roo.fly(body).addClass("x-tabs-body");
29375     container.appendChild(body);
29376     return body;
29377 };
29378 /** @private */
29379 Roo.TabPanel.prototype.createItemBody = function(bodyEl, id){
29380     var body = Roo.getDom(id);
29381     if(!body){
29382         body = document.createElement("div");
29383         body.id = id;
29384     }
29385     Roo.fly(body).addClass("x-tabs-item-body");
29386     bodyEl.insertBefore(body, bodyEl.firstChild);
29387     return body;
29388 };
29389 /** @private */
29390 Roo.TabPanel.prototype.createStripElements = function(stripEl, text, closable){
29391     var td = document.createElement("td");
29392     stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
29393     //stripEl.appendChild(td);
29394     if(closable){
29395         td.className = "x-tabs-closable";
29396         if(!this.closeTpl){
29397             this.closeTpl = new Roo.Template(
29398                '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
29399                '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
29400                '<div unselectable="on" class="close-icon">&#160;</div></em></span></a>'
29401             );
29402         }
29403         var el = this.closeTpl.overwrite(td, {"text": text});
29404         var close = el.getElementsByTagName("div")[0];
29405         var inner = el.getElementsByTagName("em")[0];
29406         return {"el": el, "close": close, "inner": inner};
29407     } else {
29408         if(!this.tabTpl){
29409             this.tabTpl = new Roo.Template(
29410                '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
29411                '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
29412             );
29413         }
29414         var el = this.tabTpl.overwrite(td, {"text": text});
29415         var inner = el.getElementsByTagName("em")[0];
29416         return {"el": el, "inner": inner};
29417     }
29418 };/*
29419  * Based on:
29420  * Ext JS Library 1.1.1
29421  * Copyright(c) 2006-2007, Ext JS, LLC.
29422  *
29423  * Originally Released Under LGPL - original licence link has changed is not relivant.
29424  *
29425  * Fork - LGPL
29426  * <script type="text/javascript">
29427  */
29428
29429 /**
29430  * @class Roo.Button
29431  * @extends Roo.util.Observable
29432  * Simple Button class
29433  * @cfg {String} text The button text
29434  * @cfg {String} icon The path to an image to display in the button (the image will be set as the background-image
29435  * CSS property of the button by default, so if you want a mixed icon/text button, set cls:"x-btn-text-icon")
29436  * @cfg {Function} handler A function called when the button is clicked (can be used instead of click event)
29437  * @cfg {Object} scope The scope of the handler
29438  * @cfg {Number} minWidth The minimum width for this button (used to give a set of buttons a common width)
29439  * @cfg {String/Object} tooltip The tooltip for the button - can be a string or QuickTips config object
29440  * @cfg {Boolean} hidden True to start hidden (defaults to false)
29441  * @cfg {Boolean} disabled True to start disabled (defaults to false)
29442  * @cfg {Boolean} pressed True to start pressed (only if enableToggle = true)
29443  * @cfg {String} toggleGroup The group this toggle button is a member of (only 1 per group can be pressed, only
29444    applies if enableToggle = true)
29445  * @cfg {String/HTMLElement/Element} renderTo The element to append the button to
29446  * @cfg {Boolean/Object} repeat True to repeat fire the click event while the mouse is down. This can also be
29447   an {@link Roo.util.ClickRepeater} config object (defaults to false).
29448  * @constructor
29449  * Create a new button
29450  * @param {Object} config The config object
29451  */
29452 Roo.Button = function(renderTo, config)
29453 {
29454     if (!config) {
29455         config = renderTo;
29456         renderTo = config.renderTo || false;
29457     }
29458     
29459     Roo.apply(this, config);
29460     this.addEvents({
29461         /**
29462              * @event click
29463              * Fires when this button is clicked
29464              * @param {Button} this
29465              * @param {EventObject} e The click event
29466              */
29467             "click" : true,
29468         /**
29469              * @event toggle
29470              * Fires when the "pressed" state of this button changes (only if enableToggle = true)
29471              * @param {Button} this
29472              * @param {Boolean} pressed
29473              */
29474             "toggle" : true,
29475         /**
29476              * @event mouseover
29477              * Fires when the mouse hovers over the button
29478              * @param {Button} this
29479              * @param {Event} e The event object
29480              */
29481         'mouseover' : true,
29482         /**
29483              * @event mouseout
29484              * Fires when the mouse exits the button
29485              * @param {Button} this
29486              * @param {Event} e The event object
29487              */
29488         'mouseout': true,
29489          /**
29490              * @event render
29491              * Fires when the button is rendered
29492              * @param {Button} this
29493              */
29494         'render': true
29495     });
29496     if(this.menu){
29497         this.menu = Roo.menu.MenuMgr.get(this.menu);
29498     }
29499     // register listeners first!!  - so render can be captured..
29500     Roo.util.Observable.call(this);
29501     if(renderTo){
29502         this.render(renderTo);
29503     }
29504     
29505   
29506 };
29507
29508 Roo.extend(Roo.Button, Roo.util.Observable, {
29509     /**
29510      * 
29511      */
29512     
29513     /**
29514      * Read-only. True if this button is hidden
29515      * @type Boolean
29516      */
29517     hidden : false,
29518     /**
29519      * Read-only. True if this button is disabled
29520      * @type Boolean
29521      */
29522     disabled : false,
29523     /**
29524      * Read-only. True if this button is pressed (only if enableToggle = true)
29525      * @type Boolean
29526      */
29527     pressed : false,
29528
29529     /**
29530      * @cfg {Number} tabIndex 
29531      * The DOM tabIndex for this button (defaults to undefined)
29532      */
29533     tabIndex : undefined,
29534
29535     /**
29536      * @cfg {Boolean} enableToggle
29537      * True to enable pressed/not pressed toggling (defaults to false)
29538      */
29539     enableToggle: false,
29540     /**
29541      * @cfg {Roo.menu.Menu} menu
29542      * Standard menu attribute consisting of a reference to a menu object, a menu id or a menu config blob (defaults to undefined).
29543      */
29544     menu : undefined,
29545     /**
29546      * @cfg {String} menuAlign
29547      * The position to align the menu to (see {@link Roo.Element#alignTo} for more details, defaults to 'tl-bl?').
29548      */
29549     menuAlign : "tl-bl?",
29550
29551     /**
29552      * @cfg {String} iconCls
29553      * A css class which sets a background image to be used as the icon for this button (defaults to undefined).
29554      */
29555     iconCls : undefined,
29556     /**
29557      * @cfg {String} type
29558      * The button's type, corresponding to the DOM input element type attribute.  Either "submit," "reset" or "button" (default).
29559      */
29560     type : 'button',
29561
29562     // private
29563     menuClassTarget: 'tr',
29564
29565     /**
29566      * @cfg {String} clickEvent
29567      * The type of event to map to the button's event handler (defaults to 'click')
29568      */
29569     clickEvent : 'click',
29570
29571     /**
29572      * @cfg {Boolean} handleMouseEvents
29573      * False to disable visual cues on mouseover, mouseout and mousedown (defaults to true)
29574      */
29575     handleMouseEvents : true,
29576
29577     /**
29578      * @cfg {String} tooltipType
29579      * The type of tooltip to use. Either "qtip" (default) for QuickTips or "title" for title attribute.
29580      */
29581     tooltipType : 'qtip',
29582
29583     /**
29584      * @cfg {String} cls
29585      * A CSS class to apply to the button's main element.
29586      */
29587     
29588     /**
29589      * @cfg {Roo.Template} template (Optional)
29590      * An {@link Roo.Template} with which to create the Button's main element. This Template must
29591      * contain numeric substitution parameter 0 if it is to display the tRoo property. Changing the template could
29592      * require code modifications if required elements (e.g. a button) aren't present.
29593      */
29594
29595     // private
29596     render : function(renderTo){
29597         var btn;
29598         if(this.hideParent){
29599             this.parentEl = Roo.get(renderTo);
29600         }
29601         if(!this.dhconfig){
29602             if(!this.template){
29603                 if(!Roo.Button.buttonTemplate){
29604                     // hideous table template
29605                     Roo.Button.buttonTemplate = new Roo.Template(
29606                         '<table border="0" cellpadding="0" cellspacing="0" class="x-btn-wrap"><tbody><tr>',
29607                         '<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>',
29608                         "</tr></tbody></table>");
29609                 }
29610                 this.template = Roo.Button.buttonTemplate;
29611             }
29612             btn = this.template.append(renderTo, [this.text || '&#160;', this.type], true);
29613             var btnEl = btn.child("button:first");
29614             btnEl.on('focus', this.onFocus, this);
29615             btnEl.on('blur', this.onBlur, this);
29616             if(this.cls){
29617                 btn.addClass(this.cls);
29618             }
29619             if(this.icon){
29620                 btnEl.setStyle('background-image', 'url(' +this.icon +')');
29621             }
29622             if(this.iconCls){
29623                 btnEl.addClass(this.iconCls);
29624                 if(!this.cls){
29625                     btn.addClass(this.text ? 'x-btn-text-icon' : 'x-btn-icon');
29626                 }
29627             }
29628             if(this.tabIndex !== undefined){
29629                 btnEl.dom.tabIndex = this.tabIndex;
29630             }
29631             if(this.tooltip){
29632                 if(typeof this.tooltip == 'object'){
29633                     Roo.QuickTips.tips(Roo.apply({
29634                           target: btnEl.id
29635                     }, this.tooltip));
29636                 } else {
29637                     btnEl.dom[this.tooltipType] = this.tooltip;
29638                 }
29639             }
29640         }else{
29641             btn = Roo.DomHelper.append(Roo.get(renderTo).dom, this.dhconfig, true);
29642         }
29643         this.el = btn;
29644         if(this.id){
29645             this.el.dom.id = this.el.id = this.id;
29646         }
29647         if(this.menu){
29648             this.el.child(this.menuClassTarget).addClass("x-btn-with-menu");
29649             this.menu.on("show", this.onMenuShow, this);
29650             this.menu.on("hide", this.onMenuHide, this);
29651         }
29652         btn.addClass("x-btn");
29653         if(Roo.isIE && !Roo.isIE7){
29654             this.autoWidth.defer(1, this);
29655         }else{
29656             this.autoWidth();
29657         }
29658         if(this.handleMouseEvents){
29659             btn.on("mouseover", this.onMouseOver, this);
29660             btn.on("mouseout", this.onMouseOut, this);
29661             btn.on("mousedown", this.onMouseDown, this);
29662         }
29663         btn.on(this.clickEvent, this.onClick, this);
29664         //btn.on("mouseup", this.onMouseUp, this);
29665         if(this.hidden){
29666             this.hide();
29667         }
29668         if(this.disabled){
29669             this.disable();
29670         }
29671         Roo.ButtonToggleMgr.register(this);
29672         if(this.pressed){
29673             this.el.addClass("x-btn-pressed");
29674         }
29675         if(this.repeat){
29676             var repeater = new Roo.util.ClickRepeater(btn,
29677                 typeof this.repeat == "object" ? this.repeat : {}
29678             );
29679             repeater.on("click", this.onClick,  this);
29680         }
29681         
29682         this.fireEvent('render', this);
29683         
29684     },
29685     /**
29686      * Returns the button's underlying element
29687      * @return {Roo.Element} The element
29688      */
29689     getEl : function(){
29690         return this.el;  
29691     },
29692     
29693     /**
29694      * Destroys this Button and removes any listeners.
29695      */
29696     destroy : function(){
29697         Roo.ButtonToggleMgr.unregister(this);
29698         this.el.removeAllListeners();
29699         this.purgeListeners();
29700         this.el.remove();
29701     },
29702
29703     // private
29704     autoWidth : function(){
29705         if(this.el){
29706             this.el.setWidth("auto");
29707             if(Roo.isIE7 && Roo.isStrict){
29708                 var ib = this.el.child('button');
29709                 if(ib && ib.getWidth() > 20){
29710                     ib.clip();
29711                     ib.setWidth(Roo.util.TextMetrics.measure(ib, this.text).width+ib.getFrameWidth('lr'));
29712                 }
29713             }
29714             if(this.minWidth){
29715                 if(this.hidden){
29716                     this.el.beginMeasure();
29717                 }
29718                 if(this.el.getWidth() < this.minWidth){
29719                     this.el.setWidth(this.minWidth);
29720                 }
29721                 if(this.hidden){
29722                     this.el.endMeasure();
29723                 }
29724             }
29725         }
29726     },
29727
29728     /**
29729      * Assigns this button's click handler
29730      * @param {Function} handler The function to call when the button is clicked
29731      * @param {Object} scope (optional) Scope for the function passed in
29732      */
29733     setHandler : function(handler, scope){
29734         this.handler = handler;
29735         this.scope = scope;  
29736     },
29737     
29738     /**
29739      * Sets this button's text
29740      * @param {String} text The button text
29741      */
29742     setText : function(text){
29743         this.text = text;
29744         if(this.el){
29745             this.el.child("td.x-btn-center button.x-btn-text").update(text);
29746         }
29747         this.autoWidth();
29748     },
29749     
29750     /**
29751      * Gets the text for this button
29752      * @return {String} The button text
29753      */
29754     getText : function(){
29755         return this.text;  
29756     },
29757     
29758     /**
29759      * Show this button
29760      */
29761     show: function(){
29762         this.hidden = false;
29763         if(this.el){
29764             this[this.hideParent? 'parentEl' : 'el'].setStyle("display", "");
29765         }
29766     },
29767     
29768     /**
29769      * Hide this button
29770      */
29771     hide: function(){
29772         this.hidden = true;
29773         if(this.el){
29774             this[this.hideParent? 'parentEl' : 'el'].setStyle("display", "none");
29775         }
29776     },
29777     
29778     /**
29779      * Convenience function for boolean show/hide
29780      * @param {Boolean} visible True to show, false to hide
29781      */
29782     setVisible: function(visible){
29783         if(visible) {
29784             this.show();
29785         }else{
29786             this.hide();
29787         }
29788     },
29789     
29790     /**
29791      * If a state it passed, it becomes the pressed state otherwise the current state is toggled.
29792      * @param {Boolean} state (optional) Force a particular state
29793      */
29794     toggle : function(state){
29795         state = state === undefined ? !this.pressed : state;
29796         if(state != this.pressed){
29797             if(state){
29798                 this.el.addClass("x-btn-pressed");
29799                 this.pressed = true;
29800                 this.fireEvent("toggle", this, true);
29801             }else{
29802                 this.el.removeClass("x-btn-pressed");
29803                 this.pressed = false;
29804                 this.fireEvent("toggle", this, false);
29805             }
29806             if(this.toggleHandler){
29807                 this.toggleHandler.call(this.scope || this, this, state);
29808             }
29809         }
29810     },
29811     
29812     /**
29813      * Focus the button
29814      */
29815     focus : function(){
29816         this.el.child('button:first').focus();
29817     },
29818     
29819     /**
29820      * Disable this button
29821      */
29822     disable : function(){
29823         if(this.el){
29824             this.el.addClass("x-btn-disabled");
29825         }
29826         this.disabled = true;
29827     },
29828     
29829     /**
29830      * Enable this button
29831      */
29832     enable : function(){
29833         if(this.el){
29834             this.el.removeClass("x-btn-disabled");
29835         }
29836         this.disabled = false;
29837     },
29838
29839     /**
29840      * Convenience function for boolean enable/disable
29841      * @param {Boolean} enabled True to enable, false to disable
29842      */
29843     setDisabled : function(v){
29844         this[v !== true ? "enable" : "disable"]();
29845     },
29846
29847     // private
29848     onClick : function(e)
29849     {
29850         if(e){
29851             e.preventDefault();
29852         }
29853         if(e.button != 0){
29854             return;
29855         }
29856         if(!this.disabled){
29857             if(this.enableToggle){
29858                 this.toggle();
29859             }
29860             if(this.menu && !this.menu.isVisible()){
29861                 this.menu.show(this.el, this.menuAlign);
29862             }
29863             this.fireEvent("click", this, e);
29864             if(this.handler){
29865                 this.el.removeClass("x-btn-over");
29866                 this.handler.call(this.scope || this, this, e);
29867             }
29868         }
29869     },
29870     // private
29871     onMouseOver : function(e){
29872         if(!this.disabled){
29873             this.el.addClass("x-btn-over");
29874             this.fireEvent('mouseover', this, e);
29875         }
29876     },
29877     // private
29878     onMouseOut : function(e){
29879         if(!e.within(this.el,  true)){
29880             this.el.removeClass("x-btn-over");
29881             this.fireEvent('mouseout', this, e);
29882         }
29883     },
29884     // private
29885     onFocus : function(e){
29886         if(!this.disabled){
29887             this.el.addClass("x-btn-focus");
29888         }
29889     },
29890     // private
29891     onBlur : function(e){
29892         this.el.removeClass("x-btn-focus");
29893     },
29894     // private
29895     onMouseDown : function(e){
29896         if(!this.disabled && e.button == 0){
29897             this.el.addClass("x-btn-click");
29898             Roo.get(document).on('mouseup', this.onMouseUp, this);
29899         }
29900     },
29901     // private
29902     onMouseUp : function(e){
29903         if(e.button == 0){
29904             this.el.removeClass("x-btn-click");
29905             Roo.get(document).un('mouseup', this.onMouseUp, this);
29906         }
29907     },
29908     // private
29909     onMenuShow : function(e){
29910         this.el.addClass("x-btn-menu-active");
29911     },
29912     // private
29913     onMenuHide : function(e){
29914         this.el.removeClass("x-btn-menu-active");
29915     }   
29916 });
29917
29918 // Private utility class used by Button
29919 Roo.ButtonToggleMgr = function(){
29920    var groups = {};
29921    
29922    function toggleGroup(btn, state){
29923        if(state){
29924            var g = groups[btn.toggleGroup];
29925            for(var i = 0, l = g.length; i < l; i++){
29926                if(g[i] != btn){
29927                    g[i].toggle(false);
29928                }
29929            }
29930        }
29931    }
29932    
29933    return {
29934        register : function(btn){
29935            if(!btn.toggleGroup){
29936                return;
29937            }
29938            var g = groups[btn.toggleGroup];
29939            if(!g){
29940                g = groups[btn.toggleGroup] = [];
29941            }
29942            g.push(btn);
29943            btn.on("toggle", toggleGroup);
29944        },
29945        
29946        unregister : function(btn){
29947            if(!btn.toggleGroup){
29948                return;
29949            }
29950            var g = groups[btn.toggleGroup];
29951            if(g){
29952                g.remove(btn);
29953                btn.un("toggle", toggleGroup);
29954            }
29955        }
29956    };
29957 }();/*
29958  * Based on:
29959  * Ext JS Library 1.1.1
29960  * Copyright(c) 2006-2007, Ext JS, LLC.
29961  *
29962  * Originally Released Under LGPL - original licence link has changed is not relivant.
29963  *
29964  * Fork - LGPL
29965  * <script type="text/javascript">
29966  */
29967  
29968 /**
29969  * @class Roo.SplitButton
29970  * @extends Roo.Button
29971  * A split button that provides a built-in dropdown arrow that can fire an event separately from the default
29972  * click event of the button.  Typically this would be used to display a dropdown menu that provides additional
29973  * options to the primary button action, but any custom handler can provide the arrowclick implementation.
29974  * @cfg {Function} arrowHandler A function called when the arrow button is clicked (can be used instead of click event)
29975  * @cfg {String} arrowTooltip The title attribute of the arrow
29976  * @constructor
29977  * Create a new menu button
29978  * @param {String/HTMLElement/Element} renderTo The element to append the button to
29979  * @param {Object} config The config object
29980  */
29981 Roo.SplitButton = function(renderTo, config){
29982     Roo.SplitButton.superclass.constructor.call(this, renderTo, config);
29983     /**
29984      * @event arrowclick
29985      * Fires when this button's arrow is clicked
29986      * @param {SplitButton} this
29987      * @param {EventObject} e The click event
29988      */
29989     this.addEvents({"arrowclick":true});
29990 };
29991
29992 Roo.extend(Roo.SplitButton, Roo.Button, {
29993     render : function(renderTo){
29994         // this is one sweet looking template!
29995         var tpl = new Roo.Template(
29996             '<table cellspacing="0" class="x-btn-menu-wrap x-btn"><tr><td>',
29997             '<table cellspacing="0" class="x-btn-wrap x-btn-menu-text-wrap"><tbody>',
29998             '<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>',
29999             "</tbody></table></td><td>",
30000             '<table cellspacing="0" class="x-btn-wrap x-btn-menu-arrow-wrap"><tbody>',
30001             '<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>',
30002             "</tbody></table></td></tr></table>"
30003         );
30004         var btn = tpl.append(renderTo, [this.text, this.type], true);
30005         var btnEl = btn.child("button");
30006         if(this.cls){
30007             btn.addClass(this.cls);
30008         }
30009         if(this.icon){
30010             btnEl.setStyle('background-image', 'url(' +this.icon +')');
30011         }
30012         if(this.iconCls){
30013             btnEl.addClass(this.iconCls);
30014             if(!this.cls){
30015                 btn.addClass(this.text ? 'x-btn-text-icon' : 'x-btn-icon');
30016             }
30017         }
30018         this.el = btn;
30019         if(this.handleMouseEvents){
30020             btn.on("mouseover", this.onMouseOver, this);
30021             btn.on("mouseout", this.onMouseOut, this);
30022             btn.on("mousedown", this.onMouseDown, this);
30023             btn.on("mouseup", this.onMouseUp, this);
30024         }
30025         btn.on(this.clickEvent, this.onClick, this);
30026         if(this.tooltip){
30027             if(typeof this.tooltip == 'object'){
30028                 Roo.QuickTips.tips(Roo.apply({
30029                       target: btnEl.id
30030                 }, this.tooltip));
30031             } else {
30032                 btnEl.dom[this.tooltipType] = this.tooltip;
30033             }
30034         }
30035         if(this.arrowTooltip){
30036             btn.child("button:nth(2)").dom[this.tooltipType] = this.arrowTooltip;
30037         }
30038         if(this.hidden){
30039             this.hide();
30040         }
30041         if(this.disabled){
30042             this.disable();
30043         }
30044         if(this.pressed){
30045             this.el.addClass("x-btn-pressed");
30046         }
30047         if(Roo.isIE && !Roo.isIE7){
30048             this.autoWidth.defer(1, this);
30049         }else{
30050             this.autoWidth();
30051         }
30052         if(this.menu){
30053             this.menu.on("show", this.onMenuShow, this);
30054             this.menu.on("hide", this.onMenuHide, this);
30055         }
30056         this.fireEvent('render', this);
30057     },
30058
30059     // private
30060     autoWidth : function(){
30061         if(this.el){
30062             var tbl = this.el.child("table:first");
30063             var tbl2 = this.el.child("table:last");
30064             this.el.setWidth("auto");
30065             tbl.setWidth("auto");
30066             if(Roo.isIE7 && Roo.isStrict){
30067                 var ib = this.el.child('button:first');
30068                 if(ib && ib.getWidth() > 20){
30069                     ib.clip();
30070                     ib.setWidth(Roo.util.TextMetrics.measure(ib, this.text).width+ib.getFrameWidth('lr'));
30071                 }
30072             }
30073             if(this.minWidth){
30074                 if(this.hidden){
30075                     this.el.beginMeasure();
30076                 }
30077                 if((tbl.getWidth()+tbl2.getWidth()) < this.minWidth){
30078                     tbl.setWidth(this.minWidth-tbl2.getWidth());
30079                 }
30080                 if(this.hidden){
30081                     this.el.endMeasure();
30082                 }
30083             }
30084             this.el.setWidth(tbl.getWidth()+tbl2.getWidth());
30085         } 
30086     },
30087     /**
30088      * Sets this button's click handler
30089      * @param {Function} handler The function to call when the button is clicked
30090      * @param {Object} scope (optional) Scope for the function passed above
30091      */
30092     setHandler : function(handler, scope){
30093         this.handler = handler;
30094         this.scope = scope;  
30095     },
30096     
30097     /**
30098      * Sets this button's arrow click handler
30099      * @param {Function} handler The function to call when the arrow is clicked
30100      * @param {Object} scope (optional) Scope for the function passed above
30101      */
30102     setArrowHandler : function(handler, scope){
30103         this.arrowHandler = handler;
30104         this.scope = scope;  
30105     },
30106     
30107     /**
30108      * Focus the button
30109      */
30110     focus : function(){
30111         if(this.el){
30112             this.el.child("button:first").focus();
30113         }
30114     },
30115
30116     // private
30117     onClick : function(e){
30118         e.preventDefault();
30119         if(!this.disabled){
30120             if(e.getTarget(".x-btn-menu-arrow-wrap")){
30121                 if(this.menu && !this.menu.isVisible()){
30122                     this.menu.show(this.el, this.menuAlign);
30123                 }
30124                 this.fireEvent("arrowclick", this, e);
30125                 if(this.arrowHandler){
30126                     this.arrowHandler.call(this.scope || this, this, e);
30127                 }
30128             }else{
30129                 this.fireEvent("click", this, e);
30130                 if(this.handler){
30131                     this.handler.call(this.scope || this, this, e);
30132                 }
30133             }
30134         }
30135     },
30136     // private
30137     onMouseDown : function(e){
30138         if(!this.disabled){
30139             Roo.fly(e.getTarget("table")).addClass("x-btn-click");
30140         }
30141     },
30142     // private
30143     onMouseUp : function(e){
30144         Roo.fly(e.getTarget("table")).removeClass("x-btn-click");
30145     }   
30146 });
30147
30148
30149 // backwards compat
30150 Roo.MenuButton = Roo.SplitButton;/*
30151  * Based on:
30152  * Ext JS Library 1.1.1
30153  * Copyright(c) 2006-2007, Ext JS, LLC.
30154  *
30155  * Originally Released Under LGPL - original licence link has changed is not relivant.
30156  *
30157  * Fork - LGPL
30158  * <script type="text/javascript">
30159  */
30160
30161 /**
30162  * @class Roo.Toolbar
30163  * @children   Roo.Toolbar.Item Roo.form.Field
30164  * Basic Toolbar class.
30165  * @constructor
30166  * Creates a new Toolbar
30167  * @param {Object} container The config object
30168  */ 
30169 Roo.Toolbar = function(container, buttons, config)
30170 {
30171     /// old consturctor format still supported..
30172     if(container instanceof Array){ // omit the container for later rendering
30173         buttons = container;
30174         config = buttons;
30175         container = null;
30176     }
30177     if (typeof(container) == 'object' && container.xtype) {
30178         config = container;
30179         container = config.container;
30180         buttons = config.buttons || []; // not really - use items!!
30181     }
30182     var xitems = [];
30183     if (config && config.items) {
30184         xitems = config.items;
30185         delete config.items;
30186     }
30187     Roo.apply(this, config);
30188     this.buttons = buttons;
30189     
30190     if(container){
30191         this.render(container);
30192     }
30193     this.xitems = xitems;
30194     Roo.each(xitems, function(b) {
30195         this.add(b);
30196     }, this);
30197     
30198 };
30199
30200 Roo.Toolbar.prototype = {
30201     /**
30202      * @cfg {Array} items
30203      * array of button configs or elements to add (will be converted to a MixedCollection)
30204      */
30205     items: false,
30206     /**
30207      * @cfg {String/HTMLElement/Element} container
30208      * The id or element that will contain the toolbar
30209      */
30210     // private
30211     render : function(ct){
30212         this.el = Roo.get(ct);
30213         if(this.cls){
30214             this.el.addClass(this.cls);
30215         }
30216         // using a table allows for vertical alignment
30217         // 100% width is needed by Safari...
30218         this.el.update('<div class="x-toolbar x-small-editor"><table cellspacing="0"><tr></tr></table></div>');
30219         this.tr = this.el.child("tr", true);
30220         var autoId = 0;
30221         this.items = new Roo.util.MixedCollection(false, function(o){
30222             return o.id || ("item" + (++autoId));
30223         });
30224         if(this.buttons){
30225             this.add.apply(this, this.buttons);
30226             delete this.buttons;
30227         }
30228     },
30229
30230     /**
30231      * Adds element(s) to the toolbar -- this function takes a variable number of 
30232      * arguments of mixed type and adds them to the toolbar.
30233      * @param {Mixed} arg1 The following types of arguments are all valid:<br />
30234      * <ul>
30235      * <li>{@link Roo.Toolbar.Button} config: A valid button config object (equivalent to {@link #addButton})</li>
30236      * <li>HtmlElement: Any standard HTML element (equivalent to {@link #addElement})</li>
30237      * <li>Field: Any form field (equivalent to {@link #addField})</li>
30238      * <li>Item: Any subclass of {@link Roo.Toolbar.Item} (equivalent to {@link #addItem})</li>
30239      * <li>String: Any generic string (gets wrapped in a {@link Roo.Toolbar.TextItem}, equivalent to {@link #addText}).
30240      * Note that there are a few special strings that are treated differently as explained nRoo.</li>
30241      * <li>'separator' or '-': Creates a separator element (equivalent to {@link #addSeparator})</li>
30242      * <li>' ': Creates a spacer element (equivalent to {@link #addSpacer})</li>
30243      * <li>'->': Creates a fill element (equivalent to {@link #addFill})</li>
30244      * </ul>
30245      * @param {Mixed} arg2
30246      * @param {Mixed} etc.
30247      */
30248     add : function(){
30249         var a = arguments, l = a.length;
30250         for(var i = 0; i < l; i++){
30251             this._add(a[i]);
30252         }
30253     },
30254     // private..
30255     _add : function(el) {
30256         
30257         if (el.xtype) {
30258             el = Roo.factory(el, typeof(Roo.Toolbar[el.xtype]) == 'undefined' ? Roo.form : Roo.Toolbar);
30259         }
30260         
30261         if (el.applyTo){ // some kind of form field
30262             return this.addField(el);
30263         } 
30264         if (el.render){ // some kind of Toolbar.Item
30265             return this.addItem(el);
30266         }
30267         if (typeof el == "string"){ // string
30268             if(el == "separator" || el == "-"){
30269                 return this.addSeparator();
30270             }
30271             if (el == " "){
30272                 return this.addSpacer();
30273             }
30274             if(el == "->"){
30275                 return this.addFill();
30276             }
30277             return this.addText(el);
30278             
30279         }
30280         if(el.tagName){ // element
30281             return this.addElement(el);
30282         }
30283         if(typeof el == "object"){ // must be button config?
30284             return this.addButton(el);
30285         }
30286         // and now what?!?!
30287         return false;
30288         
30289     },
30290     
30291     /**
30292      * Add an Xtype element
30293      * @param {Object} xtype Xtype Object
30294      * @return {Object} created Object
30295      */
30296     addxtype : function(e){
30297         return this.add(e);  
30298     },
30299     
30300     /**
30301      * Returns the Element for this toolbar.
30302      * @return {Roo.Element}
30303      */
30304     getEl : function(){
30305         return this.el;  
30306     },
30307     
30308     /**
30309      * Adds a separator
30310      * @return {Roo.Toolbar.Item} The separator item
30311      */
30312     addSeparator : function(){
30313         return this.addItem(new Roo.Toolbar.Separator());
30314     },
30315
30316     /**
30317      * Adds a spacer element
30318      * @return {Roo.Toolbar.Spacer} The spacer item
30319      */
30320     addSpacer : function(){
30321         return this.addItem(new Roo.Toolbar.Spacer());
30322     },
30323
30324     /**
30325      * Adds a fill element that forces subsequent additions to the right side of the toolbar
30326      * @return {Roo.Toolbar.Fill} The fill item
30327      */
30328     addFill : function(){
30329         return this.addItem(new Roo.Toolbar.Fill());
30330     },
30331
30332     /**
30333      * Adds any standard HTML element to the toolbar
30334      * @param {String/HTMLElement/Element} el The element or id of the element to add
30335      * @return {Roo.Toolbar.Item} The element's item
30336      */
30337     addElement : function(el){
30338         return this.addItem(new Roo.Toolbar.Item(el));
30339     },
30340     /**
30341      * Collection of items on the toolbar.. (only Toolbar Items, so use fields to retrieve fields)
30342      * @type Roo.util.MixedCollection  
30343      */
30344     items : false,
30345      
30346     /**
30347      * Adds any Toolbar.Item or subclass
30348      * @param {Roo.Toolbar.Item} item
30349      * @return {Roo.Toolbar.Item} The item
30350      */
30351     addItem : function(item){
30352         var td = this.nextBlock();
30353         item.render(td);
30354         this.items.add(item);
30355         return item;
30356     },
30357     
30358     /**
30359      * Adds a button (or buttons). See {@link Roo.Toolbar.Button} for more info on the config.
30360      * @param {Object/Array} config A button config or array of configs
30361      * @return {Roo.Toolbar.Button/Array}
30362      */
30363     addButton : function(config){
30364         if(config instanceof Array){
30365             var buttons = [];
30366             for(var i = 0, len = config.length; i < len; i++) {
30367                 buttons.push(this.addButton(config[i]));
30368             }
30369             return buttons;
30370         }
30371         var b = config;
30372         if(!(config instanceof Roo.Toolbar.Button)){
30373             b = config.split ?
30374                 new Roo.Toolbar.SplitButton(config) :
30375                 new Roo.Toolbar.Button(config);
30376         }
30377         var td = this.nextBlock();
30378         b.render(td);
30379         this.items.add(b);
30380         return b;
30381     },
30382     
30383     /**
30384      * Adds text to the toolbar
30385      * @param {String} text The text to add
30386      * @return {Roo.Toolbar.Item} The element's item
30387      */
30388     addText : function(text){
30389         return this.addItem(new Roo.Toolbar.TextItem(text));
30390     },
30391     
30392     /**
30393      * Inserts any {@link Roo.Toolbar.Item}/{@link Roo.Toolbar.Button} at the specified index.
30394      * @param {Number} index The index where the item is to be inserted
30395      * @param {Object/Roo.Toolbar.Item/Roo.Toolbar.Button (may be Array)} item The button, or button config object to be inserted.
30396      * @return {Roo.Toolbar.Button/Item}
30397      */
30398     insertButton : function(index, item){
30399         if(item instanceof Array){
30400             var buttons = [];
30401             for(var i = 0, len = item.length; i < len; i++) {
30402                buttons.push(this.insertButton(index + i, item[i]));
30403             }
30404             return buttons;
30405         }
30406         if (!(item instanceof Roo.Toolbar.Button)){
30407            item = new Roo.Toolbar.Button(item);
30408         }
30409         var td = document.createElement("td");
30410         this.tr.insertBefore(td, this.tr.childNodes[index]);
30411         item.render(td);
30412         this.items.insert(index, item);
30413         return item;
30414     },
30415     
30416     /**
30417      * Adds a new element to the toolbar from the passed {@link Roo.DomHelper} config.
30418      * @param {Object} config
30419      * @return {Roo.Toolbar.Item} The element's item
30420      */
30421     addDom : function(config, returnEl){
30422         var td = this.nextBlock();
30423         Roo.DomHelper.overwrite(td, config);
30424         var ti = new Roo.Toolbar.Item(td.firstChild);
30425         ti.render(td);
30426         this.items.add(ti);
30427         return ti;
30428     },
30429
30430     /**
30431      * Collection of fields on the toolbar.. usefull for quering (value is false if there are no fields)
30432      * @type Roo.util.MixedCollection  
30433      */
30434     fields : false,
30435     
30436     /**
30437      * Adds a dynamically rendered Roo.form field (TextField, ComboBox, etc).
30438      * Note: the field should not have been rendered yet. For a field that has already been
30439      * rendered, use {@link #addElement}.
30440      * @param {Roo.form.Field} field
30441      * @return {Roo.ToolbarItem}
30442      */
30443      
30444       
30445     addField : function(field) {
30446         if (!this.fields) {
30447             var autoId = 0;
30448             this.fields = new Roo.util.MixedCollection(false, function(o){
30449                 return o.id || ("item" + (++autoId));
30450             });
30451
30452         }
30453         
30454         var td = this.nextBlock();
30455         field.render(td);
30456         var ti = new Roo.Toolbar.Item(td.firstChild);
30457         ti.render(td);
30458         this.items.add(ti);
30459         this.fields.add(field);
30460         return ti;
30461     },
30462     /**
30463      * Hide the toolbar
30464      * @method hide
30465      */
30466      
30467       
30468     hide : function()
30469     {
30470         this.el.child('div').setVisibilityMode(Roo.Element.DISPLAY);
30471         this.el.child('div').hide();
30472     },
30473     /**
30474      * Show the toolbar
30475      * @method show
30476      */
30477     show : function()
30478     {
30479         this.el.child('div').show();
30480     },
30481       
30482     // private
30483     nextBlock : function(){
30484         var td = document.createElement("td");
30485         this.tr.appendChild(td);
30486         return td;
30487     },
30488
30489     // private
30490     destroy : function(){
30491         if(this.items){ // rendered?
30492             Roo.destroy.apply(Roo, this.items.items);
30493         }
30494         if(this.fields){ // rendered?
30495             Roo.destroy.apply(Roo, this.fields.items);
30496         }
30497         Roo.Element.uncache(this.el, this.tr);
30498     }
30499 };
30500
30501 /**
30502  * @class Roo.Toolbar.Item
30503  * The base class that other classes should extend in order to get some basic common toolbar item functionality.
30504  * @constructor
30505  * Creates a new Item
30506  * @param {HTMLElement} el 
30507  */
30508 Roo.Toolbar.Item = function(el){
30509     var cfg = {};
30510     if (typeof (el.xtype) != 'undefined') {
30511         cfg = el;
30512         el = cfg.el;
30513     }
30514     
30515     this.el = Roo.getDom(el);
30516     this.id = Roo.id(this.el);
30517     this.hidden = false;
30518     
30519     this.addEvents({
30520          /**
30521              * @event render
30522              * Fires when the button is rendered
30523              * @param {Button} this
30524              */
30525         'render': true
30526     });
30527     Roo.Toolbar.Item.superclass.constructor.call(this,cfg);
30528 };
30529 Roo.extend(Roo.Toolbar.Item, Roo.util.Observable, {
30530 //Roo.Toolbar.Item.prototype = {
30531     
30532     /**
30533      * Get this item's HTML Element
30534      * @return {HTMLElement}
30535      */
30536     getEl : function(){
30537        return this.el;  
30538     },
30539
30540     // private
30541     render : function(td){
30542         
30543          this.td = td;
30544         td.appendChild(this.el);
30545         
30546         this.fireEvent('render', this);
30547     },
30548     
30549     /**
30550      * Removes and destroys this item.
30551      */
30552     destroy : function(){
30553         this.td.parentNode.removeChild(this.td);
30554     },
30555     
30556     /**
30557      * Shows this item.
30558      */
30559     show: function(){
30560         this.hidden = false;
30561         this.td.style.display = "";
30562     },
30563     
30564     /**
30565      * Hides this item.
30566      */
30567     hide: function(){
30568         this.hidden = true;
30569         this.td.style.display = "none";
30570     },
30571     
30572     /**
30573      * Convenience function for boolean show/hide.
30574      * @param {Boolean} visible true to show/false to hide
30575      */
30576     setVisible: function(visible){
30577         if(visible) {
30578             this.show();
30579         }else{
30580             this.hide();
30581         }
30582     },
30583     
30584     /**
30585      * Try to focus this item.
30586      */
30587     focus : function(){
30588         Roo.fly(this.el).focus();
30589     },
30590     
30591     /**
30592      * Disables this item.
30593      */
30594     disable : function(){
30595         Roo.fly(this.td).addClass("x-item-disabled");
30596         this.disabled = true;
30597         this.el.disabled = true;
30598     },
30599     
30600     /**
30601      * Enables this item.
30602      */
30603     enable : function(){
30604         Roo.fly(this.td).removeClass("x-item-disabled");
30605         this.disabled = false;
30606         this.el.disabled = false;
30607     }
30608 });
30609
30610
30611 /**
30612  * @class Roo.Toolbar.Separator
30613  * @extends Roo.Toolbar.Item
30614  * A simple toolbar separator class
30615  * @constructor
30616  * Creates a new Separator
30617  */
30618 Roo.Toolbar.Separator = function(cfg){
30619     
30620     var s = document.createElement("span");
30621     s.className = "ytb-sep";
30622     if (cfg) {
30623         cfg.el = s;
30624     }
30625     
30626     Roo.Toolbar.Separator.superclass.constructor.call(this, cfg || s);
30627 };
30628 Roo.extend(Roo.Toolbar.Separator, Roo.Toolbar.Item, {
30629     enable:Roo.emptyFn,
30630     disable:Roo.emptyFn,
30631     focus:Roo.emptyFn
30632 });
30633
30634 /**
30635  * @class Roo.Toolbar.Spacer
30636  * @extends Roo.Toolbar.Item
30637  * A simple element that adds extra horizontal space to a toolbar.
30638  * @constructor
30639  * Creates a new Spacer
30640  */
30641 Roo.Toolbar.Spacer = function(cfg){
30642     var s = document.createElement("div");
30643     s.className = "ytb-spacer";
30644     if (cfg) {
30645         cfg.el = s;
30646     }
30647     Roo.Toolbar.Spacer.superclass.constructor.call(this, cfg || s);
30648 };
30649 Roo.extend(Roo.Toolbar.Spacer, Roo.Toolbar.Item, {
30650     enable:Roo.emptyFn,
30651     disable:Roo.emptyFn,
30652     focus:Roo.emptyFn
30653 });
30654
30655 /**
30656  * @class Roo.Toolbar.Fill
30657  * @extends Roo.Toolbar.Spacer
30658  * A simple element that adds a greedy (100% width) horizontal space to a toolbar.
30659  * @constructor
30660  * Creates a new Spacer
30661  */
30662 Roo.Toolbar.Fill = Roo.extend(Roo.Toolbar.Spacer, {
30663     // private
30664     render : function(td){
30665         td.style.width = '100%';
30666         Roo.Toolbar.Fill.superclass.render.call(this, td);
30667     }
30668 });
30669
30670 /**
30671  * @class Roo.Toolbar.TextItem
30672  * @extends Roo.Toolbar.Item
30673  * A simple class that renders text directly into a toolbar.
30674  * @constructor
30675  * Creates a new TextItem
30676  * @cfg {string} text 
30677  */
30678 Roo.Toolbar.TextItem = function(cfg){
30679     var  text = cfg || "";
30680     if (typeof(cfg) == 'object') {
30681         text = cfg.text || "";
30682     }  else {
30683         cfg = null;
30684     }
30685     var s = document.createElement("span");
30686     s.className = "ytb-text";
30687     s.innerHTML = text;
30688     if (cfg) {
30689         cfg.el  = s;
30690     }
30691     
30692     Roo.Toolbar.TextItem.superclass.constructor.call(this, cfg ||  s);
30693 };
30694 Roo.extend(Roo.Toolbar.TextItem, Roo.Toolbar.Item, {
30695     
30696      
30697     enable:Roo.emptyFn,
30698     disable:Roo.emptyFn,
30699     focus:Roo.emptyFn
30700 });
30701
30702 /**
30703  * @class Roo.Toolbar.Button
30704  * @extends Roo.Button
30705  * A button that renders into a toolbar.
30706  * @constructor
30707  * Creates a new Button
30708  * @param {Object} config A standard {@link Roo.Button} config object
30709  */
30710 Roo.Toolbar.Button = function(config){
30711     Roo.Toolbar.Button.superclass.constructor.call(this, null, config);
30712 };
30713 Roo.extend(Roo.Toolbar.Button, Roo.Button,
30714 {
30715     
30716     
30717     render : function(td){
30718         this.td = td;
30719         Roo.Toolbar.Button.superclass.render.call(this, td);
30720     },
30721     
30722     /**
30723      * Removes and destroys this button
30724      */
30725     destroy : function(){
30726         Roo.Toolbar.Button.superclass.destroy.call(this);
30727         this.td.parentNode.removeChild(this.td);
30728     },
30729     
30730     /**
30731      * Shows this button
30732      */
30733     show: function(){
30734         this.hidden = false;
30735         this.td.style.display = "";
30736     },
30737     
30738     /**
30739      * Hides this button
30740      */
30741     hide: function(){
30742         this.hidden = true;
30743         this.td.style.display = "none";
30744     },
30745
30746     /**
30747      * Disables this item
30748      */
30749     disable : function(){
30750         Roo.fly(this.td).addClass("x-item-disabled");
30751         this.disabled = true;
30752     },
30753
30754     /**
30755      * Enables this item
30756      */
30757     enable : function(){
30758         Roo.fly(this.td).removeClass("x-item-disabled");
30759         this.disabled = false;
30760     }
30761 });
30762 // backwards compat
30763 Roo.ToolbarButton = Roo.Toolbar.Button;
30764
30765 /**
30766  * @class Roo.Toolbar.SplitButton
30767  * @extends Roo.SplitButton
30768  * A menu button that renders into a toolbar.
30769  * @constructor
30770  * Creates a new SplitButton
30771  * @param {Object} config A standard {@link Roo.SplitButton} config object
30772  */
30773 Roo.Toolbar.SplitButton = function(config){
30774     Roo.Toolbar.SplitButton.superclass.constructor.call(this, null, config);
30775 };
30776 Roo.extend(Roo.Toolbar.SplitButton, Roo.SplitButton, {
30777     render : function(td){
30778         this.td = td;
30779         Roo.Toolbar.SplitButton.superclass.render.call(this, td);
30780     },
30781     
30782     /**
30783      * Removes and destroys this button
30784      */
30785     destroy : function(){
30786         Roo.Toolbar.SplitButton.superclass.destroy.call(this);
30787         this.td.parentNode.removeChild(this.td);
30788     },
30789     
30790     /**
30791      * Shows this button
30792      */
30793     show: function(){
30794         this.hidden = false;
30795         this.td.style.display = "";
30796     },
30797     
30798     /**
30799      * Hides this button
30800      */
30801     hide: function(){
30802         this.hidden = true;
30803         this.td.style.display = "none";
30804     }
30805 });
30806
30807 // backwards compat
30808 Roo.Toolbar.MenuButton = Roo.Toolbar.SplitButton;/*
30809  * Based on:
30810  * Ext JS Library 1.1.1
30811  * Copyright(c) 2006-2007, Ext JS, LLC.
30812  *
30813  * Originally Released Under LGPL - original licence link has changed is not relivant.
30814  *
30815  * Fork - LGPL
30816  * <script type="text/javascript">
30817  */
30818  
30819 /**
30820  * @class Roo.PagingToolbar
30821  * @extends Roo.Toolbar
30822  * @children   Roo.Toolbar.Item Roo.form.Field
30823  * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
30824  * @constructor
30825  * Create a new PagingToolbar
30826  * @param {Object} config The config object
30827  */
30828 Roo.PagingToolbar = function(el, ds, config)
30829 {
30830     // old args format still supported... - xtype is prefered..
30831     if (typeof(el) == 'object' && el.xtype) {
30832         // created from xtype...
30833         config = el;
30834         ds = el.dataSource;
30835         el = config.container;
30836     }
30837     var items = [];
30838     if (config.items) {
30839         items = config.items;
30840         config.items = [];
30841     }
30842     
30843     Roo.PagingToolbar.superclass.constructor.call(this, el, null, config);
30844     this.ds = ds;
30845     this.cursor = 0;
30846     this.renderButtons(this.el);
30847     this.bind(ds);
30848     
30849     // supprot items array.
30850    
30851     Roo.each(items, function(e) {
30852         this.add(Roo.factory(e));
30853     },this);
30854     
30855 };
30856
30857 Roo.extend(Roo.PagingToolbar, Roo.Toolbar, {
30858    
30859     /**
30860      * @cfg {String/HTMLElement/Element} container
30861      * container The id or element that will contain the toolbar
30862      */
30863     /**
30864      * @cfg {Boolean} displayInfo
30865      * True to display the displayMsg (defaults to false)
30866      */
30867     
30868     
30869     /**
30870      * @cfg {Number} pageSize
30871      * The number of records to display per page (defaults to 20)
30872      */
30873     pageSize: 20,
30874     /**
30875      * @cfg {String} displayMsg
30876      * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
30877      */
30878     displayMsg : 'Displaying {0} - {1} of {2}',
30879     /**
30880      * @cfg {String} emptyMsg
30881      * The message to display when no records are found (defaults to "No data to display")
30882      */
30883     emptyMsg : 'No data to display',
30884     /**
30885      * Customizable piece of the default paging text (defaults to "Page")
30886      * @type String
30887      */
30888     beforePageText : "Page",
30889     /**
30890      * Customizable piece of the default paging text (defaults to "of %0")
30891      * @type String
30892      */
30893     afterPageText : "of {0}",
30894     /**
30895      * Customizable piece of the default paging text (defaults to "First Page")
30896      * @type String
30897      */
30898     firstText : "First Page",
30899     /**
30900      * Customizable piece of the default paging text (defaults to "Previous Page")
30901      * @type String
30902      */
30903     prevText : "Previous Page",
30904     /**
30905      * Customizable piece of the default paging text (defaults to "Next Page")
30906      * @type String
30907      */
30908     nextText : "Next Page",
30909     /**
30910      * Customizable piece of the default paging text (defaults to "Last Page")
30911      * @type String
30912      */
30913     lastText : "Last Page",
30914     /**
30915      * Customizable piece of the default paging text (defaults to "Refresh")
30916      * @type String
30917      */
30918     refreshText : "Refresh",
30919
30920     // private
30921     renderButtons : function(el){
30922         Roo.PagingToolbar.superclass.render.call(this, el);
30923         this.first = this.addButton({
30924             tooltip: this.firstText,
30925             cls: "x-btn-icon x-grid-page-first",
30926             disabled: true,
30927             handler: this.onClick.createDelegate(this, ["first"])
30928         });
30929         this.prev = this.addButton({
30930             tooltip: this.prevText,
30931             cls: "x-btn-icon x-grid-page-prev",
30932             disabled: true,
30933             handler: this.onClick.createDelegate(this, ["prev"])
30934         });
30935         //this.addSeparator();
30936         this.add(this.beforePageText);
30937         this.field = Roo.get(this.addDom({
30938            tag: "input",
30939            type: "text",
30940            size: "3",
30941            value: "1",
30942            cls: "x-grid-page-number"
30943         }).el);
30944         this.field.on("keydown", this.onPagingKeydown, this);
30945         this.field.on("focus", function(){this.dom.select();});
30946         this.afterTextEl = this.addText(String.format(this.afterPageText, 1));
30947         this.field.setHeight(18);
30948         //this.addSeparator();
30949         this.next = this.addButton({
30950             tooltip: this.nextText,
30951             cls: "x-btn-icon x-grid-page-next",
30952             disabled: true,
30953             handler: this.onClick.createDelegate(this, ["next"])
30954         });
30955         this.last = this.addButton({
30956             tooltip: this.lastText,
30957             cls: "x-btn-icon x-grid-page-last",
30958             disabled: true,
30959             handler: this.onClick.createDelegate(this, ["last"])
30960         });
30961         //this.addSeparator();
30962         this.loading = this.addButton({
30963             tooltip: this.refreshText,
30964             cls: "x-btn-icon x-grid-loading",
30965             handler: this.onClick.createDelegate(this, ["refresh"])
30966         });
30967
30968         if(this.displayInfo){
30969             this.displayEl = Roo.fly(this.el.dom.firstChild).createChild({cls:'x-paging-info'});
30970         }
30971     },
30972
30973     // private
30974     updateInfo : function(){
30975         if(this.displayEl){
30976             var count = this.ds.getCount();
30977             var msg = count == 0 ?
30978                 this.emptyMsg :
30979                 String.format(
30980                     this.displayMsg,
30981                     this.cursor+1, this.cursor+count, this.ds.getTotalCount()    
30982                 );
30983             this.displayEl.update(msg);
30984         }
30985     },
30986
30987     // private
30988     onLoad : function(ds, r, o){
30989        this.cursor = o.params ? o.params.start : 0;
30990        var d = this.getPageData(), ap = d.activePage, ps = d.pages;
30991
30992        this.afterTextEl.el.innerHTML = String.format(this.afterPageText, d.pages);
30993        this.field.dom.value = ap;
30994        this.first.setDisabled(ap == 1);
30995        this.prev.setDisabled(ap == 1);
30996        this.next.setDisabled(ap == ps);
30997        this.last.setDisabled(ap == ps);
30998        this.loading.enable();
30999        this.updateInfo();
31000     },
31001
31002     // private
31003     getPageData : function(){
31004         var total = this.ds.getTotalCount();
31005         return {
31006             total : total,
31007             activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
31008             pages :  total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
31009         };
31010     },
31011
31012     // private
31013     onLoadError : function(){
31014         this.loading.enable();
31015     },
31016
31017     // private
31018     onPagingKeydown : function(e){
31019         var k = e.getKey();
31020         var d = this.getPageData();
31021         if(k == e.RETURN){
31022             var v = this.field.dom.value, pageNum;
31023             if(!v || isNaN(pageNum = parseInt(v, 10))){
31024                 this.field.dom.value = d.activePage;
31025                 return;
31026             }
31027             pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
31028             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
31029             e.stopEvent();
31030         }
31031         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))
31032         {
31033           var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
31034           this.field.dom.value = pageNum;
31035           this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
31036           e.stopEvent();
31037         }
31038         else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
31039         {
31040           var v = this.field.dom.value, pageNum; 
31041           var increment = (e.shiftKey) ? 10 : 1;
31042           if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN) {
31043             increment *= -1;
31044           }
31045           if(!v || isNaN(pageNum = parseInt(v, 10))) {
31046             this.field.dom.value = d.activePage;
31047             return;
31048           }
31049           else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
31050           {
31051             this.field.dom.value = parseInt(v, 10) + increment;
31052             pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
31053             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
31054           }
31055           e.stopEvent();
31056         }
31057     },
31058
31059     // private
31060     beforeLoad : function(){
31061         if(this.loading){
31062             this.loading.disable();
31063         }
31064     },
31065
31066     // private
31067     onClick : function(which){
31068         var ds = this.ds;
31069         switch(which){
31070             case "first":
31071                 ds.load({params:{start: 0, limit: this.pageSize}});
31072             break;
31073             case "prev":
31074                 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
31075             break;
31076             case "next":
31077                 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
31078             break;
31079             case "last":
31080                 var total = ds.getTotalCount();
31081                 var extra = total % this.pageSize;
31082                 var lastStart = extra ? (total - extra) : total-this.pageSize;
31083                 ds.load({params:{start: lastStart, limit: this.pageSize}});
31084             break;
31085             case "refresh":
31086                 ds.load({params:{start: this.cursor, limit: this.pageSize}});
31087             break;
31088         }
31089     },
31090
31091     /**
31092      * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
31093      * @param {Roo.data.Store} store The data store to unbind
31094      */
31095     unbind : function(ds){
31096         ds.un("beforeload", this.beforeLoad, this);
31097         ds.un("load", this.onLoad, this);
31098         ds.un("loadexception", this.onLoadError, this);
31099         ds.un("remove", this.updateInfo, this);
31100         ds.un("add", this.updateInfo, this);
31101         this.ds = undefined;
31102     },
31103
31104     /**
31105      * Binds the paging toolbar to the specified {@link Roo.data.Store}
31106      * @param {Roo.data.Store} store The data store to bind
31107      */
31108     bind : function(ds){
31109         ds.on("beforeload", this.beforeLoad, this);
31110         ds.on("load", this.onLoad, this);
31111         ds.on("loadexception", this.onLoadError, this);
31112         ds.on("remove", this.updateInfo, this);
31113         ds.on("add", this.updateInfo, this);
31114         this.ds = ds;
31115     }
31116 });/*
31117  * Based on:
31118  * Ext JS Library 1.1.1
31119  * Copyright(c) 2006-2007, Ext JS, LLC.
31120  *
31121  * Originally Released Under LGPL - original licence link has changed is not relivant.
31122  *
31123  * Fork - LGPL
31124  * <script type="text/javascript">
31125  */
31126
31127 /**
31128  * @class Roo.Resizable
31129  * @extends Roo.util.Observable
31130  * <p>Applies drag handles to an element to make it resizable. The drag handles are inserted into the element
31131  * and positioned absolute. Some elements, such as a textarea or image, don't support this. To overcome that, you can wrap
31132  * 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
31133  * the element will be wrapped for you automatically.</p>
31134  * <p>Here is the list of valid resize handles:</p>
31135  * <pre>
31136 Value   Description
31137 ------  -------------------
31138  'n'     north
31139  's'     south
31140  'e'     east
31141  'w'     west
31142  'nw'    northwest
31143  'sw'    southwest
31144  'se'    southeast
31145  'ne'    northeast
31146  'hd'    horizontal drag
31147  'all'   all
31148 </pre>
31149  * <p>Here's an example showing the creation of a typical Resizable:</p>
31150  * <pre><code>
31151 var resizer = new Roo.Resizable("element-id", {
31152     handles: 'all',
31153     minWidth: 200,
31154     minHeight: 100,
31155     maxWidth: 500,
31156     maxHeight: 400,
31157     pinned: true
31158 });
31159 resizer.on("resize", myHandler);
31160 </code></pre>
31161  * <p>To hide a particular handle, set its display to none in CSS, or through script:<br>
31162  * resizer.east.setDisplayed(false);</p>
31163  * @cfg {Boolean/String/Element} resizeChild True to resize the first child, or id/element to resize (defaults to false)
31164  * @cfg {Array/String} adjustments String "auto" or an array [width, height] with values to be <b>added</b> to the
31165  * resize operation's new size (defaults to [0, 0])
31166  * @cfg {Number} minWidth The minimum width for the element (defaults to 5)
31167  * @cfg {Number} minHeight The minimum height for the element (defaults to 5)
31168  * @cfg {Number} maxWidth The maximum width for the element (defaults to 10000)
31169  * @cfg {Number} maxHeight The maximum height for the element (defaults to 10000)
31170  * @cfg {Boolean} enabled False to disable resizing (defaults to true)
31171  * @cfg {Boolean} wrap True to wrap an element with a div if needed (required for textareas and images, defaults to false)
31172  * @cfg {Number} width The width of the element in pixels (defaults to null)
31173  * @cfg {Number} height The height of the element in pixels (defaults to null)
31174  * @cfg {Boolean} animate True to animate the resize (not compatible with dynamic sizing, defaults to false)
31175  * @cfg {Number} duration Animation duration if animate = true (defaults to .35)
31176  * @cfg {Boolean} dynamic True to resize the element while dragging instead of using a proxy (defaults to false)
31177  * @cfg {String} handles String consisting of the resize handles to display (defaults to undefined)
31178  * @cfg {Boolean} multiDirectional <b>Deprecated</b>.  The old style of adding multi-direction resize handles, deprecated
31179  * in favor of the handles config option (defaults to false)
31180  * @cfg {Boolean} disableTrackOver True to disable mouse tracking. This is only applied at config time. (defaults to false)
31181  * @cfg {String} easing Animation easing if animate = true (defaults to 'easingOutStrong')
31182  * @cfg {Number} widthIncrement The increment to snap the width resize in pixels (dynamic must be true, defaults to 0)
31183  * @cfg {Number} heightIncrement The increment to snap the height resize in pixels (dynamic must be true, defaults to 0)
31184  * @cfg {Boolean} pinned True to ensure that the resize handles are always visible, false to display them only when the
31185  * user mouses over the resizable borders. This is only applied at config time. (defaults to false)
31186  * @cfg {Boolean} preserveRatio True to preserve the original ratio between height and width during resize (defaults to false)
31187  * @cfg {Boolean} transparent True for transparent handles. This is only applied at config time. (defaults to false)
31188  * @cfg {Number} minX The minimum allowed page X for the element (only used for west resizing, defaults to 0)
31189  * @cfg {Number} minY The minimum allowed page Y for the element (only used for north resizing, defaults to 0)
31190  * @cfg {Boolean} draggable Convenience to initialize drag drop (defaults to false)
31191  * @constructor
31192  * Create a new resizable component
31193  * @param {String/HTMLElement/Roo.Element} el The id or element to resize
31194  * @param {Object} config configuration options
31195   */
31196 Roo.Resizable = function(el, config)
31197 {
31198     this.el = Roo.get(el);
31199
31200     if(config && config.wrap){
31201         config.resizeChild = this.el;
31202         this.el = this.el.wrap(typeof config.wrap == "object" ? config.wrap : {cls:"xresizable-wrap"});
31203         this.el.id = this.el.dom.id = config.resizeChild.id + "-rzwrap";
31204         this.el.setStyle("overflow", "hidden");
31205         this.el.setPositioning(config.resizeChild.getPositioning());
31206         config.resizeChild.clearPositioning();
31207         if(!config.width || !config.height){
31208             var csize = config.resizeChild.getSize();
31209             this.el.setSize(csize.width, csize.height);
31210         }
31211         if(config.pinned && !config.adjustments){
31212             config.adjustments = "auto";
31213         }
31214     }
31215
31216     this.proxy = this.el.createProxy({tag: "div", cls: "x-resizable-proxy", id: this.el.id + "-rzproxy"});
31217     this.proxy.unselectable();
31218     this.proxy.enableDisplayMode('block');
31219
31220     Roo.apply(this, config);
31221
31222     if(this.pinned){
31223         this.disableTrackOver = true;
31224         this.el.addClass("x-resizable-pinned");
31225     }
31226     // if the element isn't positioned, make it relative
31227     var position = this.el.getStyle("position");
31228     if(position != "absolute" && position != "fixed"){
31229         this.el.setStyle("position", "relative");
31230     }
31231     if(!this.handles){ // no handles passed, must be legacy style
31232         this.handles = 's,e,se';
31233         if(this.multiDirectional){
31234             this.handles += ',n,w';
31235         }
31236     }
31237     if(this.handles == "all"){
31238         this.handles = "n s e w ne nw se sw";
31239     }
31240     var hs = this.handles.split(/\s*?[,;]\s*?| /);
31241     var ps = Roo.Resizable.positions;
31242     for(var i = 0, len = hs.length; i < len; i++){
31243         if(hs[i] && ps[hs[i]]){
31244             var pos = ps[hs[i]];
31245             this[pos] = new Roo.Resizable.Handle(this, pos, this.disableTrackOver, this.transparent);
31246         }
31247     }
31248     // legacy
31249     this.corner = this.southeast;
31250     
31251     // updateBox = the box can move..
31252     if(this.handles.indexOf("n") != -1 || this.handles.indexOf("w") != -1 || this.handles.indexOf("hd") != -1) {
31253         this.updateBox = true;
31254     }
31255
31256     this.activeHandle = null;
31257
31258     if(this.resizeChild){
31259         if(typeof this.resizeChild == "boolean"){
31260             this.resizeChild = Roo.get(this.el.dom.firstChild, true);
31261         }else{
31262             this.resizeChild = Roo.get(this.resizeChild, true);
31263         }
31264     }
31265     
31266     if(this.adjustments == "auto"){
31267         var rc = this.resizeChild;
31268         var hw = this.west, he = this.east, hn = this.north, hs = this.south;
31269         if(rc && (hw || hn)){
31270             rc.position("relative");
31271             rc.setLeft(hw ? hw.el.getWidth() : 0);
31272             rc.setTop(hn ? hn.el.getHeight() : 0);
31273         }
31274         this.adjustments = [
31275             (he ? -he.el.getWidth() : 0) + (hw ? -hw.el.getWidth() : 0),
31276             (hn ? -hn.el.getHeight() : 0) + (hs ? -hs.el.getHeight() : 0) -1
31277         ];
31278     }
31279
31280     if(this.draggable){
31281         this.dd = this.dynamic ?
31282             this.el.initDD(null) : this.el.initDDProxy(null, {dragElId: this.proxy.id});
31283         this.dd.setHandleElId(this.resizeChild ? this.resizeChild.id : this.el.id);
31284     }
31285
31286     // public events
31287     this.addEvents({
31288         /**
31289          * @event beforeresize
31290          * Fired before resize is allowed. Set enabled to false to cancel resize.
31291          * @param {Roo.Resizable} this
31292          * @param {Roo.EventObject} e The mousedown event
31293          */
31294         "beforeresize" : true,
31295         /**
31296          * @event resizing
31297          * Fired a resizing.
31298          * @param {Roo.Resizable} this
31299          * @param {Number} x The new x position
31300          * @param {Number} y The new y position
31301          * @param {Number} w The new w width
31302          * @param {Number} h The new h hight
31303          * @param {Roo.EventObject} e The mouseup event
31304          */
31305         "resizing" : true,
31306         /**
31307          * @event resize
31308          * Fired after a resize.
31309          * @param {Roo.Resizable} this
31310          * @param {Number} width The new width
31311          * @param {Number} height The new height
31312          * @param {Roo.EventObject} e The mouseup event
31313          */
31314         "resize" : true
31315     });
31316
31317     if(this.width !== null && this.height !== null){
31318         this.resizeTo(this.width, this.height);
31319     }else{
31320         this.updateChildSize();
31321     }
31322     if(Roo.isIE){
31323         this.el.dom.style.zoom = 1;
31324     }
31325     Roo.Resizable.superclass.constructor.call(this);
31326 };
31327
31328 Roo.extend(Roo.Resizable, Roo.util.Observable, {
31329         resizeChild : false,
31330         adjustments : [0, 0],
31331         minWidth : 5,
31332         minHeight : 5,
31333         maxWidth : 10000,
31334         maxHeight : 10000,
31335         enabled : true,
31336         animate : false,
31337         duration : .35,
31338         dynamic : false,
31339         handles : false,
31340         multiDirectional : false,
31341         disableTrackOver : false,
31342         easing : 'easeOutStrong',
31343         widthIncrement : 0,
31344         heightIncrement : 0,
31345         pinned : false,
31346         width : null,
31347         height : null,
31348         preserveRatio : false,
31349         transparent: false,
31350         minX: 0,
31351         minY: 0,
31352         draggable: false,
31353
31354         /**
31355          * @cfg {String/HTMLElement/Element} constrainTo Constrain the resize to a particular element
31356          */
31357         constrainTo: undefined,
31358         /**
31359          * @cfg {Roo.lib.Region} resizeRegion Constrain the resize to a particular region
31360          */
31361         resizeRegion: undefined,
31362
31363
31364     /**
31365      * Perform a manual resize
31366      * @param {Number} width
31367      * @param {Number} height
31368      */
31369     resizeTo : function(width, height){
31370         this.el.setSize(width, height);
31371         this.updateChildSize();
31372         this.fireEvent("resize", this, width, height, null);
31373     },
31374
31375     // private
31376     startSizing : function(e, handle){
31377         this.fireEvent("beforeresize", this, e);
31378         if(this.enabled){ // 2nd enabled check in case disabled before beforeresize handler
31379
31380             if(!this.overlay){
31381                 this.overlay = this.el.createProxy({tag: "div", cls: "x-resizable-overlay", html: "&#160;"});
31382                 this.overlay.unselectable();
31383                 this.overlay.enableDisplayMode("block");
31384                 this.overlay.on("mousemove", this.onMouseMove, this);
31385                 this.overlay.on("mouseup", this.onMouseUp, this);
31386             }
31387             this.overlay.setStyle("cursor", handle.el.getStyle("cursor"));
31388
31389             this.resizing = true;
31390             this.startBox = this.el.getBox();
31391             this.startPoint = e.getXY();
31392             this.offsets = [(this.startBox.x + this.startBox.width) - this.startPoint[0],
31393                             (this.startBox.y + this.startBox.height) - this.startPoint[1]];
31394
31395             this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
31396             this.overlay.show();
31397
31398             if(this.constrainTo) {
31399                 var ct = Roo.get(this.constrainTo);
31400                 this.resizeRegion = ct.getRegion().adjust(
31401                     ct.getFrameWidth('t'),
31402                     ct.getFrameWidth('l'),
31403                     -ct.getFrameWidth('b'),
31404                     -ct.getFrameWidth('r')
31405                 );
31406             }
31407
31408             this.proxy.setStyle('visibility', 'hidden'); // workaround display none
31409             this.proxy.show();
31410             this.proxy.setBox(this.startBox);
31411             if(!this.dynamic){
31412                 this.proxy.setStyle('visibility', 'visible');
31413             }
31414         }
31415     },
31416
31417     // private
31418     onMouseDown : function(handle, e){
31419         if(this.enabled){
31420             e.stopEvent();
31421             this.activeHandle = handle;
31422             this.startSizing(e, handle);
31423         }
31424     },
31425
31426     // private
31427     onMouseUp : function(e){
31428         var size = this.resizeElement();
31429         this.resizing = false;
31430         this.handleOut();
31431         this.overlay.hide();
31432         this.proxy.hide();
31433         this.fireEvent("resize", this, size.width, size.height, e);
31434     },
31435
31436     // private
31437     updateChildSize : function(){
31438         
31439         if(this.resizeChild){
31440             var el = this.el;
31441             var child = this.resizeChild;
31442             var adj = this.adjustments;
31443             if(el.dom.offsetWidth){
31444                 var b = el.getSize(true);
31445                 child.setSize(b.width+adj[0], b.height+adj[1]);
31446             }
31447             // Second call here for IE
31448             // The first call enables instant resizing and
31449             // the second call corrects scroll bars if they
31450             // exist
31451             if(Roo.isIE){
31452                 setTimeout(function(){
31453                     if(el.dom.offsetWidth){
31454                         var b = el.getSize(true);
31455                         child.setSize(b.width+adj[0], b.height+adj[1]);
31456                     }
31457                 }, 10);
31458             }
31459         }
31460     },
31461
31462     // private
31463     snap : function(value, inc, min){
31464         if(!inc || !value) {
31465             return value;
31466         }
31467         var newValue = value;
31468         var m = value % inc;
31469         if(m > 0){
31470             if(m > (inc/2)){
31471                 newValue = value + (inc-m);
31472             }else{
31473                 newValue = value - m;
31474             }
31475         }
31476         return Math.max(min, newValue);
31477     },
31478
31479     // private
31480     resizeElement : function(){
31481         var box = this.proxy.getBox();
31482         if(this.updateBox){
31483             this.el.setBox(box, false, this.animate, this.duration, null, this.easing);
31484         }else{
31485             this.el.setSize(box.width, box.height, this.animate, this.duration, null, this.easing);
31486         }
31487         this.updateChildSize();
31488         if(!this.dynamic){
31489             this.proxy.hide();
31490         }
31491         return box;
31492     },
31493
31494     // private
31495     constrain : function(v, diff, m, mx){
31496         if(v - diff < m){
31497             diff = v - m;
31498         }else if(v - diff > mx){
31499             diff = mx - v;
31500         }
31501         return diff;
31502     },
31503
31504     // private
31505     onMouseMove : function(e){
31506         
31507         if(this.enabled){
31508             try{// try catch so if something goes wrong the user doesn't get hung
31509
31510             if(this.resizeRegion && !this.resizeRegion.contains(e.getPoint())) {
31511                 return;
31512             }
31513
31514             //var curXY = this.startPoint;
31515             var curSize = this.curSize || this.startBox;
31516             var x = this.startBox.x, y = this.startBox.y;
31517             var ox = x, oy = y;
31518             var w = curSize.width, h = curSize.height;
31519             var ow = w, oh = h;
31520             var mw = this.minWidth, mh = this.minHeight;
31521             var mxw = this.maxWidth, mxh = this.maxHeight;
31522             var wi = this.widthIncrement;
31523             var hi = this.heightIncrement;
31524
31525             var eventXY = e.getXY();
31526             var diffX = -(this.startPoint[0] - Math.max(this.minX, eventXY[0]));
31527             var diffY = -(this.startPoint[1] - Math.max(this.minY, eventXY[1]));
31528
31529             var pos = this.activeHandle.position;
31530
31531             switch(pos){
31532                 case "east":
31533                     w += diffX;
31534                     w = Math.min(Math.max(mw, w), mxw);
31535                     break;
31536              
31537                 case "south":
31538                     h += diffY;
31539                     h = Math.min(Math.max(mh, h), mxh);
31540                     break;
31541                 case "southeast":
31542                     w += diffX;
31543                     h += diffY;
31544                     w = Math.min(Math.max(mw, w), mxw);
31545                     h = Math.min(Math.max(mh, h), mxh);
31546                     break;
31547                 case "north":
31548                     diffY = this.constrain(h, diffY, mh, mxh);
31549                     y += diffY;
31550                     h -= diffY;
31551                     break;
31552                 case "hdrag":
31553                     
31554                     if (wi) {
31555                         var adiffX = Math.abs(diffX);
31556                         var sub = (adiffX % wi); // how much 
31557                         if (sub > (wi/2)) { // far enough to snap
31558                             diffX = (diffX > 0) ? diffX-sub + wi : diffX+sub - wi;
31559                         } else {
31560                             // remove difference.. 
31561                             diffX = (diffX > 0) ? diffX-sub : diffX+sub;
31562                         }
31563                     }
31564                     x += diffX;
31565                     x = Math.max(this.minX, x);
31566                     break;
31567                 case "west":
31568                     diffX = this.constrain(w, diffX, mw, mxw);
31569                     x += diffX;
31570                     w -= diffX;
31571                     break;
31572                 case "northeast":
31573                     w += diffX;
31574                     w = Math.min(Math.max(mw, w), mxw);
31575                     diffY = this.constrain(h, diffY, mh, mxh);
31576                     y += diffY;
31577                     h -= diffY;
31578                     break;
31579                 case "northwest":
31580                     diffX = this.constrain(w, diffX, mw, mxw);
31581                     diffY = this.constrain(h, diffY, mh, mxh);
31582                     y += diffY;
31583                     h -= diffY;
31584                     x += diffX;
31585                     w -= diffX;
31586                     break;
31587                case "southwest":
31588                     diffX = this.constrain(w, diffX, mw, mxw);
31589                     h += diffY;
31590                     h = Math.min(Math.max(mh, h), mxh);
31591                     x += diffX;
31592                     w -= diffX;
31593                     break;
31594             }
31595
31596             var sw = this.snap(w, wi, mw);
31597             var sh = this.snap(h, hi, mh);
31598             if(sw != w || sh != h){
31599                 switch(pos){
31600                     case "northeast":
31601                         y -= sh - h;
31602                     break;
31603                     case "north":
31604                         y -= sh - h;
31605                         break;
31606                     case "southwest":
31607                         x -= sw - w;
31608                     break;
31609                     case "west":
31610                         x -= sw - w;
31611                         break;
31612                     case "northwest":
31613                         x -= sw - w;
31614                         y -= sh - h;
31615                     break;
31616                 }
31617                 w = sw;
31618                 h = sh;
31619             }
31620
31621             if(this.preserveRatio){
31622                 switch(pos){
31623                     case "southeast":
31624                     case "east":
31625                         h = oh * (w/ow);
31626                         h = Math.min(Math.max(mh, h), mxh);
31627                         w = ow * (h/oh);
31628                        break;
31629                     case "south":
31630                         w = ow * (h/oh);
31631                         w = Math.min(Math.max(mw, w), mxw);
31632                         h = oh * (w/ow);
31633                         break;
31634                     case "northeast":
31635                         w = ow * (h/oh);
31636                         w = Math.min(Math.max(mw, w), mxw);
31637                         h = oh * (w/ow);
31638                     break;
31639                     case "north":
31640                         var tw = w;
31641                         w = ow * (h/oh);
31642                         w = Math.min(Math.max(mw, w), mxw);
31643                         h = oh * (w/ow);
31644                         x += (tw - w) / 2;
31645                         break;
31646                     case "southwest":
31647                         h = oh * (w/ow);
31648                         h = Math.min(Math.max(mh, h), mxh);
31649                         var tw = w;
31650                         w = ow * (h/oh);
31651                         x += tw - w;
31652                         break;
31653                     case "west":
31654                         var th = h;
31655                         h = oh * (w/ow);
31656                         h = Math.min(Math.max(mh, h), mxh);
31657                         y += (th - h) / 2;
31658                         var tw = w;
31659                         w = ow * (h/oh);
31660                         x += tw - w;
31661                        break;
31662                     case "northwest":
31663                         var tw = w;
31664                         var th = h;
31665                         h = oh * (w/ow);
31666                         h = Math.min(Math.max(mh, h), mxh);
31667                         w = ow * (h/oh);
31668                         y += th - h;
31669                         x += tw - w;
31670                        break;
31671
31672                 }
31673             }
31674             if (pos == 'hdrag') {
31675                 w = ow;
31676             }
31677             this.proxy.setBounds(x, y, w, h);
31678             if(this.dynamic){
31679                 this.resizeElement();
31680             }
31681             }catch(e){}
31682         }
31683         this.fireEvent("resizing", this, x, y, w, h, e);
31684     },
31685
31686     // private
31687     handleOver : function(){
31688         if(this.enabled){
31689             this.el.addClass("x-resizable-over");
31690         }
31691     },
31692
31693     // private
31694     handleOut : function(){
31695         if(!this.resizing){
31696             this.el.removeClass("x-resizable-over");
31697         }
31698     },
31699
31700     /**
31701      * Returns the element this component is bound to.
31702      * @return {Roo.Element}
31703      */
31704     getEl : function(){
31705         return this.el;
31706     },
31707
31708     /**
31709      * Returns the resizeChild element (or null).
31710      * @return {Roo.Element}
31711      */
31712     getResizeChild : function(){
31713         return this.resizeChild;
31714     },
31715     groupHandler : function()
31716     {
31717         
31718     },
31719     /**
31720      * Destroys this resizable. If the element was wrapped and
31721      * removeEl is not true then the element remains.
31722      * @param {Boolean} removeEl (optional) true to remove the element from the DOM
31723      */
31724     destroy : function(removeEl){
31725         this.proxy.remove();
31726         if(this.overlay){
31727             this.overlay.removeAllListeners();
31728             this.overlay.remove();
31729         }
31730         var ps = Roo.Resizable.positions;
31731         for(var k in ps){
31732             if(typeof ps[k] != "function" && this[ps[k]]){
31733                 var h = this[ps[k]];
31734                 h.el.removeAllListeners();
31735                 h.el.remove();
31736             }
31737         }
31738         if(removeEl){
31739             this.el.update("");
31740             this.el.remove();
31741         }
31742     }
31743 });
31744
31745 // private
31746 // hash to map config positions to true positions
31747 Roo.Resizable.positions = {
31748     n: "north", s: "south", e: "east", w: "west", se: "southeast", sw: "southwest", nw: "northwest", ne: "northeast", 
31749     hd: "hdrag"
31750 };
31751
31752 // private
31753 Roo.Resizable.Handle = function(rz, pos, disableTrackOver, transparent){
31754     if(!this.tpl){
31755         // only initialize the template if resizable is used
31756         var tpl = Roo.DomHelper.createTemplate(
31757             {tag: "div", cls: "x-resizable-handle x-resizable-handle-{0}"}
31758         );
31759         tpl.compile();
31760         Roo.Resizable.Handle.prototype.tpl = tpl;
31761     }
31762     this.position = pos;
31763     this.rz = rz;
31764     // show north drag fro topdra
31765     var handlepos = pos == 'hdrag' ? 'north' : pos;
31766     
31767     this.el = this.tpl.append(rz.el.dom, [handlepos], true);
31768     if (pos == 'hdrag') {
31769         this.el.setStyle('cursor', 'pointer');
31770     }
31771     this.el.unselectable();
31772     if(transparent){
31773         this.el.setOpacity(0);
31774     }
31775     this.el.on("mousedown", this.onMouseDown, this);
31776     if(!disableTrackOver){
31777         this.el.on("mouseover", this.onMouseOver, this);
31778         this.el.on("mouseout", this.onMouseOut, this);
31779     }
31780 };
31781
31782 // private
31783 Roo.Resizable.Handle.prototype = {
31784     afterResize : function(rz){
31785         Roo.log('after?');
31786         // do nothing
31787     },
31788     // private
31789     onMouseDown : function(e){
31790         this.rz.onMouseDown(this, e);
31791     },
31792     // private
31793     onMouseOver : function(e){
31794         this.rz.handleOver(this, e);
31795     },
31796     // private
31797     onMouseOut : function(e){
31798         this.rz.handleOut(this, e);
31799     }
31800 };/*
31801  * Based on:
31802  * Ext JS Library 1.1.1
31803  * Copyright(c) 2006-2007, Ext JS, LLC.
31804  *
31805  * Originally Released Under LGPL - original licence link has changed is not relivant.
31806  *
31807  * Fork - LGPL
31808  * <script type="text/javascript">
31809  */
31810
31811 /**
31812  * @class Roo.Editor
31813  * @extends Roo.Component
31814  * A base editor field that handles displaying/hiding on demand and has some built-in sizing and event handling logic.
31815  * @constructor
31816  * Create a new Editor
31817  * @param {Roo.form.Field} field The Field object (or descendant)
31818  * @param {Object} config The config object
31819  */
31820 Roo.Editor = function(field, config){
31821     Roo.Editor.superclass.constructor.call(this, config);
31822     this.field = field;
31823     this.addEvents({
31824         /**
31825              * @event beforestartedit
31826              * Fires when editing is initiated, but before the value changes.  Editing can be canceled by returning
31827              * false from the handler of this event.
31828              * @param {Editor} this
31829              * @param {Roo.Element} boundEl The underlying element bound to this editor
31830              * @param {Mixed} value The field value being set
31831              */
31832         "beforestartedit" : true,
31833         /**
31834              * @event startedit
31835              * Fires when this editor is displayed
31836              * @param {Roo.Element} boundEl The underlying element bound to this editor
31837              * @param {Mixed} value The starting field value
31838              */
31839         "startedit" : true,
31840         /**
31841              * @event beforecomplete
31842              * Fires after a change has been made to the field, but before the change is reflected in the underlying
31843              * field.  Saving the change to the field can be canceled by returning false from the handler of this event.
31844              * Note that if the value has not changed and ignoreNoChange = true, the editing will still end but this
31845              * event will not fire since no edit actually occurred.
31846              * @param {Editor} this
31847              * @param {Mixed} value The current field value
31848              * @param {Mixed} startValue The original field value
31849              */
31850         "beforecomplete" : true,
31851         /**
31852              * @event complete
31853              * Fires after editing is complete and any changed value has been written to the underlying field.
31854              * @param {Editor} this
31855              * @param {Mixed} value The current field value
31856              * @param {Mixed} startValue The original field value
31857              */
31858         "complete" : true,
31859         /**
31860          * @event specialkey
31861          * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
31862          * {@link Roo.EventObject#getKey} to determine which key was pressed.
31863          * @param {Roo.form.Field} this
31864          * @param {Roo.EventObject} e The event object
31865          */
31866         "specialkey" : true
31867     });
31868 };
31869
31870 Roo.extend(Roo.Editor, Roo.Component, {
31871     /**
31872      * @cfg {Boolean/String} autosize
31873      * True for the editor to automatically adopt the size of the underlying field, "width" to adopt the width only,
31874      * or "height" to adopt the height only (defaults to false)
31875      */
31876     /**
31877      * @cfg {Boolean} revertInvalid
31878      * True to automatically revert the field value and cancel the edit when the user completes an edit and the field
31879      * validation fails (defaults to true)
31880      */
31881     /**
31882      * @cfg {Boolean} ignoreNoChange
31883      * True to skip the the edit completion process (no save, no events fired) if the user completes an edit and
31884      * the value has not changed (defaults to false).  Applies only to string values - edits for other data types
31885      * will never be ignored.
31886      */
31887     /**
31888      * @cfg {Boolean} hideEl
31889      * False to keep the bound element visible while the editor is displayed (defaults to true)
31890      */
31891     /**
31892      * @cfg {Mixed} value
31893      * The data value of the underlying field (defaults to "")
31894      */
31895     value : "",
31896     /**
31897      * @cfg {String} alignment
31898      * The position to align to (see {@link Roo.Element#alignTo} for more details, defaults to "c-c?").
31899      */
31900     alignment: "c-c?",
31901     /**
31902      * @cfg {Boolean/String} shadow "sides" for sides/bottom only, "frame" for 4-way shadow, and "drop"
31903      * for bottom-right shadow (defaults to "frame")
31904      */
31905     shadow : "frame",
31906     /**
31907      * @cfg {Boolean} constrain True to constrain the editor to the viewport
31908      */
31909     constrain : false,
31910     /**
31911      * @cfg {Boolean} completeOnEnter True to complete the edit when the enter key is pressed (defaults to false)
31912      */
31913     completeOnEnter : false,
31914     /**
31915      * @cfg {Boolean} cancelOnEsc True to cancel the edit when the escape key is pressed (defaults to false)
31916      */
31917     cancelOnEsc : false,
31918     /**
31919      * @cfg {Boolean} updateEl True to update the innerHTML of the bound element when the update completes (defaults to false)
31920      */
31921     updateEl : false,
31922
31923     // private
31924     onRender : function(ct, position){
31925         this.el = new Roo.Layer({
31926             shadow: this.shadow,
31927             cls: "x-editor",
31928             parentEl : ct,
31929             shim : this.shim,
31930             shadowOffset:4,
31931             id: this.id,
31932             constrain: this.constrain
31933         });
31934         this.el.setStyle("overflow", Roo.isGecko ? "auto" : "hidden");
31935         if(this.field.msgTarget != 'title'){
31936             this.field.msgTarget = 'qtip';
31937         }
31938         this.field.render(this.el);
31939         if(Roo.isGecko){
31940             this.field.el.dom.setAttribute('autocomplete', 'off');
31941         }
31942         this.field.on("specialkey", this.onSpecialKey, this);
31943         if(this.swallowKeys){
31944             this.field.el.swallowEvent(['keydown','keypress']);
31945         }
31946         this.field.show();
31947         this.field.on("blur", this.onBlur, this);
31948         if(this.field.grow){
31949             this.field.on("autosize", this.el.sync,  this.el, {delay:1});
31950         }
31951     },
31952
31953     onSpecialKey : function(field, e)
31954     {
31955         //Roo.log('editor onSpecialKey');
31956         if(this.completeOnEnter && e.getKey() == e.ENTER){
31957             e.stopEvent();
31958             this.completeEdit();
31959             return;
31960         }
31961         // do not fire special key otherwise it might hide close the editor...
31962         if(e.getKey() == e.ENTER){    
31963             return;
31964         }
31965         if(this.cancelOnEsc && e.getKey() == e.ESC){
31966             this.cancelEdit();
31967             return;
31968         } 
31969         this.fireEvent('specialkey', field, e);
31970     
31971     },
31972
31973     /**
31974      * Starts the editing process and shows the editor.
31975      * @param {String/HTMLElement/Element} el The element to edit
31976      * @param {String} value (optional) A value to initialize the editor with. If a value is not provided, it defaults
31977       * to the innerHTML of el.
31978      */
31979     startEdit : function(el, value){
31980         if(this.editing){
31981             this.completeEdit();
31982         }
31983         this.boundEl = Roo.get(el);
31984         var v = value !== undefined ? value : this.boundEl.dom.innerHTML;
31985         if(!this.rendered){
31986             this.render(this.parentEl || document.body);
31987         }
31988         if(this.fireEvent("beforestartedit", this, this.boundEl, v) === false){
31989             return;
31990         }
31991         this.startValue = v;
31992         this.field.setValue(v);
31993         if(this.autoSize){
31994             var sz = this.boundEl.getSize();
31995             switch(this.autoSize){
31996                 case "width":
31997                 this.setSize(sz.width,  "");
31998                 break;
31999                 case "height":
32000                 this.setSize("",  sz.height);
32001                 break;
32002                 default:
32003                 this.setSize(sz.width,  sz.height);
32004             }
32005         }
32006         this.el.alignTo(this.boundEl, this.alignment);
32007         this.editing = true;
32008         if(Roo.QuickTips){
32009             Roo.QuickTips.disable();
32010         }
32011         this.show();
32012     },
32013
32014     /**
32015      * Sets the height and width of this editor.
32016      * @param {Number} width The new width
32017      * @param {Number} height The new height
32018      */
32019     setSize : function(w, h){
32020         this.field.setSize(w, h);
32021         if(this.el){
32022             this.el.sync();
32023         }
32024     },
32025
32026     /**
32027      * Realigns the editor to the bound field based on the current alignment config value.
32028      */
32029     realign : function(){
32030         this.el.alignTo(this.boundEl, this.alignment);
32031     },
32032
32033     /**
32034      * Ends the editing process, persists the changed value to the underlying field, and hides the editor.
32035      * @param {Boolean} remainVisible Override the default behavior and keep the editor visible after edit (defaults to false)
32036      */
32037     completeEdit : function(remainVisible){
32038         if(!this.editing){
32039             return;
32040         }
32041         var v = this.getValue();
32042         if(this.revertInvalid !== false && !this.field.isValid()){
32043             v = this.startValue;
32044             this.cancelEdit(true);
32045         }
32046         if(String(v) === String(this.startValue) && this.ignoreNoChange){
32047             this.editing = false;
32048             this.hide();
32049             return;
32050         }
32051         if(this.fireEvent("beforecomplete", this, v, this.startValue) !== false){
32052             this.editing = false;
32053             if(this.updateEl && this.boundEl){
32054                 this.boundEl.update(v);
32055             }
32056             if(remainVisible !== true){
32057                 this.hide();
32058             }
32059             this.fireEvent("complete", this, v, this.startValue);
32060         }
32061     },
32062
32063     // private
32064     onShow : function(){
32065         this.el.show();
32066         if(this.hideEl !== false){
32067             this.boundEl.hide();
32068         }
32069         this.field.show();
32070         if(Roo.isIE && !this.fixIEFocus){ // IE has problems with focusing the first time
32071             this.fixIEFocus = true;
32072             this.deferredFocus.defer(50, this);
32073         }else{
32074             this.field.focus();
32075         }
32076         this.fireEvent("startedit", this.boundEl, this.startValue);
32077     },
32078
32079     deferredFocus : function(){
32080         if(this.editing){
32081             this.field.focus();
32082         }
32083     },
32084
32085     /**
32086      * Cancels the editing process and hides the editor without persisting any changes.  The field value will be
32087      * reverted to the original starting value.
32088      * @param {Boolean} remainVisible Override the default behavior and keep the editor visible after
32089      * cancel (defaults to false)
32090      */
32091     cancelEdit : function(remainVisible){
32092         if(this.editing){
32093             this.setValue(this.startValue);
32094             if(remainVisible !== true){
32095                 this.hide();
32096             }
32097         }
32098     },
32099
32100     // private
32101     onBlur : function(){
32102         if(this.allowBlur !== true && this.editing){
32103             this.completeEdit();
32104         }
32105     },
32106
32107     // private
32108     onHide : function(){
32109         if(this.editing){
32110             this.completeEdit();
32111             return;
32112         }
32113         this.field.blur();
32114         if(this.field.collapse){
32115             this.field.collapse();
32116         }
32117         this.el.hide();
32118         if(this.hideEl !== false){
32119             this.boundEl.show();
32120         }
32121         if(Roo.QuickTips){
32122             Roo.QuickTips.enable();
32123         }
32124     },
32125
32126     /**
32127      * Sets the data value of the editor
32128      * @param {Mixed} value Any valid value supported by the underlying field
32129      */
32130     setValue : function(v){
32131         this.field.setValue(v);
32132     },
32133
32134     /**
32135      * Gets the data value of the editor
32136      * @return {Mixed} The data value
32137      */
32138     getValue : function(){
32139         return this.field.getValue();
32140     }
32141 });/*
32142  * Based on:
32143  * Ext JS Library 1.1.1
32144  * Copyright(c) 2006-2007, Ext JS, LLC.
32145  *
32146  * Originally Released Under LGPL - original licence link has changed is not relivant.
32147  *
32148  * Fork - LGPL
32149  * <script type="text/javascript">
32150  */
32151  
32152 /**
32153  * @class Roo.BasicDialog
32154  * @extends Roo.util.Observable
32155  * Lightweight Dialog Class.  The code below shows the creation of a typical dialog using existing HTML markup:
32156  * <pre><code>
32157 var dlg = new Roo.BasicDialog("my-dlg", {
32158     height: 200,
32159     width: 300,
32160     minHeight: 100,
32161     minWidth: 150,
32162     modal: true,
32163     proxyDrag: true,
32164     shadow: true
32165 });
32166 dlg.addKeyListener(27, dlg.hide, dlg); // ESC can also close the dialog
32167 dlg.addButton('OK', dlg.hide, dlg);    // Could call a save function instead of hiding
32168 dlg.addButton('Cancel', dlg.hide, dlg);
32169 dlg.show();
32170 </code></pre>
32171   <b>A Dialog should always be a direct child of the body element.</b>
32172  * @cfg {Boolean/DomHelper} autoCreate True to auto create from scratch, or using a DomHelper Object (defaults to false)
32173  * @cfg {String} title Default text to display in the title bar (defaults to null)
32174  * @cfg {Number} width Width of the dialog in pixels (can also be set via CSS).  Determined by browser if unspecified.
32175  * @cfg {Number} height Height of the dialog in pixels (can also be set via CSS).  Determined by browser if unspecified.
32176  * @cfg {Number} x The default left page coordinate of the dialog (defaults to center screen)
32177  * @cfg {Number} y The default top page coordinate of the dialog (defaults to center screen)
32178  * @cfg {String/Element} animateTarget Id or element from which the dialog should animate while opening
32179  * (defaults to null with no animation)
32180  * @cfg {Boolean} resizable False to disable manual dialog resizing (defaults to true)
32181  * @cfg {String} resizeHandles Which resize handles to display - see the {@link Roo.Resizable} handles config
32182  * property for valid values (defaults to 'all')
32183  * @cfg {Number} minHeight The minimum allowable height for a resizable dialog (defaults to 80)
32184  * @cfg {Number} minWidth The minimum allowable width for a resizable dialog (defaults to 200)
32185  * @cfg {Boolean} modal True to show the dialog modally, preventing user interaction with the rest of the page (defaults to false)
32186  * @cfg {Boolean} autoScroll True to allow the dialog body contents to overflow and display scrollbars (defaults to false)
32187  * @cfg {Boolean} closable False to remove the built-in top-right corner close button (defaults to true)
32188  * @cfg {Boolean} collapsible False to remove the built-in top-right corner collapse button (defaults to true)
32189  * @cfg {Boolean} constraintoviewport True to keep the dialog constrained within the visible viewport boundaries (defaults to true)
32190  * @cfg {Boolean} syncHeightBeforeShow True to cause the dimensions to be recalculated before the dialog is shown (defaults to false)
32191  * @cfg {Boolean} draggable False to disable dragging of the dialog within the viewport (defaults to true)
32192  * @cfg {Boolean} autoTabs If true, all elements with class 'x-dlg-tab' will get automatically converted to tabs (defaults to false)
32193  * @cfg {String} tabTag The tag name of tab elements, used when autoTabs = true (defaults to 'div')
32194  * @cfg {Boolean} proxyDrag True to drag a lightweight proxy element rather than the dialog itself, used when
32195  * draggable = true (defaults to false)
32196  * @cfg {Boolean} fixedcenter True to ensure that anytime the dialog is shown or resized it gets centered (defaults to false)
32197  * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
32198  * shadow (defaults to false)
32199  * @cfg {Number} shadowOffset The number of pixels to offset the shadow if displayed (defaults to 5)
32200  * @cfg {String} buttonAlign Valid values are "left," "center" and "right" (defaults to "right")
32201  * @cfg {Number} minButtonWidth Minimum width of all dialog buttons (defaults to 75)
32202  * @cfg {Array} buttons Array of buttons
32203  * @cfg {Boolean} shim True to create an iframe shim that prevents selects from showing through (defaults to false)
32204  * @constructor
32205  * Create a new BasicDialog.
32206  * @param {String/HTMLElement/Roo.Element} el The container element or DOM node, or its id
32207  * @param {Object} config Configuration options
32208  */
32209 Roo.BasicDialog = function(el, config){
32210     this.el = Roo.get(el);
32211     var dh = Roo.DomHelper;
32212     if(!this.el && config && config.autoCreate){
32213         if(typeof config.autoCreate == "object"){
32214             if(!config.autoCreate.id){
32215                 config.autoCreate.id = el;
32216             }
32217             this.el = dh.append(document.body,
32218                         config.autoCreate, true);
32219         }else{
32220             this.el = dh.append(document.body,
32221                         {tag: "div", id: el, style:'visibility:hidden;'}, true);
32222         }
32223     }
32224     el = this.el;
32225     el.setDisplayed(true);
32226     el.hide = this.hideAction;
32227     this.id = el.id;
32228     el.addClass("x-dlg");
32229
32230     Roo.apply(this, config);
32231
32232     this.proxy = el.createProxy("x-dlg-proxy");
32233     this.proxy.hide = this.hideAction;
32234     this.proxy.setOpacity(.5);
32235     this.proxy.hide();
32236
32237     if(config.width){
32238         el.setWidth(config.width);
32239     }
32240     if(config.height){
32241         el.setHeight(config.height);
32242     }
32243     this.size = el.getSize();
32244     if(typeof config.x != "undefined" && typeof config.y != "undefined"){
32245         this.xy = [config.x,config.y];
32246     }else{
32247         this.xy = el.getCenterXY(true);
32248     }
32249     /** The header element @type Roo.Element */
32250     this.header = el.child("> .x-dlg-hd");
32251     /** The body element @type Roo.Element */
32252     this.body = el.child("> .x-dlg-bd");
32253     /** The footer element @type Roo.Element */
32254     this.footer = el.child("> .x-dlg-ft");
32255
32256     if(!this.header){
32257         this.header = el.createChild({tag: "div", cls:"x-dlg-hd", html: "&#160;"}, this.body ? this.body.dom : null);
32258     }
32259     if(!this.body){
32260         this.body = el.createChild({tag: "div", cls:"x-dlg-bd"});
32261     }
32262
32263     this.header.unselectable();
32264     if(this.title){
32265         this.header.update(this.title);
32266     }
32267     // this element allows the dialog to be focused for keyboard event
32268     this.focusEl = el.createChild({tag: "a", href:"#", cls:"x-dlg-focus", tabIndex:"-1"});
32269     this.focusEl.swallowEvent("click", true);
32270
32271     this.header.wrap({cls:"x-dlg-hd-right"}).wrap({cls:"x-dlg-hd-left"}, true);
32272
32273     // wrap the body and footer for special rendering
32274     this.bwrap = this.body.wrap({tag: "div", cls:"x-dlg-dlg-body"});
32275     if(this.footer){
32276         this.bwrap.dom.appendChild(this.footer.dom);
32277     }
32278
32279     this.bg = this.el.createChild({
32280         tag: "div", cls:"x-dlg-bg",
32281         html: '<div class="x-dlg-bg-left"><div class="x-dlg-bg-right"><div class="x-dlg-bg-center">&#160;</div></div></div>'
32282     });
32283     this.centerBg = this.bg.child("div.x-dlg-bg-center");
32284
32285
32286     if(this.autoScroll !== false && !this.autoTabs){
32287         this.body.setStyle("overflow", "auto");
32288     }
32289
32290     this.toolbox = this.el.createChild({cls: "x-dlg-toolbox"});
32291
32292     if(this.closable !== false){
32293         this.el.addClass("x-dlg-closable");
32294         this.close = this.toolbox.createChild({cls:"x-dlg-close"});
32295         this.close.on("click", this.closeClick, this);
32296         this.close.addClassOnOver("x-dlg-close-over");
32297     }
32298     if(this.collapsible !== false){
32299         this.collapseBtn = this.toolbox.createChild({cls:"x-dlg-collapse"});
32300         this.collapseBtn.on("click", this.collapseClick, this);
32301         this.collapseBtn.addClassOnOver("x-dlg-collapse-over");
32302         this.header.on("dblclick", this.collapseClick, this);
32303     }
32304     if(this.resizable !== false){
32305         this.el.addClass("x-dlg-resizable");
32306         this.resizer = new Roo.Resizable(el, {
32307             minWidth: this.minWidth || 80,
32308             minHeight:this.minHeight || 80,
32309             handles: this.resizeHandles || "all",
32310             pinned: true
32311         });
32312         this.resizer.on("beforeresize", this.beforeResize, this);
32313         this.resizer.on("resize", this.onResize, this);
32314     }
32315     if(this.draggable !== false){
32316         el.addClass("x-dlg-draggable");
32317         if (!this.proxyDrag) {
32318             var dd = new Roo.dd.DD(el.dom.id, "WindowDrag");
32319         }
32320         else {
32321             var dd = new Roo.dd.DDProxy(el.dom.id, "WindowDrag", {dragElId: this.proxy.id});
32322         }
32323         dd.setHandleElId(this.header.id);
32324         dd.endDrag = this.endMove.createDelegate(this);
32325         dd.startDrag = this.startMove.createDelegate(this);
32326         dd.onDrag = this.onDrag.createDelegate(this);
32327         dd.scroll = false;
32328         this.dd = dd;
32329     }
32330     if(this.modal){
32331         this.mask = dh.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
32332         this.mask.enableDisplayMode("block");
32333         this.mask.hide();
32334         this.el.addClass("x-dlg-modal");
32335     }
32336     if(this.shadow){
32337         this.shadow = new Roo.Shadow({
32338             mode : typeof this.shadow == "string" ? this.shadow : "sides",
32339             offset : this.shadowOffset
32340         });
32341     }else{
32342         this.shadowOffset = 0;
32343     }
32344     if(Roo.useShims && this.shim !== false){
32345         this.shim = this.el.createShim();
32346         this.shim.hide = this.hideAction;
32347         this.shim.hide();
32348     }else{
32349         this.shim = false;
32350     }
32351     if(this.autoTabs){
32352         this.initTabs();
32353     }
32354     if (this.buttons) { 
32355         var bts= this.buttons;
32356         this.buttons = [];
32357         Roo.each(bts, function(b) {
32358             this.addButton(b);
32359         }, this);
32360     }
32361     
32362     
32363     this.addEvents({
32364         /**
32365          * @event keydown
32366          * Fires when a key is pressed
32367          * @param {Roo.BasicDialog} this
32368          * @param {Roo.EventObject} e
32369          */
32370         "keydown" : true,
32371         /**
32372          * @event move
32373          * Fires when this dialog is moved by the user.
32374          * @param {Roo.BasicDialog} this
32375          * @param {Number} x The new page X
32376          * @param {Number} y The new page Y
32377          */
32378         "move" : true,
32379         /**
32380          * @event resize
32381          * Fires when this dialog is resized by the user.
32382          * @param {Roo.BasicDialog} this
32383          * @param {Number} width The new width
32384          * @param {Number} height The new height
32385          */
32386         "resize" : true,
32387         /**
32388          * @event beforehide
32389          * Fires before this dialog is hidden.
32390          * @param {Roo.BasicDialog} this
32391          */
32392         "beforehide" : true,
32393         /**
32394          * @event hide
32395          * Fires when this dialog is hidden.
32396          * @param {Roo.BasicDialog} this
32397          */
32398         "hide" : true,
32399         /**
32400          * @event beforeshow
32401          * Fires before this dialog is shown.
32402          * @param {Roo.BasicDialog} this
32403          */
32404         "beforeshow" : true,
32405         /**
32406          * @event show
32407          * Fires when this dialog is shown.
32408          * @param {Roo.BasicDialog} this
32409          */
32410         "show" : true
32411     });
32412     el.on("keydown", this.onKeyDown, this);
32413     el.on("mousedown", this.toFront, this);
32414     Roo.EventManager.onWindowResize(this.adjustViewport, this, true);
32415     this.el.hide();
32416     Roo.DialogManager.register(this);
32417     Roo.BasicDialog.superclass.constructor.call(this);
32418 };
32419
32420 Roo.extend(Roo.BasicDialog, Roo.util.Observable, {
32421     shadowOffset: Roo.isIE ? 6 : 5,
32422     minHeight: 80,
32423     minWidth: 200,
32424     minButtonWidth: 75,
32425     defaultButton: null,
32426     buttonAlign: "right",
32427     tabTag: 'div',
32428     firstShow: true,
32429
32430     /**
32431      * Sets the dialog title text
32432      * @param {String} text The title text to display
32433      * @return {Roo.BasicDialog} this
32434      */
32435     setTitle : function(text){
32436         this.header.update(text);
32437         return this;
32438     },
32439
32440     // private
32441     closeClick : function(){
32442         this.hide();
32443     },
32444
32445     // private
32446     collapseClick : function(){
32447         this[this.collapsed ? "expand" : "collapse"]();
32448     },
32449
32450     /**
32451      * Collapses the dialog to its minimized state (only the title bar is visible).
32452      * Equivalent to the user clicking the collapse dialog button.
32453      */
32454     collapse : function(){
32455         if(!this.collapsed){
32456             this.collapsed = true;
32457             this.el.addClass("x-dlg-collapsed");
32458             this.restoreHeight = this.el.getHeight();
32459             this.resizeTo(this.el.getWidth(), this.header.getHeight());
32460         }
32461     },
32462
32463     /**
32464      * Expands a collapsed dialog back to its normal state.  Equivalent to the user
32465      * clicking the expand dialog button.
32466      */
32467     expand : function(){
32468         if(this.collapsed){
32469             this.collapsed = false;
32470             this.el.removeClass("x-dlg-collapsed");
32471             this.resizeTo(this.el.getWidth(), this.restoreHeight);
32472         }
32473     },
32474
32475     /**
32476      * Reinitializes the tabs component, clearing out old tabs and finding new ones.
32477      * @return {Roo.TabPanel} The tabs component
32478      */
32479     initTabs : function(){
32480         var tabs = this.getTabs();
32481         while(tabs.getTab(0)){
32482             tabs.removeTab(0);
32483         }
32484         this.el.select(this.tabTag+'.x-dlg-tab').each(function(el){
32485             var dom = el.dom;
32486             tabs.addTab(Roo.id(dom), dom.title);
32487             dom.title = "";
32488         });
32489         tabs.activate(0);
32490         return tabs;
32491     },
32492
32493     // private
32494     beforeResize : function(){
32495         this.resizer.minHeight = Math.max(this.minHeight, this.getHeaderFooterHeight(true)+40);
32496     },
32497
32498     // private
32499     onResize : function(){
32500         this.refreshSize();
32501         this.syncBodyHeight();
32502         this.adjustAssets();
32503         this.focus();
32504         this.fireEvent("resize", this, this.size.width, this.size.height);
32505     },
32506
32507     // private
32508     onKeyDown : function(e){
32509         if(this.isVisible()){
32510             this.fireEvent("keydown", this, e);
32511         }
32512     },
32513
32514     /**
32515      * Resizes the dialog.
32516      * @param {Number} width
32517      * @param {Number} height
32518      * @return {Roo.BasicDialog} this
32519      */
32520     resizeTo : function(width, height){
32521         this.el.setSize(width, height);
32522         this.size = {width: width, height: height};
32523         this.syncBodyHeight();
32524         if(this.fixedcenter){
32525             this.center();
32526         }
32527         if(this.isVisible()){
32528             this.constrainXY();
32529             this.adjustAssets();
32530         }
32531         this.fireEvent("resize", this, width, height);
32532         return this;
32533     },
32534
32535
32536     /**
32537      * Resizes the dialog to fit the specified content size.
32538      * @param {Number} width
32539      * @param {Number} height
32540      * @return {Roo.BasicDialog} this
32541      */
32542     setContentSize : function(w, h){
32543         h += this.getHeaderFooterHeight() + this.body.getMargins("tb");
32544         w += this.body.getMargins("lr") + this.bwrap.getMargins("lr") + this.centerBg.getPadding("lr");
32545         //if(!this.el.isBorderBox()){
32546             h +=  this.body.getPadding("tb") + this.bwrap.getBorderWidth("tb") + this.body.getBorderWidth("tb") + this.el.getBorderWidth("tb");
32547             w += this.body.getPadding("lr") + this.bwrap.getBorderWidth("lr") + this.body.getBorderWidth("lr") + this.bwrap.getPadding("lr") + this.el.getBorderWidth("lr");
32548         //}
32549         if(this.tabs){
32550             h += this.tabs.stripWrap.getHeight() + this.tabs.bodyEl.getMargins("tb") + this.tabs.bodyEl.getPadding("tb");
32551             w += this.tabs.bodyEl.getMargins("lr") + this.tabs.bodyEl.getPadding("lr");
32552         }
32553         this.resizeTo(w, h);
32554         return this;
32555     },
32556
32557     /**
32558      * Adds a key listener for when this dialog is displayed.  This allows you to hook in a function that will be
32559      * executed in response to a particular key being pressed while the dialog is active.
32560      * @param {Number/Array/Object} key Either the numeric key code, array of key codes or an object with the following options:
32561      *                                  {key: (number or array), shift: (true/false), ctrl: (true/false), alt: (true/false)}
32562      * @param {Function} fn The function to call
32563      * @param {Object} scope (optional) The scope of the function
32564      * @return {Roo.BasicDialog} this
32565      */
32566     addKeyListener : function(key, fn, scope){
32567         var keyCode, shift, ctrl, alt;
32568         if(typeof key == "object" && !(key instanceof Array)){
32569             keyCode = key["key"];
32570             shift = key["shift"];
32571             ctrl = key["ctrl"];
32572             alt = key["alt"];
32573         }else{
32574             keyCode = key;
32575         }
32576         var handler = function(dlg, e){
32577             if((!shift || e.shiftKey) && (!ctrl || e.ctrlKey) &&  (!alt || e.altKey)){
32578                 var k = e.getKey();
32579                 if(keyCode instanceof Array){
32580                     for(var i = 0, len = keyCode.length; i < len; i++){
32581                         if(keyCode[i] == k){
32582                           fn.call(scope || window, dlg, k, e);
32583                           return;
32584                         }
32585                     }
32586                 }else{
32587                     if(k == keyCode){
32588                         fn.call(scope || window, dlg, k, e);
32589                     }
32590                 }
32591             }
32592         };
32593         this.on("keydown", handler);
32594         return this;
32595     },
32596
32597     /**
32598      * Returns the TabPanel component (creates it if it doesn't exist).
32599      * Note: If you wish to simply check for the existence of tabs without creating them,
32600      * check for a null 'tabs' property.
32601      * @return {Roo.TabPanel} The tabs component
32602      */
32603     getTabs : function(){
32604         if(!this.tabs){
32605             this.el.addClass("x-dlg-auto-tabs");
32606             this.body.addClass(this.tabPosition == "bottom" ? "x-tabs-bottom" : "x-tabs-top");
32607             this.tabs = new Roo.TabPanel(this.body.dom, this.tabPosition == "bottom");
32608         }
32609         return this.tabs;
32610     },
32611
32612     /**
32613      * Adds a button to the footer section of the dialog.
32614      * @param {String/Object} config A string becomes the button text, an object can either be a Button config
32615      * object or a valid Roo.DomHelper element config
32616      * @param {Function} handler The function called when the button is clicked
32617      * @param {Object} scope (optional) The scope of the handler function (accepts position as a property)
32618      * @return {Roo.Button} The new button
32619      */
32620     addButton : function(config, handler, scope){
32621         var dh = Roo.DomHelper;
32622         if(!this.footer){
32623             this.footer = dh.append(this.bwrap, {tag: "div", cls:"x-dlg-ft"}, true);
32624         }
32625         if(!this.btnContainer){
32626             var tb = this.footer.createChild({
32627
32628                 cls:"x-dlg-btns x-dlg-btns-"+this.buttonAlign,
32629                 html:'<table cellspacing="0"><tbody><tr></tr></tbody></table><div class="x-clear"></div>'
32630             }, null, true);
32631             this.btnContainer = tb.firstChild.firstChild.firstChild;
32632         }
32633         var bconfig = {
32634             handler: handler,
32635             scope: scope,
32636             minWidth: this.minButtonWidth,
32637             hideParent:true
32638         };
32639         if(typeof config == "string"){
32640             bconfig.text = config;
32641         }else{
32642             if(config.tag){
32643                 bconfig.dhconfig = config;
32644             }else{
32645                 Roo.apply(bconfig, config);
32646             }
32647         }
32648         var fc = false;
32649         if ((typeof(bconfig.position) != 'undefined') && bconfig.position < this.btnContainer.childNodes.length-1) {
32650             bconfig.position = Math.max(0, bconfig.position);
32651             fc = this.btnContainer.childNodes[bconfig.position];
32652         }
32653          
32654         var btn = new Roo.Button(
32655             fc ? 
32656                 this.btnContainer.insertBefore(document.createElement("td"),fc)
32657                 : this.btnContainer.appendChild(document.createElement("td")),
32658             //Roo.get(this.btnContainer).createChild( { tag: 'td'},  fc ),
32659             bconfig
32660         );
32661         this.syncBodyHeight();
32662         if(!this.buttons){
32663             /**
32664              * Array of all the buttons that have been added to this dialog via addButton
32665              * @type Array
32666              */
32667             this.buttons = [];
32668         }
32669         this.buttons.push(btn);
32670         return btn;
32671     },
32672
32673     /**
32674      * Sets the default button to be focused when the dialog is displayed.
32675      * @param {Roo.BasicDialog.Button} btn The button object returned by {@link #addButton}
32676      * @return {Roo.BasicDialog} this
32677      */
32678     setDefaultButton : function(btn){
32679         this.defaultButton = btn;
32680         return this;
32681     },
32682
32683     // private
32684     getHeaderFooterHeight : function(safe){
32685         var height = 0;
32686         if(this.header){
32687            height += this.header.getHeight();
32688         }
32689         if(this.footer){
32690            var fm = this.footer.getMargins();
32691             height += (this.footer.getHeight()+fm.top+fm.bottom);
32692         }
32693         height += this.bwrap.getPadding("tb")+this.bwrap.getBorderWidth("tb");
32694         height += this.centerBg.getPadding("tb");
32695         return height;
32696     },
32697
32698     // private
32699     syncBodyHeight : function()
32700     {
32701         var bd = this.body, // the text
32702             cb = this.centerBg, // wrapper around bottom.. but does not seem to be used..
32703             bw = this.bwrap;
32704         var height = this.size.height - this.getHeaderFooterHeight(false);
32705         bd.setHeight(height-bd.getMargins("tb"));
32706         var hh = this.header.getHeight();
32707         var h = this.size.height-hh;
32708         cb.setHeight(h);
32709         
32710         bw.setLeftTop(cb.getPadding("l"), hh+cb.getPadding("t"));
32711         bw.setHeight(h-cb.getPadding("tb"));
32712         
32713         bw.setWidth(this.el.getWidth(true)-cb.getPadding("lr"));
32714         bd.setWidth(bw.getWidth(true));
32715         if(this.tabs){
32716             this.tabs.syncHeight();
32717             if(Roo.isIE){
32718                 this.tabs.el.repaint();
32719             }
32720         }
32721     },
32722
32723     /**
32724      * Restores the previous state of the dialog if Roo.state is configured.
32725      * @return {Roo.BasicDialog} this
32726      */
32727     restoreState : function(){
32728         var box = Roo.state.Manager.get(this.stateId || (this.el.id + "-state"));
32729         if(box && box.width){
32730             this.xy = [box.x, box.y];
32731             this.resizeTo(box.width, box.height);
32732         }
32733         return this;
32734     },
32735
32736     // private
32737     beforeShow : function(){
32738         this.expand();
32739         if(this.fixedcenter){
32740             this.xy = this.el.getCenterXY(true);
32741         }
32742         if(this.modal){
32743             Roo.get(document.body).addClass("x-body-masked");
32744             this.mask.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
32745             this.mask.show();
32746         }
32747         this.constrainXY();
32748     },
32749
32750     // private
32751     animShow : function(){
32752         var b = Roo.get(this.animateTarget).getBox();
32753         this.proxy.setSize(b.width, b.height);
32754         this.proxy.setLocation(b.x, b.y);
32755         this.proxy.show();
32756         this.proxy.setBounds(this.xy[0], this.xy[1], this.size.width, this.size.height,
32757                     true, .35, this.showEl.createDelegate(this));
32758     },
32759
32760     /**
32761      * Shows the dialog.
32762      * @param {String/HTMLElement/Roo.Element} animateTarget (optional) Reset the animation target
32763      * @return {Roo.BasicDialog} this
32764      */
32765     show : function(animateTarget){
32766         if (this.fireEvent("beforeshow", this) === false){
32767             return;
32768         }
32769         if(this.syncHeightBeforeShow){
32770             this.syncBodyHeight();
32771         }else if(this.firstShow){
32772             this.firstShow = false;
32773             this.syncBodyHeight(); // sync the height on the first show instead of in the constructor
32774         }
32775         this.animateTarget = animateTarget || this.animateTarget;
32776         if(!this.el.isVisible()){
32777             this.beforeShow();
32778             if(this.animateTarget && Roo.get(this.animateTarget)){
32779                 this.animShow();
32780             }else{
32781                 this.showEl();
32782             }
32783         }
32784         return this;
32785     },
32786
32787     // private
32788     showEl : function(){
32789         this.proxy.hide();
32790         this.el.setXY(this.xy);
32791         this.el.show();
32792         this.adjustAssets(true);
32793         this.toFront();
32794         this.focus();
32795         // IE peekaboo bug - fix found by Dave Fenwick
32796         if(Roo.isIE){
32797             this.el.repaint();
32798         }
32799         this.fireEvent("show", this);
32800     },
32801
32802     /**
32803      * Focuses the dialog.  If a defaultButton is set, it will receive focus, otherwise the
32804      * dialog itself will receive focus.
32805      */
32806     focus : function(){
32807         if(this.defaultButton){
32808             this.defaultButton.focus();
32809         }else{
32810             this.focusEl.focus();
32811         }
32812     },
32813
32814     // private
32815     constrainXY : function(){
32816         if(this.constraintoviewport !== false){
32817             if(!this.viewSize){
32818                 if(this.container){
32819                     var s = this.container.getSize();
32820                     this.viewSize = [s.width, s.height];
32821                 }else{
32822                     this.viewSize = [Roo.lib.Dom.getViewWidth(),Roo.lib.Dom.getViewHeight()];
32823                 }
32824             }
32825             var s = Roo.get(this.container||document).getScroll();
32826
32827             var x = this.xy[0], y = this.xy[1];
32828             var w = this.size.width, h = this.size.height;
32829             var vw = this.viewSize[0], vh = this.viewSize[1];
32830             // only move it if it needs it
32831             var moved = false;
32832             // first validate right/bottom
32833             if(x + w > vw+s.left){
32834                 x = vw - w;
32835                 moved = true;
32836             }
32837             if(y + h > vh+s.top){
32838                 y = vh - h;
32839                 moved = true;
32840             }
32841             // then make sure top/left isn't negative
32842             if(x < s.left){
32843                 x = s.left;
32844                 moved = true;
32845             }
32846             if(y < s.top){
32847                 y = s.top;
32848                 moved = true;
32849             }
32850             if(moved){
32851                 // cache xy
32852                 this.xy = [x, y];
32853                 if(this.isVisible()){
32854                     this.el.setLocation(x, y);
32855                     this.adjustAssets();
32856                 }
32857             }
32858         }
32859     },
32860
32861     // private
32862     onDrag : function(){
32863         if(!this.proxyDrag){
32864             this.xy = this.el.getXY();
32865             this.adjustAssets();
32866         }
32867     },
32868
32869     // private
32870     adjustAssets : function(doShow){
32871         var x = this.xy[0], y = this.xy[1];
32872         var w = this.size.width, h = this.size.height;
32873         if(doShow === true){
32874             if(this.shadow){
32875                 this.shadow.show(this.el);
32876             }
32877             if(this.shim){
32878                 this.shim.show();
32879             }
32880         }
32881         if(this.shadow && this.shadow.isVisible()){
32882             this.shadow.show(this.el);
32883         }
32884         if(this.shim && this.shim.isVisible()){
32885             this.shim.setBounds(x, y, w, h);
32886         }
32887     },
32888
32889     // private
32890     adjustViewport : function(w, h){
32891         if(!w || !h){
32892             w = Roo.lib.Dom.getViewWidth();
32893             h = Roo.lib.Dom.getViewHeight();
32894         }
32895         // cache the size
32896         this.viewSize = [w, h];
32897         if(this.modal && this.mask.isVisible()){
32898             this.mask.setSize(w, h); // first make sure the mask isn't causing overflow
32899             this.mask.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
32900         }
32901         if(this.isVisible()){
32902             this.constrainXY();
32903         }
32904     },
32905
32906     /**
32907      * Destroys this dialog and all its supporting elements (including any tabs, shim,
32908      * shadow, proxy, mask, etc.)  Also removes all event listeners.
32909      * @param {Boolean} removeEl (optional) true to remove the element from the DOM
32910      */
32911     destroy : function(removeEl){
32912         if(this.isVisible()){
32913             this.animateTarget = null;
32914             this.hide();
32915         }
32916         Roo.EventManager.removeResizeListener(this.adjustViewport, this);
32917         if(this.tabs){
32918             this.tabs.destroy(removeEl);
32919         }
32920         Roo.destroy(
32921              this.shim,
32922              this.proxy,
32923              this.resizer,
32924              this.close,
32925              this.mask
32926         );
32927         if(this.dd){
32928             this.dd.unreg();
32929         }
32930         if(this.buttons){
32931            for(var i = 0, len = this.buttons.length; i < len; i++){
32932                this.buttons[i].destroy();
32933            }
32934         }
32935         this.el.removeAllListeners();
32936         if(removeEl === true){
32937             this.el.update("");
32938             this.el.remove();
32939         }
32940         Roo.DialogManager.unregister(this);
32941     },
32942
32943     // private
32944     startMove : function(){
32945         if(this.proxyDrag){
32946             this.proxy.show();
32947         }
32948         if(this.constraintoviewport !== false){
32949             this.dd.constrainTo(document.body, {right: this.shadowOffset, bottom: this.shadowOffset});
32950         }
32951     },
32952
32953     // private
32954     endMove : function(){
32955         if(!this.proxyDrag){
32956             Roo.dd.DD.prototype.endDrag.apply(this.dd, arguments);
32957         }else{
32958             Roo.dd.DDProxy.prototype.endDrag.apply(this.dd, arguments);
32959             this.proxy.hide();
32960         }
32961         this.refreshSize();
32962         this.adjustAssets();
32963         this.focus();
32964         this.fireEvent("move", this, this.xy[0], this.xy[1]);
32965     },
32966
32967     /**
32968      * Brings this dialog to the front of any other visible dialogs
32969      * @return {Roo.BasicDialog} this
32970      */
32971     toFront : function(){
32972         Roo.DialogManager.bringToFront(this);
32973         return this;
32974     },
32975
32976     /**
32977      * Sends this dialog to the back (under) of any other visible dialogs
32978      * @return {Roo.BasicDialog} this
32979      */
32980     toBack : function(){
32981         Roo.DialogManager.sendToBack(this);
32982         return this;
32983     },
32984
32985     /**
32986      * Centers this dialog in the viewport
32987      * @return {Roo.BasicDialog} this
32988      */
32989     center : function(){
32990         var xy = this.el.getCenterXY(true);
32991         this.moveTo(xy[0], xy[1]);
32992         return this;
32993     },
32994
32995     /**
32996      * Moves the dialog's top-left corner to the specified point
32997      * @param {Number} x
32998      * @param {Number} y
32999      * @return {Roo.BasicDialog} this
33000      */
33001     moveTo : function(x, y){
33002         this.xy = [x,y];
33003         if(this.isVisible()){
33004             this.el.setXY(this.xy);
33005             this.adjustAssets();
33006         }
33007         return this;
33008     },
33009
33010     /**
33011      * Aligns the dialog to the specified element
33012      * @param {String/HTMLElement/Roo.Element} element The element to align to.
33013      * @param {String} position The position to align to (see {@link Roo.Element#alignTo} for more details).
33014      * @param {Array} offsets (optional) Offset the positioning by [x, y]
33015      * @return {Roo.BasicDialog} this
33016      */
33017     alignTo : function(element, position, offsets){
33018         this.xy = this.el.getAlignToXY(element, position, offsets);
33019         if(this.isVisible()){
33020             this.el.setXY(this.xy);
33021             this.adjustAssets();
33022         }
33023         return this;
33024     },
33025
33026     /**
33027      * Anchors an element to another element and realigns it when the window is resized.
33028      * @param {String/HTMLElement/Roo.Element} element The element to align to.
33029      * @param {String} position The position to align to (see {@link Roo.Element#alignTo} for more details)
33030      * @param {Array} offsets (optional) Offset the positioning by [x, y]
33031      * @param {Boolean/Number} monitorScroll (optional) true to monitor body scroll and reposition. If this parameter
33032      * is a number, it is used as the buffer delay (defaults to 50ms).
33033      * @return {Roo.BasicDialog} this
33034      */
33035     anchorTo : function(el, alignment, offsets, monitorScroll){
33036         var action = function(){
33037             this.alignTo(el, alignment, offsets);
33038         };
33039         Roo.EventManager.onWindowResize(action, this);
33040         var tm = typeof monitorScroll;
33041         if(tm != 'undefined'){
33042             Roo.EventManager.on(window, 'scroll', action, this,
33043                 {buffer: tm == 'number' ? monitorScroll : 50});
33044         }
33045         action.call(this);
33046         return this;
33047     },
33048
33049     /**
33050      * Returns true if the dialog is visible
33051      * @return {Boolean}
33052      */
33053     isVisible : function(){
33054         return this.el.isVisible();
33055     },
33056
33057     // private
33058     animHide : function(callback){
33059         var b = Roo.get(this.animateTarget).getBox();
33060         this.proxy.show();
33061         this.proxy.setBounds(this.xy[0], this.xy[1], this.size.width, this.size.height);
33062         this.el.hide();
33063         this.proxy.setBounds(b.x, b.y, b.width, b.height, true, .35,
33064                     this.hideEl.createDelegate(this, [callback]));
33065     },
33066
33067     /**
33068      * Hides the dialog.
33069      * @param {Function} callback (optional) Function to call when the dialog is hidden
33070      * @return {Roo.BasicDialog} this
33071      */
33072     hide : function(callback){
33073         if (this.fireEvent("beforehide", this) === false){
33074             return;
33075         }
33076         if(this.shadow){
33077             this.shadow.hide();
33078         }
33079         if(this.shim) {
33080           this.shim.hide();
33081         }
33082         // sometimes animateTarget seems to get set.. causing problems...
33083         // this just double checks..
33084         if(this.animateTarget && Roo.get(this.animateTarget)) {
33085            this.animHide(callback);
33086         }else{
33087             this.el.hide();
33088             this.hideEl(callback);
33089         }
33090         return this;
33091     },
33092
33093     // private
33094     hideEl : function(callback){
33095         this.proxy.hide();
33096         if(this.modal){
33097             this.mask.hide();
33098             Roo.get(document.body).removeClass("x-body-masked");
33099         }
33100         this.fireEvent("hide", this);
33101         if(typeof callback == "function"){
33102             callback();
33103         }
33104     },
33105
33106     // private
33107     hideAction : function(){
33108         this.setLeft("-10000px");
33109         this.setTop("-10000px");
33110         this.setStyle("visibility", "hidden");
33111     },
33112
33113     // private
33114     refreshSize : function(){
33115         this.size = this.el.getSize();
33116         this.xy = this.el.getXY();
33117         Roo.state.Manager.set(this.stateId || this.el.id + "-state", this.el.getBox());
33118     },
33119
33120     // private
33121     // z-index is managed by the DialogManager and may be overwritten at any time
33122     setZIndex : function(index){
33123         if(this.modal){
33124             this.mask.setStyle("z-index", index);
33125         }
33126         if(this.shim){
33127             this.shim.setStyle("z-index", ++index);
33128         }
33129         if(this.shadow){
33130             this.shadow.setZIndex(++index);
33131         }
33132         this.el.setStyle("z-index", ++index);
33133         if(this.proxy){
33134             this.proxy.setStyle("z-index", ++index);
33135         }
33136         if(this.resizer){
33137             this.resizer.proxy.setStyle("z-index", ++index);
33138         }
33139
33140         this.lastZIndex = index;
33141     },
33142
33143     /**
33144      * Returns the element for this dialog
33145      * @return {Roo.Element} The underlying dialog Element
33146      */
33147     getEl : function(){
33148         return this.el;
33149     }
33150 });
33151
33152 /**
33153  * @class Roo.DialogManager
33154  * Provides global access to BasicDialogs that have been created and
33155  * support for z-indexing (layering) multiple open dialogs.
33156  */
33157 Roo.DialogManager = function(){
33158     var list = {};
33159     var accessList = [];
33160     var front = null;
33161
33162     // private
33163     var sortDialogs = function(d1, d2){
33164         return (!d1._lastAccess || d1._lastAccess < d2._lastAccess) ? -1 : 1;
33165     };
33166
33167     // private
33168     var orderDialogs = function(){
33169         accessList.sort(sortDialogs);
33170         var seed = Roo.DialogManager.zseed;
33171         for(var i = 0, len = accessList.length; i < len; i++){
33172             var dlg = accessList[i];
33173             if(dlg){
33174                 dlg.setZIndex(seed + (i*10));
33175             }
33176         }
33177     };
33178
33179     return {
33180         /**
33181          * The starting z-index for BasicDialogs (defaults to 9000)
33182          * @type Number The z-index value
33183          */
33184         zseed : 9000,
33185
33186         // private
33187         register : function(dlg){
33188             list[dlg.id] = dlg;
33189             accessList.push(dlg);
33190         },
33191
33192         // private
33193         unregister : function(dlg){
33194             delete list[dlg.id];
33195             var i=0;
33196             var len=0;
33197             if(!accessList.indexOf){
33198                 for(  i = 0, len = accessList.length; i < len; i++){
33199                     if(accessList[i] == dlg){
33200                         accessList.splice(i, 1);
33201                         return;
33202                     }
33203                 }
33204             }else{
33205                  i = accessList.indexOf(dlg);
33206                 if(i != -1){
33207                     accessList.splice(i, 1);
33208                 }
33209             }
33210         },
33211
33212         /**
33213          * Gets a registered dialog by id
33214          * @param {String/Object} id The id of the dialog or a dialog
33215          * @return {Roo.BasicDialog} this
33216          */
33217         get : function(id){
33218             return typeof id == "object" ? id : list[id];
33219         },
33220
33221         /**
33222          * Brings the specified dialog to the front
33223          * @param {String/Object} dlg The id of the dialog or a dialog
33224          * @return {Roo.BasicDialog} this
33225          */
33226         bringToFront : function(dlg){
33227             dlg = this.get(dlg);
33228             if(dlg != front){
33229                 front = dlg;
33230                 dlg._lastAccess = new Date().getTime();
33231                 orderDialogs();
33232             }
33233             return dlg;
33234         },
33235
33236         /**
33237          * Sends the specified dialog to the back
33238          * @param {String/Object} dlg The id of the dialog or a dialog
33239          * @return {Roo.BasicDialog} this
33240          */
33241         sendToBack : function(dlg){
33242             dlg = this.get(dlg);
33243             dlg._lastAccess = -(new Date().getTime());
33244             orderDialogs();
33245             return dlg;
33246         },
33247
33248         /**
33249          * Hides all dialogs
33250          */
33251         hideAll : function(){
33252             for(var id in list){
33253                 if(list[id] && typeof list[id] != "function" && list[id].isVisible()){
33254                     list[id].hide();
33255                 }
33256             }
33257         }
33258     };
33259 }();
33260
33261 /**
33262  * @class Roo.LayoutDialog
33263  * @extends Roo.BasicDialog
33264  * @children Roo.ContentPanel
33265  * @parent builder none
33266  * Dialog which provides adjustments for working with a layout in a Dialog.
33267  * Add your necessary layout config options to the dialog's config.<br>
33268  * Example usage (including a nested layout):
33269  * <pre><code>
33270 if(!dialog){
33271     dialog = new Roo.LayoutDialog("download-dlg", {
33272         modal: true,
33273         width:600,
33274         height:450,
33275         shadow:true,
33276         minWidth:500,
33277         minHeight:350,
33278         autoTabs:true,
33279         proxyDrag:true,
33280         // layout config merges with the dialog config
33281         center:{
33282             tabPosition: "top",
33283             alwaysShowTabs: true
33284         }
33285     });
33286     dialog.addKeyListener(27, dialog.hide, dialog);
33287     dialog.setDefaultButton(dialog.addButton("Close", dialog.hide, dialog));
33288     dialog.addButton("Build It!", this.getDownload, this);
33289
33290     // we can even add nested layouts
33291     var innerLayout = new Roo.BorderLayout("dl-inner", {
33292         east: {
33293             initialSize: 200,
33294             autoScroll:true,
33295             split:true
33296         },
33297         center: {
33298             autoScroll:true
33299         }
33300     });
33301     innerLayout.beginUpdate();
33302     innerLayout.add("east", new Roo.ContentPanel("dl-details"));
33303     innerLayout.add("center", new Roo.ContentPanel("selection-panel"));
33304     innerLayout.endUpdate(true);
33305
33306     var layout = dialog.getLayout();
33307     layout.beginUpdate();
33308     layout.add("center", new Roo.ContentPanel("standard-panel",
33309                         {title: "Download the Source", fitToFrame:true}));
33310     layout.add("center", new Roo.NestedLayoutPanel(innerLayout,
33311                {title: "Build your own roo.js"}));
33312     layout.getRegion("center").showPanel(sp);
33313     layout.endUpdate();
33314 }
33315 </code></pre>
33316     * @constructor
33317     * @param {String/HTMLElement/Roo.Element} el The id of or container element, or config
33318     * @param {Object} config configuration options
33319   */
33320 Roo.LayoutDialog = function(el, cfg){
33321     
33322     var config=  cfg;
33323     if (typeof(cfg) == 'undefined') {
33324         config = Roo.apply({}, el);
33325         // not sure why we use documentElement here.. - it should always be body.
33326         // IE7 borks horribly if we use documentElement.
33327         // webkit also does not like documentElement - it creates a body element...
33328         el = Roo.get( document.body || document.documentElement ).createChild();
33329         //config.autoCreate = true;
33330     }
33331     
33332     
33333     config.autoTabs = false;
33334     Roo.LayoutDialog.superclass.constructor.call(this, el, config);
33335     this.body.setStyle({overflow:"hidden", position:"relative"});
33336     this.layout = new Roo.BorderLayout(this.body.dom, config);
33337     this.layout.monitorWindowResize = false;
33338     this.el.addClass("x-dlg-auto-layout");
33339     // fix case when center region overwrites center function
33340     this.center = Roo.BasicDialog.prototype.center;
33341     this.on("show", this.layout.layout, this.layout, true);
33342     if (config.items) {
33343         var xitems = config.items;
33344         delete config.items;
33345         Roo.each(xitems, this.addxtype, this);
33346     }
33347     
33348     
33349 };
33350 Roo.extend(Roo.LayoutDialog, Roo.BasicDialog, {
33351     
33352     
33353     /**
33354      * @cfg {Roo.LayoutRegion} east  
33355      */
33356     /**
33357      * @cfg {Roo.LayoutRegion} west
33358      */
33359     /**
33360      * @cfg {Roo.LayoutRegion} south
33361      */
33362     /**
33363      * @cfg {Roo.LayoutRegion} north
33364      */
33365     /**
33366      * @cfg {Roo.LayoutRegion} center
33367      */
33368     /**
33369      * @cfg {Roo.Button} buttons[]  Bottom buttons..
33370      */
33371     
33372     
33373     /**
33374      * Ends update of the layout <strike>and resets display to none</strike>. Use standard beginUpdate/endUpdate on the layout.
33375      * @deprecated
33376      */
33377     endUpdate : function(){
33378         this.layout.endUpdate();
33379     },
33380
33381     /**
33382      * Begins an update of the layout <strike>and sets display to block and visibility to hidden</strike>. Use standard beginUpdate/endUpdate on the layout.
33383      *  @deprecated
33384      */
33385     beginUpdate : function(){
33386         this.layout.beginUpdate();
33387     },
33388
33389     /**
33390      * Get the BorderLayout for this dialog
33391      * @return {Roo.BorderLayout}
33392      */
33393     getLayout : function(){
33394         return this.layout;
33395     },
33396
33397     showEl : function(){
33398         Roo.LayoutDialog.superclass.showEl.apply(this, arguments);
33399         if(Roo.isIE7){
33400             this.layout.layout();
33401         }
33402     },
33403
33404     // private
33405     // Use the syncHeightBeforeShow config option to control this automatically
33406     syncBodyHeight : function(){
33407         Roo.LayoutDialog.superclass.syncBodyHeight.call(this);
33408         if(this.layout){this.layout.layout();}
33409     },
33410     
33411       /**
33412      * Add an xtype element (actually adds to the layout.)
33413      * @return {Object} xdata xtype object data.
33414      */
33415     
33416     addxtype : function(c) {
33417         return this.layout.addxtype(c);
33418     }
33419 });/*
33420  * Based on:
33421  * Ext JS Library 1.1.1
33422  * Copyright(c) 2006-2007, Ext JS, LLC.
33423  *
33424  * Originally Released Under LGPL - original licence link has changed is not relivant.
33425  *
33426  * Fork - LGPL
33427  * <script type="text/javascript">
33428  */
33429  
33430 /**
33431  * @class Roo.MessageBox
33432  * Utility class for generating different styles of message boxes.  The alias Roo.Msg can also be used.
33433  * Example usage:
33434  *<pre><code>
33435 // Basic alert:
33436 Roo.Msg.alert('Status', 'Changes saved successfully.');
33437
33438 // Prompt for user data:
33439 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
33440     if (btn == 'ok'){
33441         // process text value...
33442     }
33443 });
33444
33445 // Show a dialog using config options:
33446 Roo.Msg.show({
33447    title:'Save Changes?',
33448    msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
33449    buttons: Roo.Msg.YESNOCANCEL,
33450    fn: processResult,
33451    animEl: 'elId'
33452 });
33453 </code></pre>
33454  * @static
33455  */
33456 Roo.MessageBox = function(){
33457     var dlg, opt, mask, waitTimer;
33458     var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
33459     var buttons, activeTextEl, bwidth;
33460
33461     // private
33462     var handleButton = function(button){
33463         dlg.hide();
33464         Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
33465     };
33466
33467     // private
33468     var handleHide = function(){
33469         if(opt && opt.cls){
33470             dlg.el.removeClass(opt.cls);
33471         }
33472         if(waitTimer){
33473             Roo.TaskMgr.stop(waitTimer);
33474             waitTimer = null;
33475         }
33476     };
33477
33478     // private
33479     var updateButtons = function(b){
33480         var width = 0;
33481         if(!b){
33482             buttons["ok"].hide();
33483             buttons["cancel"].hide();
33484             buttons["yes"].hide();
33485             buttons["no"].hide();
33486             dlg.footer.dom.style.display = 'none';
33487             return width;
33488         }
33489         dlg.footer.dom.style.display = '';
33490         for(var k in buttons){
33491             if(typeof buttons[k] != "function"){
33492                 if(b[k]){
33493                     buttons[k].show();
33494                     buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.MessageBox.buttonText[k]);
33495                     width += buttons[k].el.getWidth()+15;
33496                 }else{
33497                     buttons[k].hide();
33498                 }
33499             }
33500         }
33501         return width;
33502     };
33503
33504     // private
33505     var handleEsc = function(d, k, e){
33506         if(opt && opt.closable !== false){
33507             dlg.hide();
33508         }
33509         if(e){
33510             e.stopEvent();
33511         }
33512     };
33513
33514     return {
33515         /**
33516          * Returns a reference to the underlying {@link Roo.BasicDialog} element
33517          * @return {Roo.BasicDialog} The BasicDialog element
33518          */
33519         getDialog : function(){
33520            if(!dlg){
33521                 dlg = new Roo.BasicDialog("x-msg-box", {
33522                     autoCreate : true,
33523                     shadow: true,
33524                     draggable: true,
33525                     resizable:false,
33526                     constraintoviewport:false,
33527                     fixedcenter:true,
33528                     collapsible : false,
33529                     shim:true,
33530                     modal: true,
33531                     width:400, height:100,
33532                     buttonAlign:"center",
33533                     closeClick : function(){
33534                         if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
33535                             handleButton("no");
33536                         }else{
33537                             handleButton("cancel");
33538                         }
33539                     }
33540                 });
33541                 dlg.on("hide", handleHide);
33542                 mask = dlg.mask;
33543                 dlg.addKeyListener(27, handleEsc);
33544                 buttons = {};
33545                 var bt = this.buttonText;
33546                 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
33547                 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
33548                 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
33549                 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
33550                 bodyEl = dlg.body.createChild({
33551
33552                     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>'
33553                 });
33554                 msgEl = bodyEl.dom.firstChild;
33555                 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
33556                 textboxEl.enableDisplayMode();
33557                 textboxEl.addKeyListener([10,13], function(){
33558                     if(dlg.isVisible() && opt && opt.buttons){
33559                         if(opt.buttons.ok){
33560                             handleButton("ok");
33561                         }else if(opt.buttons.yes){
33562                             handleButton("yes");
33563                         }
33564                     }
33565                 });
33566                 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
33567                 textareaEl.enableDisplayMode();
33568                 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
33569                 progressEl.enableDisplayMode();
33570                 var pf = progressEl.dom.firstChild;
33571                 if (pf) {
33572                     pp = Roo.get(pf.firstChild);
33573                     pp.setHeight(pf.offsetHeight);
33574                 }
33575                 
33576             }
33577             return dlg;
33578         },
33579
33580         /**
33581          * Updates the message box body text
33582          * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
33583          * the XHTML-compliant non-breaking space character '&amp;#160;')
33584          * @return {Roo.MessageBox} This message box
33585          */
33586         updateText : function(text){
33587             if(!dlg.isVisible() && !opt.width){
33588                 dlg.resizeTo(this.maxWidth, 100); // resize first so content is never clipped from previous shows
33589             }
33590             msgEl.innerHTML = text || '&#160;';
33591       
33592             var cw =  Math.max(msgEl.offsetWidth, msgEl.parentNode.scrollWidth);
33593             //Roo.log("guesed size: " + JSON.stringify([cw,msgEl.offsetWidth, msgEl.parentNode.scrollWidth]));
33594             var w = Math.max(
33595                     Math.min(opt.width || cw , this.maxWidth), 
33596                     Math.max(opt.minWidth || this.minWidth, bwidth)
33597             );
33598             if(opt.prompt){
33599                 activeTextEl.setWidth(w);
33600             }
33601             if(dlg.isVisible()){
33602                 dlg.fixedcenter = false;
33603             }
33604             // to big, make it scroll. = But as usual stupid IE does not support
33605             // !important..
33606             
33607             if ( bodyEl.getHeight() > (Roo.lib.Dom.getViewHeight() - 100)) {
33608                 bodyEl.setHeight ( Roo.lib.Dom.getViewHeight() - 100 );
33609                 bodyEl.dom.style.overflowY = 'auto' + ( Roo.isIE ? '' : ' !important');
33610             } else {
33611                 bodyEl.dom.style.height = '';
33612                 bodyEl.dom.style.overflowY = '';
33613             }
33614             if (cw > w) {
33615                 bodyEl.dom.style.get = 'auto' + ( Roo.isIE ? '' : ' !important');
33616             } else {
33617                 bodyEl.dom.style.overflowX = '';
33618             }
33619             
33620             dlg.setContentSize(w, bodyEl.getHeight());
33621             if(dlg.isVisible()){
33622                 dlg.fixedcenter = true;
33623             }
33624             return this;
33625         },
33626
33627         /**
33628          * Updates a progress-style message box's text and progress bar.  Only relevant on message boxes
33629          * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
33630          * @param {Number} value Any number between 0 and 1 (e.g., .5)
33631          * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
33632          * @return {Roo.MessageBox} This message box
33633          */
33634         updateProgress : function(value, text){
33635             if(text){
33636                 this.updateText(text);
33637             }
33638             if (pp) { // weird bug on my firefox - for some reason this is not defined
33639                 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
33640             }
33641             return this;
33642         },        
33643
33644         /**
33645          * Returns true if the message box is currently displayed
33646          * @return {Boolean} True if the message box is visible, else false
33647          */
33648         isVisible : function(){
33649             return dlg && dlg.isVisible();  
33650         },
33651
33652         /**
33653          * Hides the message box if it is displayed
33654          */
33655         hide : function(){
33656             if(this.isVisible()){
33657                 dlg.hide();
33658             }  
33659         },
33660
33661         /**
33662          * Displays a new message box, or reinitializes an existing message box, based on the config options
33663          * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
33664          * The following config object properties are supported:
33665          * <pre>
33666 Property    Type             Description
33667 ----------  ---------------  ------------------------------------------------------------------------------------
33668 animEl            String/Element   An id or Element from which the message box should animate as it opens and
33669                                    closes (defaults to undefined)
33670 buttons           Object/Boolean   A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
33671                                    cancel:'Bar'}), or false to not show any buttons (defaults to false)
33672 closable          Boolean          False to hide the top-right close button (defaults to true).  Note that
33673                                    progress and wait dialogs will ignore this property and always hide the
33674                                    close button as they can only be closed programmatically.
33675 cls               String           A custom CSS class to apply to the message box element
33676 defaultTextHeight Number           The default height in pixels of the message box's multiline textarea if
33677                                    displayed (defaults to 75)
33678 fn                Function         A callback function to execute after closing the dialog.  The arguments to the
33679                                    function will be btn (the name of the button that was clicked, if applicable,
33680                                    e.g. "ok"), and text (the value of the active text field, if applicable).
33681                                    Progress and wait dialogs will ignore this option since they do not respond to
33682                                    user actions and can only be closed programmatically, so any required function
33683                                    should be called by the same code after it closes the dialog.
33684 icon              String           A CSS class that provides a background image to be used as an icon for
33685                                    the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
33686 maxWidth          Number           The maximum width in pixels of the message box (defaults to 600)
33687 minWidth          Number           The minimum width in pixels of the message box (defaults to 100)
33688 modal             Boolean          False to allow user interaction with the page while the message box is
33689                                    displayed (defaults to true)
33690 msg               String           A string that will replace the existing message box body text (defaults
33691                                    to the XHTML-compliant non-breaking space character '&#160;')
33692 multiline         Boolean          True to prompt the user to enter multi-line text (defaults to false)
33693 progress          Boolean          True to display a progress bar (defaults to false)
33694 progressText      String           The text to display inside the progress bar if progress = true (defaults to '')
33695 prompt            Boolean          True to prompt the user to enter single-line text (defaults to false)
33696 proxyDrag         Boolean          True to display a lightweight proxy while dragging (defaults to false)
33697 title             String           The title text
33698 value             String           The string value to set into the active textbox element if displayed
33699 wait              Boolean          True to display a progress bar (defaults to false)
33700 width             Number           The width of the dialog in pixels
33701 </pre>
33702          *
33703          * Example usage:
33704          * <pre><code>
33705 Roo.Msg.show({
33706    title: 'Address',
33707    msg: 'Please enter your address:',
33708    width: 300,
33709    buttons: Roo.MessageBox.OKCANCEL,
33710    multiline: true,
33711    fn: saveAddress,
33712    animEl: 'addAddressBtn'
33713 });
33714 </code></pre>
33715          * @param {Object} config Configuration options
33716          * @return {Roo.MessageBox} This message box
33717          */
33718         show : function(options)
33719         {
33720             
33721             // this causes nightmares if you show one dialog after another
33722             // especially on callbacks..
33723              
33724             if(this.isVisible()){
33725                 
33726                 this.hide();
33727                 Roo.log("[Roo.Messagebox] Show called while message displayed:" );
33728                 Roo.log("Old Dialog Message:" +  msgEl.innerHTML );
33729                 Roo.log("New Dialog Message:" +  options.msg )
33730                 //this.alert("ERROR", "Multiple dialogs where displayed at the same time");
33731                 //throw "Roo.MessageBox ERROR : Multiple dialogs where displayed at the same time";
33732                 
33733             }
33734             var d = this.getDialog();
33735             opt = options;
33736             d.setTitle(opt.title || "&#160;");
33737             d.close.setDisplayed(opt.closable !== false);
33738             activeTextEl = textboxEl;
33739             opt.prompt = opt.prompt || (opt.multiline ? true : false);
33740             if(opt.prompt){
33741                 if(opt.multiline){
33742                     textboxEl.hide();
33743                     textareaEl.show();
33744                     textareaEl.setHeight(typeof opt.multiline == "number" ?
33745                         opt.multiline : this.defaultTextHeight);
33746                     activeTextEl = textareaEl;
33747                 }else{
33748                     textboxEl.show();
33749                     textareaEl.hide();
33750                 }
33751             }else{
33752                 textboxEl.hide();
33753                 textareaEl.hide();
33754             }
33755             progressEl.setDisplayed(opt.progress === true);
33756             this.updateProgress(0);
33757             activeTextEl.dom.value = opt.value || "";
33758             if(opt.prompt){
33759                 dlg.setDefaultButton(activeTextEl);
33760             }else{
33761                 var bs = opt.buttons;
33762                 var db = null;
33763                 if(bs && bs.ok){
33764                     db = buttons["ok"];
33765                 }else if(bs && bs.yes){
33766                     db = buttons["yes"];
33767                 }
33768                 dlg.setDefaultButton(db);
33769             }
33770             bwidth = updateButtons(opt.buttons);
33771             this.updateText(opt.msg);
33772             if(opt.cls){
33773                 d.el.addClass(opt.cls);
33774             }
33775             d.proxyDrag = opt.proxyDrag === true;
33776             d.modal = opt.modal !== false;
33777             d.mask = opt.modal !== false ? mask : false;
33778             if(!d.isVisible()){
33779                 // force it to the end of the z-index stack so it gets a cursor in FF
33780                 document.body.appendChild(dlg.el.dom);
33781                 d.animateTarget = null;
33782                 d.show(options.animEl);
33783             }
33784             return this;
33785         },
33786
33787         /**
33788          * Displays a message box with a progress bar.  This message box has no buttons and is not closeable by
33789          * the user.  You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
33790          * and closing the message box when the process is complete.
33791          * @param {String} title The title bar text
33792          * @param {String} msg The message box body text
33793          * @return {Roo.MessageBox} This message box
33794          */
33795         progress : function(title, msg){
33796             this.show({
33797                 title : title,
33798                 msg : msg,
33799                 buttons: false,
33800                 progress:true,
33801                 closable:false,
33802                 minWidth: this.minProgressWidth,
33803                 modal : true
33804             });
33805             return this;
33806         },
33807
33808         /**
33809          * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
33810          * If a callback function is passed it will be called after the user clicks the button, and the
33811          * id of the button that was clicked will be passed as the only parameter to the callback
33812          * (could also be the top-right close button).
33813          * @param {String} title The title bar text
33814          * @param {String} msg The message box body text
33815          * @param {Function} fn (optional) The callback function invoked after the message box is closed
33816          * @param {Object} scope (optional) The scope of the callback function
33817          * @return {Roo.MessageBox} This message box
33818          */
33819         alert : function(title, msg, fn, scope){
33820             this.show({
33821                 title : title,
33822                 msg : msg,
33823                 buttons: this.OK,
33824                 fn: fn,
33825                 scope : scope,
33826                 modal : true
33827             });
33828             return this;
33829         },
33830
33831         /**
33832          * Displays a message box with an infinitely auto-updating progress bar.  This can be used to block user
33833          * interaction while waiting for a long-running process to complete that does not have defined intervals.
33834          * You are responsible for closing the message box when the process is complete.
33835          * @param {String} msg The message box body text
33836          * @param {String} title (optional) The title bar text
33837          * @return {Roo.MessageBox} This message box
33838          */
33839         wait : function(msg, title){
33840             this.show({
33841                 title : title,
33842                 msg : msg,
33843                 buttons: false,
33844                 closable:false,
33845                 progress:true,
33846                 modal:true,
33847                 width:300,
33848                 wait:true
33849             });
33850             waitTimer = Roo.TaskMgr.start({
33851                 run: function(i){
33852                     Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
33853                 },
33854                 interval: 1000
33855             });
33856             return this;
33857         },
33858
33859         /**
33860          * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
33861          * If a callback function is passed it will be called after the user clicks either button, and the id of the
33862          * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
33863          * @param {String} title The title bar text
33864          * @param {String} msg The message box body text
33865          * @param {Function} fn (optional) The callback function invoked after the message box is closed
33866          * @param {Object} scope (optional) The scope of the callback function
33867          * @return {Roo.MessageBox} This message box
33868          */
33869         confirm : function(title, msg, fn, scope){
33870             this.show({
33871                 title : title,
33872                 msg : msg,
33873                 buttons: this.YESNO,
33874                 fn: fn,
33875                 scope : scope,
33876                 modal : true
33877             });
33878             return this;
33879         },
33880
33881         /**
33882          * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
33883          * JavaScript's Window.prompt).  The prompt can be a single-line or multi-line textbox.  If a callback function
33884          * is passed it will be called after the user clicks either button, and the id of the button that was clicked
33885          * (could also be the top-right close button) and the text that was entered will be passed as the two
33886          * parameters to the callback.
33887          * @param {String} title The title bar text
33888          * @param {String} msg The message box body text
33889          * @param {Function} fn (optional) The callback function invoked after the message box is closed
33890          * @param {Object} scope (optional) The scope of the callback function
33891          * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
33892          * property, or the height in pixels to create the textbox (defaults to false / single-line)
33893          * @return {Roo.MessageBox} This message box
33894          */
33895         prompt : function(title, msg, fn, scope, multiline){
33896             this.show({
33897                 title : title,
33898                 msg : msg,
33899                 buttons: this.OKCANCEL,
33900                 fn: fn,
33901                 minWidth:250,
33902                 scope : scope,
33903                 prompt:true,
33904                 multiline: multiline,
33905                 modal : true
33906             });
33907             return this;
33908         },
33909
33910         /**
33911          * Button config that displays a single OK button
33912          * @type Object
33913          */
33914         OK : {ok:true},
33915         /**
33916          * Button config that displays Yes and No buttons
33917          * @type Object
33918          */
33919         YESNO : {yes:true, no:true},
33920         /**
33921          * Button config that displays OK and Cancel buttons
33922          * @type Object
33923          */
33924         OKCANCEL : {ok:true, cancel:true},
33925         /**
33926          * Button config that displays Yes, No and Cancel buttons
33927          * @type Object
33928          */
33929         YESNOCANCEL : {yes:true, no:true, cancel:true},
33930
33931         /**
33932          * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
33933          * @type Number
33934          */
33935         defaultTextHeight : 75,
33936         /**
33937          * The maximum width in pixels of the message box (defaults to 600)
33938          * @type Number
33939          */
33940         maxWidth : 600,
33941         /**
33942          * The minimum width in pixels of the message box (defaults to 100)
33943          * @type Number
33944          */
33945         minWidth : 100,
33946         /**
33947          * The minimum width in pixels of the message box if it is a progress-style dialog.  This is useful
33948          * for setting a different minimum width than text-only dialogs may need (defaults to 250)
33949          * @type Number
33950          */
33951         minProgressWidth : 250,
33952         /**
33953          * An object containing the default button text strings that can be overriden for localized language support.
33954          * Supported properties are: ok, cancel, yes and no.
33955          * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
33956          * @type Object
33957          */
33958         buttonText : {
33959             ok : "OK",
33960             cancel : "Cancel",
33961             yes : "Yes",
33962             no : "No"
33963         }
33964     };
33965 }();
33966
33967 /**
33968  * Shorthand for {@link Roo.MessageBox}
33969  */
33970 Roo.Msg = Roo.MessageBox;/*
33971  * Based on:
33972  * Ext JS Library 1.1.1
33973  * Copyright(c) 2006-2007, Ext JS, LLC.
33974  *
33975  * Originally Released Under LGPL - original licence link has changed is not relivant.
33976  *
33977  * Fork - LGPL
33978  * <script type="text/javascript">
33979  */
33980 /**
33981  * @class Roo.QuickTips
33982  * Provides attractive and customizable tooltips for any element.
33983  * @static
33984  */
33985 Roo.QuickTips = function(){
33986     var el, tipBody, tipBodyText, tipTitle, tm, cfg, close, tagEls = {}, esc, removeCls = null, bdLeft, bdRight;
33987     var ce, bd, xy, dd;
33988     var visible = false, disabled = true, inited = false;
33989     var showProc = 1, hideProc = 1, dismissProc = 1, locks = [];
33990     
33991     var onOver = function(e){
33992         if(disabled){
33993             return;
33994         }
33995         var t = e.getTarget();
33996         if(!t || t.nodeType !== 1 || t == document || t == document.body){
33997             return;
33998         }
33999         if(ce && t == ce.el){
34000             clearTimeout(hideProc);
34001             return;
34002         }
34003         if(t && tagEls[t.id]){
34004             tagEls[t.id].el = t;
34005             showProc = show.defer(tm.showDelay, tm, [tagEls[t.id]]);
34006             return;
34007         }
34008         var ttp, et = Roo.fly(t);
34009         var ns = cfg.namespace;
34010         if(tm.interceptTitles && t.title){
34011             ttp = t.title;
34012             t.qtip = ttp;
34013             t.removeAttribute("title");
34014             e.preventDefault();
34015         }else{
34016             ttp = t.qtip || et.getAttributeNS(ns, cfg.attribute) || et.getAttributeNS(cfg.alt_namespace, cfg.attribute) ;
34017         }
34018         if(ttp){
34019             showProc = show.defer(tm.showDelay, tm, [{
34020                 el: t, 
34021                 text: ttp.replace(/\\n/g,'<br/>'),
34022                 width: et.getAttributeNS(ns, cfg.width),
34023                 autoHide: et.getAttributeNS(ns, cfg.hide) != "user",
34024                 title: et.getAttributeNS(ns, cfg.title),
34025                     cls: et.getAttributeNS(ns, cfg.cls)
34026             }]);
34027         }
34028     };
34029     
34030     var onOut = function(e){
34031         clearTimeout(showProc);
34032         var t = e.getTarget();
34033         if(t && ce && ce.el == t && (tm.autoHide && ce.autoHide !== false)){
34034             hideProc = setTimeout(hide, tm.hideDelay);
34035         }
34036     };
34037     
34038     var onMove = function(e){
34039         if(disabled){
34040             return;
34041         }
34042         xy = e.getXY();
34043         xy[1] += 18;
34044         if(tm.trackMouse && ce){
34045             el.setXY(xy);
34046         }
34047     };
34048     
34049     var onDown = function(e){
34050         clearTimeout(showProc);
34051         clearTimeout(hideProc);
34052         if(!e.within(el)){
34053             if(tm.hideOnClick){
34054                 hide();
34055                 tm.disable();
34056                 tm.enable.defer(100, tm);
34057             }
34058         }
34059     };
34060     
34061     var getPad = function(){
34062         return 2;//bdLeft.getPadding('l')+bdRight.getPadding('r');
34063     };
34064
34065     var show = function(o){
34066         if(disabled){
34067             return;
34068         }
34069         clearTimeout(dismissProc);
34070         ce = o;
34071         if(removeCls){ // in case manually hidden
34072             el.removeClass(removeCls);
34073             removeCls = null;
34074         }
34075         if(ce.cls){
34076             el.addClass(ce.cls);
34077             removeCls = ce.cls;
34078         }
34079         if(ce.title){
34080             tipTitle.update(ce.title);
34081             tipTitle.show();
34082         }else{
34083             tipTitle.update('');
34084             tipTitle.hide();
34085         }
34086         el.dom.style.width  = tm.maxWidth+'px';
34087         //tipBody.dom.style.width = '';
34088         tipBodyText.update(o.text);
34089         var p = getPad(), w = ce.width;
34090         if(!w){
34091             var td = tipBodyText.dom;
34092             var aw = Math.max(td.offsetWidth, td.clientWidth, td.scrollWidth);
34093             if(aw > tm.maxWidth){
34094                 w = tm.maxWidth;
34095             }else if(aw < tm.minWidth){
34096                 w = tm.minWidth;
34097             }else{
34098                 w = aw;
34099             }
34100         }
34101         //tipBody.setWidth(w);
34102         el.setWidth(parseInt(w, 10) + p);
34103         if(ce.autoHide === false){
34104             close.setDisplayed(true);
34105             if(dd){
34106                 dd.unlock();
34107             }
34108         }else{
34109             close.setDisplayed(false);
34110             if(dd){
34111                 dd.lock();
34112             }
34113         }
34114         if(xy){
34115             el.avoidY = xy[1]-18;
34116             el.setXY(xy);
34117         }
34118         if(tm.animate){
34119             el.setOpacity(.1);
34120             el.setStyle("visibility", "visible");
34121             el.fadeIn({callback: afterShow});
34122         }else{
34123             afterShow();
34124         }
34125     };
34126     
34127     var afterShow = function(){
34128         if(ce){
34129             el.show();
34130             esc.enable();
34131             if(tm.autoDismiss && ce.autoHide !== false){
34132                 dismissProc = setTimeout(hide, tm.autoDismissDelay);
34133             }
34134         }
34135     };
34136     
34137     var hide = function(noanim){
34138         clearTimeout(dismissProc);
34139         clearTimeout(hideProc);
34140         ce = null;
34141         if(el.isVisible()){
34142             esc.disable();
34143             if(noanim !== true && tm.animate){
34144                 el.fadeOut({callback: afterHide});
34145             }else{
34146                 afterHide();
34147             } 
34148         }
34149     };
34150     
34151     var afterHide = function(){
34152         el.hide();
34153         if(removeCls){
34154             el.removeClass(removeCls);
34155             removeCls = null;
34156         }
34157     };
34158     
34159     return {
34160         /**
34161         * @cfg {Number} minWidth
34162         * The minimum width of the quick tip (defaults to 40)
34163         */
34164        minWidth : 40,
34165         /**
34166         * @cfg {Number} maxWidth
34167         * The maximum width of the quick tip (defaults to 300)
34168         */
34169        maxWidth : 300,
34170         /**
34171         * @cfg {Boolean} interceptTitles
34172         * True to automatically use the element's DOM title value if available (defaults to false)
34173         */
34174        interceptTitles : false,
34175         /**
34176         * @cfg {Boolean} trackMouse
34177         * True to have the quick tip follow the mouse as it moves over the target element (defaults to false)
34178         */
34179        trackMouse : false,
34180         /**
34181         * @cfg {Boolean} hideOnClick
34182         * True to hide the quick tip if the user clicks anywhere in the document (defaults to true)
34183         */
34184        hideOnClick : true,
34185         /**
34186         * @cfg {Number} showDelay
34187         * Delay in milliseconds before the quick tip displays after the mouse enters the target element (defaults to 500)
34188         */
34189        showDelay : 500,
34190         /**
34191         * @cfg {Number} hideDelay
34192         * Delay in milliseconds before the quick tip hides when autoHide = true (defaults to 200)
34193         */
34194        hideDelay : 200,
34195         /**
34196         * @cfg {Boolean} autoHide
34197         * True to automatically hide the quick tip after the mouse exits the target element (defaults to true).
34198         * Used in conjunction with hideDelay.
34199         */
34200        autoHide : true,
34201         /**
34202         * @cfg {Boolean}
34203         * True to automatically hide the quick tip after a set period of time, regardless of the user's actions
34204         * (defaults to true).  Used in conjunction with autoDismissDelay.
34205         */
34206        autoDismiss : true,
34207         /**
34208         * @cfg {Number}
34209         * Delay in milliseconds before the quick tip hides when autoDismiss = true (defaults to 5000)
34210         */
34211        autoDismissDelay : 5000,
34212        /**
34213         * @cfg {Boolean} animate
34214         * True to turn on fade animation. Defaults to false (ClearType/scrollbar flicker issues in IE7).
34215         */
34216        animate : false,
34217
34218        /**
34219         * @cfg {String} title
34220         * Title text to display (defaults to '').  This can be any valid HTML markup.
34221         */
34222         title: '',
34223        /**
34224         * @cfg {String} text
34225         * Body text to display (defaults to '').  This can be any valid HTML markup.
34226         */
34227         text : '',
34228        /**
34229         * @cfg {String} cls
34230         * A CSS class to apply to the base quick tip element (defaults to '').
34231         */
34232         cls : '',
34233        /**
34234         * @cfg {Number} width
34235         * Width in pixels of the quick tip (defaults to auto).  Width will be ignored if it exceeds the bounds of
34236         * minWidth or maxWidth.
34237         */
34238         width : null,
34239
34240     /**
34241      * Initialize and enable QuickTips for first use.  This should be called once before the first attempt to access
34242      * or display QuickTips in a page.
34243      */
34244        init : function(){
34245           tm = Roo.QuickTips;
34246           cfg = tm.tagConfig;
34247           if(!inited){
34248               if(!Roo.isReady){ // allow calling of init() before onReady
34249                   Roo.onReady(Roo.QuickTips.init, Roo.QuickTips);
34250                   return;
34251               }
34252               el = new Roo.Layer({cls:"x-tip", shadow:"drop", shim: true, constrain:true, shadowOffset:4});
34253               el.fxDefaults = {stopFx: true};
34254               // maximum custom styling
34255               //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>');
34256               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>');              
34257               tipTitle = el.child('h3');
34258               tipTitle.enableDisplayMode("block");
34259               tipBody = el.child('div.x-tip-bd');
34260               tipBodyText = el.child('div.x-tip-bd-inner');
34261               //bdLeft = el.child('div.x-tip-bd-left');
34262               //bdRight = el.child('div.x-tip-bd-right');
34263               close = el.child('div.x-tip-close');
34264               close.enableDisplayMode("block");
34265               close.on("click", hide);
34266               var d = Roo.get(document);
34267               d.on("mousedown", onDown);
34268               d.on("mouseover", onOver);
34269               d.on("mouseout", onOut);
34270               d.on("mousemove", onMove);
34271               esc = d.addKeyListener(27, hide);
34272               esc.disable();
34273               if(Roo.dd.DD){
34274                   dd = el.initDD("default", null, {
34275                       onDrag : function(){
34276                           el.sync();  
34277                       }
34278                   });
34279                   dd.setHandleElId(tipTitle.id);
34280                   dd.lock();
34281               }
34282               inited = true;
34283           }
34284           this.enable(); 
34285        },
34286
34287     /**
34288      * Configures a new quick tip instance and assigns it to a target element.  The following config options
34289      * are supported:
34290      * <pre>
34291 Property    Type                   Description
34292 ----------  ---------------------  ------------------------------------------------------------------------
34293 target      Element/String/Array   An Element, id or array of ids that this quick tip should be tied to
34294      * </ul>
34295      * @param {Object} config The config object
34296      */
34297        register : function(config){
34298            var cs = config instanceof Array ? config : arguments;
34299            for(var i = 0, len = cs.length; i < len; i++) {
34300                var c = cs[i];
34301                var target = c.target;
34302                if(target){
34303                    if(target instanceof Array){
34304                        for(var j = 0, jlen = target.length; j < jlen; j++){
34305                            tagEls[target[j]] = c;
34306                        }
34307                    }else{
34308                        tagEls[typeof target == 'string' ? target : Roo.id(target)] = c;
34309                    }
34310                }
34311            }
34312        },
34313
34314     /**
34315      * Removes this quick tip from its element and destroys it.
34316      * @param {String/HTMLElement/Element} el The element from which the quick tip is to be removed.
34317      */
34318        unregister : function(el){
34319            delete tagEls[Roo.id(el)];
34320        },
34321
34322     /**
34323      * Enable this quick tip.
34324      */
34325        enable : function(){
34326            if(inited && disabled){
34327                locks.pop();
34328                if(locks.length < 1){
34329                    disabled = false;
34330                }
34331            }
34332        },
34333
34334     /**
34335      * Disable this quick tip.
34336      */
34337        disable : function(){
34338           disabled = true;
34339           clearTimeout(showProc);
34340           clearTimeout(hideProc);
34341           clearTimeout(dismissProc);
34342           if(ce){
34343               hide(true);
34344           }
34345           locks.push(1);
34346        },
34347
34348     /**
34349      * Returns true if the quick tip is enabled, else false.
34350      */
34351        isEnabled : function(){
34352             return !disabled;
34353        },
34354
34355         // private
34356        tagConfig : {
34357            namespace : "roo", // was ext?? this may break..
34358            alt_namespace : "ext",
34359            attribute : "qtip",
34360            width : "width",
34361            target : "target",
34362            title : "qtitle",
34363            hide : "hide",
34364            cls : "qclass"
34365        }
34366    };
34367 }();
34368
34369 // backwards compat
34370 Roo.QuickTips.tips = Roo.QuickTips.register;/*
34371  * Based on:
34372  * Ext JS Library 1.1.1
34373  * Copyright(c) 2006-2007, Ext JS, LLC.
34374  *
34375  * Originally Released Under LGPL - original licence link has changed is not relivant.
34376  *
34377  * Fork - LGPL
34378  * <script type="text/javascript">
34379  */
34380  
34381
34382 /**
34383  * @class Roo.tree.TreePanel
34384  * @extends Roo.data.Tree
34385  * @cfg {Roo.tree.TreeNode} root The root node
34386  * @cfg {Boolean} rootVisible false to hide the root node (defaults to true)
34387  * @cfg {Boolean} lines false to disable tree lines (defaults to true)
34388  * @cfg {Boolean} enableDD true to enable drag and drop
34389  * @cfg {Boolean} enableDrag true to enable just drag
34390  * @cfg {Boolean} enableDrop true to enable just drop
34391  * @cfg {Object} dragConfig Custom config to pass to the {@link Roo.tree.TreeDragZone} instance
34392  * @cfg {Object} dropConfig Custom config to pass to the {@link Roo.tree.TreeDropZone} instance
34393  * @cfg {String} ddGroup The DD group this TreePanel belongs to
34394  * @cfg {String} ddAppendOnly True if the tree should only allow append drops (use for trees which are sorted)
34395  * @cfg {Boolean} ddScroll true to enable YUI body scrolling
34396  * @cfg {Boolean} containerScroll true to register this container with ScrollManager
34397  * @cfg {Boolean} hlDrop false to disable node highlight on drop (defaults to the value of Roo.enableFx)
34398  * @cfg {String} hlColor The color of the node highlight (defaults to C3DAF9)
34399  * @cfg {Boolean} animate true to enable animated expand/collapse (defaults to the value of Roo.enableFx)
34400  * @cfg {Boolean} singleExpand true if only 1 node per branch may be expanded
34401  * @cfg {Boolean} selModel A tree selection model to use with this TreePanel (defaults to a {@link Roo.tree.DefaultSelectionModel})
34402  * @cfg {Roo.tree.TreeLoader} loader A TreeLoader for use with this TreePanel
34403  * @cfg {Roo.tree.TreeEditor} editor The TreeEditor to display when clicked.
34404  * @cfg {String} pathSeparator The token used to separate sub-paths in path strings (defaults to '/')
34405  * @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>
34406  * @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>
34407  * 
34408  * @constructor
34409  * @param {String/HTMLElement/Element} el The container element
34410  * @param {Object} config
34411  */
34412 Roo.tree.TreePanel = function(el, config){
34413     var root = false;
34414     var loader = false;
34415     if (config.root) {
34416         root = config.root;
34417         delete config.root;
34418     }
34419     if (config.loader) {
34420         loader = config.loader;
34421         delete config.loader;
34422     }
34423     
34424     Roo.apply(this, config);
34425     Roo.tree.TreePanel.superclass.constructor.call(this);
34426     this.el = Roo.get(el);
34427     this.el.addClass('x-tree');
34428     //console.log(root);
34429     if (root) {
34430         this.setRootNode( Roo.factory(root, Roo.tree));
34431     }
34432     if (loader) {
34433         this.loader = Roo.factory(loader, Roo.tree);
34434     }
34435    /**
34436     * Read-only. The id of the container element becomes this TreePanel's id.
34437     */
34438     this.id = this.el.id;
34439     this.addEvents({
34440         /**
34441         * @event beforeload
34442         * Fires before a node is loaded, return false to cancel
34443         * @param {Node} node The node being loaded
34444         */
34445         "beforeload" : true,
34446         /**
34447         * @event load
34448         * Fires when a node is loaded
34449         * @param {Node} node The node that was loaded
34450         */
34451         "load" : true,
34452         /**
34453         * @event textchange
34454         * Fires when the text for a node is changed
34455         * @param {Node} node The node
34456         * @param {String} text The new text
34457         * @param {String} oldText The old text
34458         */
34459         "textchange" : true,
34460         /**
34461         * @event beforeexpand
34462         * Fires before a node is expanded, return false to cancel.
34463         * @param {Node} node The node
34464         * @param {Boolean} deep
34465         * @param {Boolean} anim
34466         */
34467         "beforeexpand" : true,
34468         /**
34469         * @event beforecollapse
34470         * Fires before a node is collapsed, return false to cancel.
34471         * @param {Node} node The node
34472         * @param {Boolean} deep
34473         * @param {Boolean} anim
34474         */
34475         "beforecollapse" : true,
34476         /**
34477         * @event expand
34478         * Fires when a node is expanded
34479         * @param {Node} node The node
34480         */
34481         "expand" : true,
34482         /**
34483         * @event disabledchange
34484         * Fires when the disabled status of a node changes
34485         * @param {Node} node The node
34486         * @param {Boolean} disabled
34487         */
34488         "disabledchange" : true,
34489         /**
34490         * @event collapse
34491         * Fires when a node is collapsed
34492         * @param {Node} node The node
34493         */
34494         "collapse" : true,
34495         /**
34496         * @event beforeclick
34497         * Fires before click processing on a node. Return false to cancel the default action.
34498         * @param {Node} node The node
34499         * @param {Roo.EventObject} e The event object
34500         */
34501         "beforeclick":true,
34502         /**
34503         * @event checkchange
34504         * Fires when a node with a checkbox's checked property changes
34505         * @param {Node} this This node
34506         * @param {Boolean} checked
34507         */
34508         "checkchange":true,
34509         /**
34510         * @event click
34511         * Fires when a node is clicked
34512         * @param {Node} node The node
34513         * @param {Roo.EventObject} e The event object
34514         */
34515         "click":true,
34516         /**
34517         * @event dblclick
34518         * Fires when a node is double clicked
34519         * @param {Node} node The node
34520         * @param {Roo.EventObject} e The event object
34521         */
34522         "dblclick":true,
34523         /**
34524         * @event contextmenu
34525         * Fires when a node is right clicked
34526         * @param {Node} node The node
34527         * @param {Roo.EventObject} e The event object
34528         */
34529         "contextmenu":true,
34530         /**
34531         * @event beforechildrenrendered
34532         * Fires right before the child nodes for a node are rendered
34533         * @param {Node} node The node
34534         */
34535         "beforechildrenrendered":true,
34536         /**
34537         * @event startdrag
34538         * Fires when a node starts being dragged
34539         * @param {Roo.tree.TreePanel} this
34540         * @param {Roo.tree.TreeNode} node
34541         * @param {event} e The raw browser event
34542         */ 
34543        "startdrag" : true,
34544        /**
34545         * @event enddrag
34546         * Fires when a drag operation is complete
34547         * @param {Roo.tree.TreePanel} this
34548         * @param {Roo.tree.TreeNode} node
34549         * @param {event} e The raw browser event
34550         */
34551        "enddrag" : true,
34552        /**
34553         * @event dragdrop
34554         * Fires when a dragged node is dropped on a valid DD target
34555         * @param {Roo.tree.TreePanel} this
34556         * @param {Roo.tree.TreeNode} node
34557         * @param {DD} dd The dd it was dropped on
34558         * @param {event} e The raw browser event
34559         */
34560        "dragdrop" : true,
34561        /**
34562         * @event beforenodedrop
34563         * Fires when a DD object is dropped on a node in this tree for preprocessing. Return false to cancel the drop. The dropEvent
34564         * passed to handlers has the following properties:<br />
34565         * <ul style="padding:5px;padding-left:16px;">
34566         * <li>tree - The TreePanel</li>
34567         * <li>target - The node being targeted for the drop</li>
34568         * <li>data - The drag data from the drag source</li>
34569         * <li>point - The point of the drop - append, above or below</li>
34570         * <li>source - The drag source</li>
34571         * <li>rawEvent - Raw mouse event</li>
34572         * <li>dropNode - Drop node(s) provided by the source <b>OR</b> you can supply node(s)
34573         * to be inserted by setting them on this object.</li>
34574         * <li>cancel - Set this to true to cancel the drop.</li>
34575         * </ul>
34576         * @param {Object} dropEvent
34577         */
34578        "beforenodedrop" : true,
34579        /**
34580         * @event nodedrop
34581         * Fires after a DD object is dropped on a node in this tree. The dropEvent
34582         * passed to handlers has the following properties:<br />
34583         * <ul style="padding:5px;padding-left:16px;">
34584         * <li>tree - The TreePanel</li>
34585         * <li>target - The node being targeted for the drop</li>
34586         * <li>data - The drag data from the drag source</li>
34587         * <li>point - The point of the drop - append, above or below</li>
34588         * <li>source - The drag source</li>
34589         * <li>rawEvent - Raw mouse event</li>
34590         * <li>dropNode - Dropped node(s).</li>
34591         * </ul>
34592         * @param {Object} dropEvent
34593         */
34594        "nodedrop" : true,
34595         /**
34596         * @event nodedragover
34597         * Fires when a tree node is being targeted for a drag drop, return false to signal drop not allowed. The dragOverEvent
34598         * passed to handlers has the following properties:<br />
34599         * <ul style="padding:5px;padding-left:16px;">
34600         * <li>tree - The TreePanel</li>
34601         * <li>target - The node being targeted for the drop</li>
34602         * <li>data - The drag data from the drag source</li>
34603         * <li>point - The point of the drop - append, above or below</li>
34604         * <li>source - The drag source</li>
34605         * <li>rawEvent - Raw mouse event</li>
34606         * <li>dropNode - Drop node(s) provided by the source.</li>
34607         * <li>cancel - Set this to true to signal drop not allowed.</li>
34608         * </ul>
34609         * @param {Object} dragOverEvent
34610         */
34611        "nodedragover" : true,
34612        /**
34613         * @event appendnode
34614         * Fires when append node to the tree
34615         * @param {Roo.tree.TreePanel} this
34616         * @param {Roo.tree.TreeNode} node
34617         * @param {Number} index The index of the newly appended node
34618         */
34619        "appendnode" : true
34620         
34621     });
34622     if(this.singleExpand){
34623        this.on("beforeexpand", this.restrictExpand, this);
34624     }
34625     if (this.editor) {
34626         this.editor.tree = this;
34627         this.editor = Roo.factory(this.editor, Roo.tree);
34628     }
34629     
34630     if (this.selModel) {
34631         this.selModel = Roo.factory(this.selModel, Roo.tree);
34632     }
34633    
34634 };
34635 Roo.extend(Roo.tree.TreePanel, Roo.data.Tree, {
34636     rootVisible : true,
34637     animate: Roo.enableFx,
34638     lines : true,
34639     enableDD : false,
34640     hlDrop : Roo.enableFx,
34641   
34642     renderer: false,
34643     
34644     rendererTip: false,
34645     // private
34646     restrictExpand : function(node){
34647         var p = node.parentNode;
34648         if(p){
34649             if(p.expandedChild && p.expandedChild.parentNode == p){
34650                 p.expandedChild.collapse();
34651             }
34652             p.expandedChild = node;
34653         }
34654     },
34655
34656     // private override
34657     setRootNode : function(node){
34658         Roo.tree.TreePanel.superclass.setRootNode.call(this, node);
34659         if(!this.rootVisible){
34660             node.ui = new Roo.tree.RootTreeNodeUI(node);
34661         }
34662         return node;
34663     },
34664
34665     /**
34666      * Returns the container element for this TreePanel
34667      */
34668     getEl : function(){
34669         return this.el;
34670     },
34671
34672     /**
34673      * Returns the default TreeLoader for this TreePanel
34674      */
34675     getLoader : function(){
34676         return this.loader;
34677     },
34678
34679     /**
34680      * Expand all nodes
34681      */
34682     expandAll : function(){
34683         this.root.expand(true);
34684     },
34685
34686     /**
34687      * Collapse all nodes
34688      */
34689     collapseAll : function(){
34690         this.root.collapse(true);
34691     },
34692
34693     /**
34694      * Returns the selection model used by this TreePanel
34695      */
34696     getSelectionModel : function(){
34697         if(!this.selModel){
34698             this.selModel = new Roo.tree.DefaultSelectionModel();
34699         }
34700         return this.selModel;
34701     },
34702
34703     /**
34704      * Retrieve an array of checked nodes, or an array of a specific attribute of checked nodes (e.g. "id")
34705      * @param {String} attribute (optional) Defaults to null (return the actual nodes)
34706      * @param {TreeNode} startNode (optional) The node to start from, defaults to the root
34707      * @return {Array}
34708      */
34709     getChecked : function(a, startNode){
34710         startNode = startNode || this.root;
34711         var r = [];
34712         var f = function(){
34713             if(this.attributes.checked){
34714                 r.push(!a ? this : (a == 'id' ? this.id : this.attributes[a]));
34715             }
34716         }
34717         startNode.cascade(f);
34718         return r;
34719     },
34720
34721     /**
34722      * Expands a specified path in this TreePanel. A path can be retrieved from a node with {@link Roo.data.Node#getPath}
34723      * @param {String} path
34724      * @param {String} attr (optional) The attribute used in the path (see {@link Roo.data.Node#getPath} for more info)
34725      * @param {Function} callback (optional) The callback to call when the expand is complete. The callback will be called with
34726      * (bSuccess, oLastNode) where bSuccess is if the expand was successful and oLastNode is the last node that was expanded.
34727      */
34728     expandPath : function(path, attr, callback){
34729         attr = attr || "id";
34730         var keys = path.split(this.pathSeparator);
34731         var curNode = this.root;
34732         if(curNode.attributes[attr] != keys[1]){ // invalid root
34733             if(callback){
34734                 callback(false, null);
34735             }
34736             return;
34737         }
34738         var index = 1;
34739         var f = function(){
34740             if(++index == keys.length){
34741                 if(callback){
34742                     callback(true, curNode);
34743                 }
34744                 return;
34745             }
34746             var c = curNode.findChild(attr, keys[index]);
34747             if(!c){
34748                 if(callback){
34749                     callback(false, curNode);
34750                 }
34751                 return;
34752             }
34753             curNode = c;
34754             c.expand(false, false, f);
34755         };
34756         curNode.expand(false, false, f);
34757     },
34758
34759     /**
34760      * Selects the node in this tree at the specified path. A path can be retrieved from a node with {@link Roo.data.Node#getPath}
34761      * @param {String} path
34762      * @param {String} attr (optional) The attribute used in the path (see {@link Roo.data.Node#getPath} for more info)
34763      * @param {Function} callback (optional) The callback to call when the selection is complete. The callback will be called with
34764      * (bSuccess, oSelNode) where bSuccess is if the selection was successful and oSelNode is the selected node.
34765      */
34766     selectPath : function(path, attr, callback){
34767         attr = attr || "id";
34768         var keys = path.split(this.pathSeparator);
34769         var v = keys.pop();
34770         if(keys.length > 0){
34771             var f = function(success, node){
34772                 if(success && node){
34773                     var n = node.findChild(attr, v);
34774                     if(n){
34775                         n.select();
34776                         if(callback){
34777                             callback(true, n);
34778                         }
34779                     }else if(callback){
34780                         callback(false, n);
34781                     }
34782                 }else{
34783                     if(callback){
34784                         callback(false, n);
34785                     }
34786                 }
34787             };
34788             this.expandPath(keys.join(this.pathSeparator), attr, f);
34789         }else{
34790             this.root.select();
34791             if(callback){
34792                 callback(true, this.root);
34793             }
34794         }
34795     },
34796
34797     getTreeEl : function(){
34798         return this.el;
34799     },
34800
34801     /**
34802      * Trigger rendering of this TreePanel
34803      */
34804     render : function(){
34805         if (this.innerCt) {
34806             return this; // stop it rendering more than once!!
34807         }
34808         
34809         this.innerCt = this.el.createChild({tag:"ul",
34810                cls:"x-tree-root-ct " +
34811                (this.lines ? "x-tree-lines" : "x-tree-no-lines")});
34812
34813         if(this.containerScroll){
34814             Roo.dd.ScrollManager.register(this.el);
34815         }
34816         if((this.enableDD || this.enableDrop) && !this.dropZone){
34817            /**
34818             * The dropZone used by this tree if drop is enabled
34819             * @type Roo.tree.TreeDropZone
34820             */
34821              this.dropZone = new Roo.tree.TreeDropZone(this, this.dropConfig || {
34822                ddGroup: this.ddGroup || "TreeDD", appendOnly: this.ddAppendOnly === true
34823            });
34824         }
34825         if((this.enableDD || this.enableDrag) && !this.dragZone){
34826            /**
34827             * The dragZone used by this tree if drag is enabled
34828             * @type Roo.tree.TreeDragZone
34829             */
34830             this.dragZone = new Roo.tree.TreeDragZone(this, this.dragConfig || {
34831                ddGroup: this.ddGroup || "TreeDD",
34832                scroll: this.ddScroll
34833            });
34834         }
34835         this.getSelectionModel().init(this);
34836         if (!this.root) {
34837             Roo.log("ROOT not set in tree");
34838             return this;
34839         }
34840         this.root.render();
34841         if(!this.rootVisible){
34842             this.root.renderChildren();
34843         }
34844         return this;
34845     }
34846 });/*
34847  * Based on:
34848  * Ext JS Library 1.1.1
34849  * Copyright(c) 2006-2007, Ext JS, LLC.
34850  *
34851  * Originally Released Under LGPL - original licence link has changed is not relivant.
34852  *
34853  * Fork - LGPL
34854  * <script type="text/javascript">
34855  */
34856  
34857
34858 /**
34859  * @class Roo.tree.DefaultSelectionModel
34860  * @extends Roo.util.Observable
34861  * The default single selection for a TreePanel.
34862  * @param {Object} cfg Configuration
34863  */
34864 Roo.tree.DefaultSelectionModel = function(cfg){
34865    this.selNode = null;
34866    
34867    
34868    
34869    this.addEvents({
34870        /**
34871         * @event selectionchange
34872         * Fires when the selected node changes
34873         * @param {DefaultSelectionModel} this
34874         * @param {TreeNode} node the new selection
34875         */
34876        "selectionchange" : true,
34877
34878        /**
34879         * @event beforeselect
34880         * Fires before the selected node changes, return false to cancel the change
34881         * @param {DefaultSelectionModel} this
34882         * @param {TreeNode} node the new selection
34883         * @param {TreeNode} node the old selection
34884         */
34885        "beforeselect" : true
34886    });
34887    
34888     Roo.tree.DefaultSelectionModel.superclass.constructor.call(this,cfg);
34889 };
34890
34891 Roo.extend(Roo.tree.DefaultSelectionModel, Roo.util.Observable, {
34892     init : function(tree){
34893         this.tree = tree;
34894         tree.getTreeEl().on("keydown", this.onKeyDown, this);
34895         tree.on("click", this.onNodeClick, this);
34896     },
34897     
34898     onNodeClick : function(node, e){
34899         if (e.ctrlKey && this.selNode == node)  {
34900             this.unselect(node);
34901             return;
34902         }
34903         this.select(node);
34904     },
34905     
34906     /**
34907      * Select a node.
34908      * @param {TreeNode} node The node to select
34909      * @return {TreeNode} The selected node
34910      */
34911     select : function(node){
34912         var last = this.selNode;
34913         if(last != node && this.fireEvent('beforeselect', this, node, last) !== false){
34914             if(last){
34915                 last.ui.onSelectedChange(false);
34916             }
34917             this.selNode = node;
34918             node.ui.onSelectedChange(true);
34919             this.fireEvent("selectionchange", this, node, last);
34920         }
34921         return node;
34922     },
34923     
34924     /**
34925      * Deselect a node.
34926      * @param {TreeNode} node The node to unselect
34927      */
34928     unselect : function(node){
34929         if(this.selNode == node){
34930             this.clearSelections();
34931         }    
34932     },
34933     
34934     /**
34935      * Clear all selections
34936      */
34937     clearSelections : function(){
34938         var n = this.selNode;
34939         if(n){
34940             n.ui.onSelectedChange(false);
34941             this.selNode = null;
34942             this.fireEvent("selectionchange", this, null);
34943         }
34944         return n;
34945     },
34946     
34947     /**
34948      * Get the selected node
34949      * @return {TreeNode} The selected node
34950      */
34951     getSelectedNode : function(){
34952         return this.selNode;    
34953     },
34954     
34955     /**
34956      * Returns true if the node is selected
34957      * @param {TreeNode} node The node to check
34958      * @return {Boolean}
34959      */
34960     isSelected : function(node){
34961         return this.selNode == node;  
34962     },
34963
34964     /**
34965      * Selects the node above the selected node in the tree, intelligently walking the nodes
34966      * @return TreeNode The new selection
34967      */
34968     selectPrevious : function(){
34969         var s = this.selNode || this.lastSelNode;
34970         if(!s){
34971             return null;
34972         }
34973         var ps = s.previousSibling;
34974         if(ps){
34975             if(!ps.isExpanded() || ps.childNodes.length < 1){
34976                 return this.select(ps);
34977             } else{
34978                 var lc = ps.lastChild;
34979                 while(lc && lc.isExpanded() && lc.childNodes.length > 0){
34980                     lc = lc.lastChild;
34981                 }
34982                 return this.select(lc);
34983             }
34984         } else if(s.parentNode && (this.tree.rootVisible || !s.parentNode.isRoot)){
34985             return this.select(s.parentNode);
34986         }
34987         return null;
34988     },
34989
34990     /**
34991      * Selects the node above the selected node in the tree, intelligently walking the nodes
34992      * @return TreeNode The new selection
34993      */
34994     selectNext : function(){
34995         var s = this.selNode || this.lastSelNode;
34996         if(!s){
34997             return null;
34998         }
34999         if(s.firstChild && s.isExpanded()){
35000              return this.select(s.firstChild);
35001          }else if(s.nextSibling){
35002              return this.select(s.nextSibling);
35003          }else if(s.parentNode){
35004             var newS = null;
35005             s.parentNode.bubble(function(){
35006                 if(this.nextSibling){
35007                     newS = this.getOwnerTree().selModel.select(this.nextSibling);
35008                     return false;
35009                 }
35010             });
35011             return newS;
35012          }
35013         return null;
35014     },
35015
35016     onKeyDown : function(e){
35017         var s = this.selNode || this.lastSelNode;
35018         // undesirable, but required
35019         var sm = this;
35020         if(!s){
35021             return;
35022         }
35023         var k = e.getKey();
35024         switch(k){
35025              case e.DOWN:
35026                  e.stopEvent();
35027                  this.selectNext();
35028              break;
35029              case e.UP:
35030                  e.stopEvent();
35031                  this.selectPrevious();
35032              break;
35033              case e.RIGHT:
35034                  e.preventDefault();
35035                  if(s.hasChildNodes()){
35036                      if(!s.isExpanded()){
35037                          s.expand();
35038                      }else if(s.firstChild){
35039                          this.select(s.firstChild, e);
35040                      }
35041                  }
35042              break;
35043              case e.LEFT:
35044                  e.preventDefault();
35045                  if(s.hasChildNodes() && s.isExpanded()){
35046                      s.collapse();
35047                  }else if(s.parentNode && (this.tree.rootVisible || s.parentNode != this.tree.getRootNode())){
35048                      this.select(s.parentNode, e);
35049                  }
35050              break;
35051         };
35052     }
35053 });
35054
35055 /**
35056  * @class Roo.tree.MultiSelectionModel
35057  * @extends Roo.util.Observable
35058  * Multi selection for a TreePanel.
35059  * @param {Object} cfg Configuration
35060  */
35061 Roo.tree.MultiSelectionModel = function(){
35062    this.selNodes = [];
35063    this.selMap = {};
35064    this.addEvents({
35065        /**
35066         * @event selectionchange
35067         * Fires when the selected nodes change
35068         * @param {MultiSelectionModel} this
35069         * @param {Array} nodes Array of the selected nodes
35070         */
35071        "selectionchange" : true
35072    });
35073    Roo.tree.MultiSelectionModel.superclass.constructor.call(this,cfg);
35074    
35075 };
35076
35077 Roo.extend(Roo.tree.MultiSelectionModel, Roo.util.Observable, {
35078     init : function(tree){
35079         this.tree = tree;
35080         tree.getTreeEl().on("keydown", this.onKeyDown, this);
35081         tree.on("click", this.onNodeClick, this);
35082     },
35083     
35084     onNodeClick : function(node, e){
35085         this.select(node, e, e.ctrlKey);
35086     },
35087     
35088     /**
35089      * Select a node.
35090      * @param {TreeNode} node The node to select
35091      * @param {EventObject} e (optional) An event associated with the selection
35092      * @param {Boolean} keepExisting True to retain existing selections
35093      * @return {TreeNode} The selected node
35094      */
35095     select : function(node, e, keepExisting){
35096         if(keepExisting !== true){
35097             this.clearSelections(true);
35098         }
35099         if(this.isSelected(node)){
35100             this.lastSelNode = node;
35101             return node;
35102         }
35103         this.selNodes.push(node);
35104         this.selMap[node.id] = node;
35105         this.lastSelNode = node;
35106         node.ui.onSelectedChange(true);
35107         this.fireEvent("selectionchange", this, this.selNodes);
35108         return node;
35109     },
35110     
35111     /**
35112      * Deselect a node.
35113      * @param {TreeNode} node The node to unselect
35114      */
35115     unselect : function(node){
35116         if(this.selMap[node.id]){
35117             node.ui.onSelectedChange(false);
35118             var sn = this.selNodes;
35119             var index = -1;
35120             if(sn.indexOf){
35121                 index = sn.indexOf(node);
35122             }else{
35123                 for(var i = 0, len = sn.length; i < len; i++){
35124                     if(sn[i] == node){
35125                         index = i;
35126                         break;
35127                     }
35128                 }
35129             }
35130             if(index != -1){
35131                 this.selNodes.splice(index, 1);
35132             }
35133             delete this.selMap[node.id];
35134             this.fireEvent("selectionchange", this, this.selNodes);
35135         }
35136     },
35137     
35138     /**
35139      * Clear all selections
35140      */
35141     clearSelections : function(suppressEvent){
35142         var sn = this.selNodes;
35143         if(sn.length > 0){
35144             for(var i = 0, len = sn.length; i < len; i++){
35145                 sn[i].ui.onSelectedChange(false);
35146             }
35147             this.selNodes = [];
35148             this.selMap = {};
35149             if(suppressEvent !== true){
35150                 this.fireEvent("selectionchange", this, this.selNodes);
35151             }
35152         }
35153     },
35154     
35155     /**
35156      * Returns true if the node is selected
35157      * @param {TreeNode} node The node to check
35158      * @return {Boolean}
35159      */
35160     isSelected : function(node){
35161         return this.selMap[node.id] ? true : false;  
35162     },
35163     
35164     /**
35165      * Returns an array of the selected nodes
35166      * @return {Array}
35167      */
35168     getSelectedNodes : function(){
35169         return this.selNodes;    
35170     },
35171
35172     onKeyDown : Roo.tree.DefaultSelectionModel.prototype.onKeyDown,
35173
35174     selectNext : Roo.tree.DefaultSelectionModel.prototype.selectNext,
35175
35176     selectPrevious : Roo.tree.DefaultSelectionModel.prototype.selectPrevious
35177 });/*
35178  * Based on:
35179  * Ext JS Library 1.1.1
35180  * Copyright(c) 2006-2007, Ext JS, LLC.
35181  *
35182  * Originally Released Under LGPL - original licence link has changed is not relivant.
35183  *
35184  * Fork - LGPL
35185  * <script type="text/javascript">
35186  */
35187  
35188 /**
35189  * @class Roo.tree.TreeNode
35190  * @extends Roo.data.Node
35191  * @cfg {String} text The text for this node
35192  * @cfg {Boolean} expanded true to start the node expanded
35193  * @cfg {Boolean} allowDrag false to make this node undraggable if DD is on (defaults to true)
35194  * @cfg {Boolean} allowDrop false if this node cannot be drop on
35195  * @cfg {Boolean} disabled true to start the node disabled
35196  * @cfg {String} icon The path to an icon for the node. The preferred way to do this
35197  *    is to use the cls or iconCls attributes and add the icon via a CSS background image.
35198  * @cfg {String} cls A css class to be added to the node
35199  * @cfg {String} iconCls A css class to be added to the nodes icon element for applying css background images
35200  * @cfg {String} href URL of the link used for the node (defaults to #)
35201  * @cfg {String} hrefTarget target frame for the link
35202  * @cfg {String} qtip An Ext QuickTip for the node
35203  * @cfg {String} qtipCfg An Ext QuickTip config for the node (used instead of qtip)
35204  * @cfg {Boolean} singleClickExpand True for single click expand on this node
35205  * @cfg {Function} uiProvider A UI <b>class</b> to use for this node (defaults to Roo.tree.TreeNodeUI)
35206  * @cfg {Boolean} checked True to render a checked checkbox for this node, false to render an unchecked checkbox
35207  * (defaults to undefined with no checkbox rendered)
35208  * @constructor
35209  * @param {Object/String} attributes The attributes/config for the node or just a string with the text for the node
35210  */
35211 Roo.tree.TreeNode = function(attributes){
35212     attributes = attributes || {};
35213     if(typeof attributes == "string"){
35214         attributes = {text: attributes};
35215     }
35216     this.childrenRendered = false;
35217     this.rendered = false;
35218     Roo.tree.TreeNode.superclass.constructor.call(this, attributes);
35219     this.expanded = attributes.expanded === true;
35220     this.isTarget = attributes.isTarget !== false;
35221     this.draggable = attributes.draggable !== false && attributes.allowDrag !== false;
35222     this.allowChildren = attributes.allowChildren !== false && attributes.allowDrop !== false;
35223
35224     /**
35225      * Read-only. The text for this node. To change it use setText().
35226      * @type String
35227      */
35228     this.text = attributes.text;
35229     /**
35230      * True if this node is disabled.
35231      * @type Boolean
35232      */
35233     this.disabled = attributes.disabled === true;
35234
35235     this.addEvents({
35236         /**
35237         * @event textchange
35238         * Fires when the text for this node is changed
35239         * @param {Node} this This node
35240         * @param {String} text The new text
35241         * @param {String} oldText The old text
35242         */
35243         "textchange" : true,
35244         /**
35245         * @event beforeexpand
35246         * Fires before this node is expanded, return false to cancel.
35247         * @param {Node} this This node
35248         * @param {Boolean} deep
35249         * @param {Boolean} anim
35250         */
35251         "beforeexpand" : true,
35252         /**
35253         * @event beforecollapse
35254         * Fires before this node is collapsed, return false to cancel.
35255         * @param {Node} this This node
35256         * @param {Boolean} deep
35257         * @param {Boolean} anim
35258         */
35259         "beforecollapse" : true,
35260         /**
35261         * @event expand
35262         * Fires when this node is expanded
35263         * @param {Node} this This node
35264         */
35265         "expand" : true,
35266         /**
35267         * @event disabledchange
35268         * Fires when the disabled status of this node changes
35269         * @param {Node} this This node
35270         * @param {Boolean} disabled
35271         */
35272         "disabledchange" : true,
35273         /**
35274         * @event collapse
35275         * Fires when this node is collapsed
35276         * @param {Node} this This node
35277         */
35278         "collapse" : true,
35279         /**
35280         * @event beforeclick
35281         * Fires before click processing. Return false to cancel the default action.
35282         * @param {Node} this This node
35283         * @param {Roo.EventObject} e The event object
35284         */
35285         "beforeclick":true,
35286         /**
35287         * @event checkchange
35288         * Fires when a node with a checkbox's checked property changes
35289         * @param {Node} this This node
35290         * @param {Boolean} checked
35291         */
35292         "checkchange":true,
35293         /**
35294         * @event click
35295         * Fires when this node is clicked
35296         * @param {Node} this This node
35297         * @param {Roo.EventObject} e The event object
35298         */
35299         "click":true,
35300         /**
35301         * @event dblclick
35302         * Fires when this node is double clicked
35303         * @param {Node} this This node
35304         * @param {Roo.EventObject} e The event object
35305         */
35306         "dblclick":true,
35307         /**
35308         * @event contextmenu
35309         * Fires when this node is right clicked
35310         * @param {Node} this This node
35311         * @param {Roo.EventObject} e The event object
35312         */
35313         "contextmenu":true,
35314         /**
35315         * @event beforechildrenrendered
35316         * Fires right before the child nodes for this node are rendered
35317         * @param {Node} this This node
35318         */
35319         "beforechildrenrendered":true
35320     });
35321
35322     var uiClass = this.attributes.uiProvider || Roo.tree.TreeNodeUI;
35323
35324     /**
35325      * Read-only. The UI for this node
35326      * @type TreeNodeUI
35327      */
35328     this.ui = new uiClass(this);
35329     
35330     // finally support items[]
35331     if (typeof(this.attributes.items) == 'undefined' || !this.attributes.items) {
35332         return;
35333     }
35334     
35335     
35336     Roo.each(this.attributes.items, function(c) {
35337         this.appendChild(Roo.factory(c,Roo.Tree));
35338     }, this);
35339     delete this.attributes.items;
35340     
35341     
35342     
35343 };
35344 Roo.extend(Roo.tree.TreeNode, Roo.data.Node, {
35345     preventHScroll: true,
35346     /**
35347      * Returns true if this node is expanded
35348      * @return {Boolean}
35349      */
35350     isExpanded : function(){
35351         return this.expanded;
35352     },
35353
35354     /**
35355      * Returns the UI object for this node
35356      * @return {TreeNodeUI}
35357      */
35358     getUI : function(){
35359         return this.ui;
35360     },
35361
35362     // private override
35363     setFirstChild : function(node){
35364         var of = this.firstChild;
35365         Roo.tree.TreeNode.superclass.setFirstChild.call(this, node);
35366         if(this.childrenRendered && of && node != of){
35367             of.renderIndent(true, true);
35368         }
35369         if(this.rendered){
35370             this.renderIndent(true, true);
35371         }
35372     },
35373
35374     // private override
35375     setLastChild : function(node){
35376         var ol = this.lastChild;
35377         Roo.tree.TreeNode.superclass.setLastChild.call(this, node);
35378         if(this.childrenRendered && ol && node != ol){
35379             ol.renderIndent(true, true);
35380         }
35381         if(this.rendered){
35382             this.renderIndent(true, true);
35383         }
35384     },
35385
35386     // these methods are overridden to provide lazy rendering support
35387     // private override
35388     appendChild : function()
35389     {
35390         var node = Roo.tree.TreeNode.superclass.appendChild.apply(this, arguments);
35391         if(node && this.childrenRendered){
35392             node.render();
35393         }
35394         this.ui.updateExpandIcon();
35395         return node;
35396     },
35397
35398     // private override
35399     removeChild : function(node){
35400         this.ownerTree.getSelectionModel().unselect(node);
35401         Roo.tree.TreeNode.superclass.removeChild.apply(this, arguments);
35402         // if it's been rendered remove dom node
35403         if(this.childrenRendered){
35404             node.ui.remove();
35405         }
35406         if(this.childNodes.length < 1){
35407             this.collapse(false, false);
35408         }else{
35409             this.ui.updateExpandIcon();
35410         }
35411         if(!this.firstChild) {
35412             this.childrenRendered = false;
35413         }
35414         return node;
35415     },
35416
35417     // private override
35418     insertBefore : function(node, refNode){
35419         var newNode = Roo.tree.TreeNode.superclass.insertBefore.apply(this, arguments);
35420         if(newNode && refNode && this.childrenRendered){
35421             node.render();
35422         }
35423         this.ui.updateExpandIcon();
35424         return newNode;
35425     },
35426
35427     /**
35428      * Sets the text for this node
35429      * @param {String} text
35430      */
35431     setText : function(text){
35432         var oldText = this.text;
35433         this.text = text;
35434         this.attributes.text = text;
35435         if(this.rendered){ // event without subscribing
35436             this.ui.onTextChange(this, text, oldText);
35437         }
35438         this.fireEvent("textchange", this, text, oldText);
35439     },
35440
35441     /**
35442      * Triggers selection of this node
35443      */
35444     select : function(){
35445         this.getOwnerTree().getSelectionModel().select(this);
35446     },
35447
35448     /**
35449      * Triggers deselection of this node
35450      */
35451     unselect : function(){
35452         this.getOwnerTree().getSelectionModel().unselect(this);
35453     },
35454
35455     /**
35456      * Returns true if this node is selected
35457      * @return {Boolean}
35458      */
35459     isSelected : function(){
35460         return this.getOwnerTree().getSelectionModel().isSelected(this);
35461     },
35462
35463     /**
35464      * Expand this node.
35465      * @param {Boolean} deep (optional) True to expand all children as well
35466      * @param {Boolean} anim (optional) false to cancel the default animation
35467      * @param {Function} callback (optional) A callback to be called when
35468      * expanding this node completes (does not wait for deep expand to complete).
35469      * Called with 1 parameter, this node.
35470      */
35471     expand : function(deep, anim, callback){
35472         if(!this.expanded){
35473             if(this.fireEvent("beforeexpand", this, deep, anim) === false){
35474                 return;
35475             }
35476             if(!this.childrenRendered){
35477                 this.renderChildren();
35478             }
35479             this.expanded = true;
35480             
35481             if(!this.isHiddenRoot() && (this.getOwnerTree() && this.getOwnerTree().animate && anim !== false) || anim){
35482                 this.ui.animExpand(function(){
35483                     this.fireEvent("expand", this);
35484                     if(typeof callback == "function"){
35485                         callback(this);
35486                     }
35487                     if(deep === true){
35488                         this.expandChildNodes(true);
35489                     }
35490                 }.createDelegate(this));
35491                 return;
35492             }else{
35493                 this.ui.expand();
35494                 this.fireEvent("expand", this);
35495                 if(typeof callback == "function"){
35496                     callback(this);
35497                 }
35498             }
35499         }else{
35500            if(typeof callback == "function"){
35501                callback(this);
35502            }
35503         }
35504         if(deep === true){
35505             this.expandChildNodes(true);
35506         }
35507     },
35508
35509     isHiddenRoot : function(){
35510         return this.isRoot && !this.getOwnerTree().rootVisible;
35511     },
35512
35513     /**
35514      * Collapse this node.
35515      * @param {Boolean} deep (optional) True to collapse all children as well
35516      * @param {Boolean} anim (optional) false to cancel the default animation
35517      */
35518     collapse : function(deep, anim){
35519         if(this.expanded && !this.isHiddenRoot()){
35520             if(this.fireEvent("beforecollapse", this, deep, anim) === false){
35521                 return;
35522             }
35523             this.expanded = false;
35524             if((this.getOwnerTree().animate && anim !== false) || anim){
35525                 this.ui.animCollapse(function(){
35526                     this.fireEvent("collapse", this);
35527                     if(deep === true){
35528                         this.collapseChildNodes(true);
35529                     }
35530                 }.createDelegate(this));
35531                 return;
35532             }else{
35533                 this.ui.collapse();
35534                 this.fireEvent("collapse", this);
35535             }
35536         }
35537         if(deep === true){
35538             var cs = this.childNodes;
35539             for(var i = 0, len = cs.length; i < len; i++) {
35540                 cs[i].collapse(true, false);
35541             }
35542         }
35543     },
35544
35545     // private
35546     delayedExpand : function(delay){
35547         if(!this.expandProcId){
35548             this.expandProcId = this.expand.defer(delay, this);
35549         }
35550     },
35551
35552     // private
35553     cancelExpand : function(){
35554         if(this.expandProcId){
35555             clearTimeout(this.expandProcId);
35556         }
35557         this.expandProcId = false;
35558     },
35559
35560     /**
35561      * Toggles expanded/collapsed state of the node
35562      */
35563     toggle : function(){
35564         if(this.expanded){
35565             this.collapse();
35566         }else{
35567             this.expand();
35568         }
35569     },
35570
35571     /**
35572      * Ensures all parent nodes are expanded
35573      */
35574     ensureVisible : function(callback){
35575         var tree = this.getOwnerTree();
35576         tree.expandPath(this.parentNode.getPath(), false, function(){
35577             tree.getTreeEl().scrollChildIntoView(this.ui.anchor);
35578             Roo.callback(callback);
35579         }.createDelegate(this));
35580     },
35581
35582     /**
35583      * Expand all child nodes
35584      * @param {Boolean} deep (optional) true if the child nodes should also expand their child nodes
35585      */
35586     expandChildNodes : function(deep){
35587         var cs = this.childNodes;
35588         for(var i = 0, len = cs.length; i < len; i++) {
35589                 cs[i].expand(deep);
35590         }
35591     },
35592
35593     /**
35594      * Collapse all child nodes
35595      * @param {Boolean} deep (optional) true if the child nodes should also collapse their child nodes
35596      */
35597     collapseChildNodes : function(deep){
35598         var cs = this.childNodes;
35599         for(var i = 0, len = cs.length; i < len; i++) {
35600                 cs[i].collapse(deep);
35601         }
35602     },
35603
35604     /**
35605      * Disables this node
35606      */
35607     disable : function(){
35608         this.disabled = true;
35609         this.unselect();
35610         if(this.rendered && this.ui.onDisableChange){ // event without subscribing
35611             this.ui.onDisableChange(this, true);
35612         }
35613         this.fireEvent("disabledchange", this, true);
35614     },
35615
35616     /**
35617      * Enables this node
35618      */
35619     enable : function(){
35620         this.disabled = false;
35621         if(this.rendered && this.ui.onDisableChange){ // event without subscribing
35622             this.ui.onDisableChange(this, false);
35623         }
35624         this.fireEvent("disabledchange", this, false);
35625     },
35626
35627     // private
35628     renderChildren : function(suppressEvent){
35629         if(suppressEvent !== false){
35630             this.fireEvent("beforechildrenrendered", this);
35631         }
35632         var cs = this.childNodes;
35633         for(var i = 0, len = cs.length; i < len; i++){
35634             cs[i].render(true);
35635         }
35636         this.childrenRendered = true;
35637     },
35638
35639     // private
35640     sort : function(fn, scope){
35641         Roo.tree.TreeNode.superclass.sort.apply(this, arguments);
35642         if(this.childrenRendered){
35643             var cs = this.childNodes;
35644             for(var i = 0, len = cs.length; i < len; i++){
35645                 cs[i].render(true);
35646             }
35647         }
35648     },
35649
35650     // private
35651     render : function(bulkRender){
35652         this.ui.render(bulkRender);
35653         if(!this.rendered){
35654             this.rendered = true;
35655             if(this.expanded){
35656                 this.expanded = false;
35657                 this.expand(false, false);
35658             }
35659         }
35660     },
35661
35662     // private
35663     renderIndent : function(deep, refresh){
35664         if(refresh){
35665             this.ui.childIndent = null;
35666         }
35667         this.ui.renderIndent();
35668         if(deep === true && this.childrenRendered){
35669             var cs = this.childNodes;
35670             for(var i = 0, len = cs.length; i < len; i++){
35671                 cs[i].renderIndent(true, refresh);
35672             }
35673         }
35674     }
35675 });/*
35676  * Based on:
35677  * Ext JS Library 1.1.1
35678  * Copyright(c) 2006-2007, Ext JS, LLC.
35679  *
35680  * Originally Released Under LGPL - original licence link has changed is not relivant.
35681  *
35682  * Fork - LGPL
35683  * <script type="text/javascript">
35684  */
35685  
35686 /**
35687  * @class Roo.tree.AsyncTreeNode
35688  * @extends Roo.tree.TreeNode
35689  * @cfg {TreeLoader} loader A TreeLoader to be used by this node (defaults to the loader defined on the tree)
35690  * @constructor
35691  * @param {Object/String} attributes The attributes/config for the node or just a string with the text for the node 
35692  */
35693  Roo.tree.AsyncTreeNode = function(config){
35694     this.loaded = false;
35695     this.loading = false;
35696     Roo.tree.AsyncTreeNode.superclass.constructor.apply(this, arguments);
35697     /**
35698     * @event beforeload
35699     * Fires before this node is loaded, return false to cancel
35700     * @param {Node} this This node
35701     */
35702     this.addEvents({'beforeload':true, 'load': true});
35703     /**
35704     * @event load
35705     * Fires when this node is loaded
35706     * @param {Node} this This node
35707     */
35708     /**
35709      * The loader used by this node (defaults to using the tree's defined loader)
35710      * @type TreeLoader
35711      * @property loader
35712      */
35713 };
35714 Roo.extend(Roo.tree.AsyncTreeNode, Roo.tree.TreeNode, {
35715     expand : function(deep, anim, callback){
35716         if(this.loading){ // if an async load is already running, waiting til it's done
35717             var timer;
35718             var f = function(){
35719                 if(!this.loading){ // done loading
35720                     clearInterval(timer);
35721                     this.expand(deep, anim, callback);
35722                 }
35723             }.createDelegate(this);
35724             timer = setInterval(f, 200);
35725             return;
35726         }
35727         if(!this.loaded){
35728             if(this.fireEvent("beforeload", this) === false){
35729                 return;
35730             }
35731             this.loading = true;
35732             this.ui.beforeLoad(this);
35733             var loader = this.loader || this.attributes.loader || this.getOwnerTree().getLoader();
35734             if(loader){
35735                 loader.load(this, this.loadComplete.createDelegate(this, [deep, anim, callback]));
35736                 return;
35737             }
35738         }
35739         Roo.tree.AsyncTreeNode.superclass.expand.call(this, deep, anim, callback);
35740     },
35741     
35742     /**
35743      * Returns true if this node is currently loading
35744      * @return {Boolean}
35745      */
35746     isLoading : function(){
35747         return this.loading;  
35748     },
35749     
35750     loadComplete : function(deep, anim, callback){
35751         this.loading = false;
35752         this.loaded = true;
35753         this.ui.afterLoad(this);
35754         this.fireEvent("load", this);
35755         this.expand(deep, anim, callback);
35756     },
35757     
35758     /**
35759      * Returns true if this node has been loaded
35760      * @return {Boolean}
35761      */
35762     isLoaded : function(){
35763         return this.loaded;
35764     },
35765     
35766     hasChildNodes : function(){
35767         if(!this.isLeaf() && !this.loaded){
35768             return true;
35769         }else{
35770             return Roo.tree.AsyncTreeNode.superclass.hasChildNodes.call(this);
35771         }
35772     },
35773
35774     /**
35775      * Trigger a reload for this node
35776      * @param {Function} callback
35777      */
35778     reload : function(callback){
35779         this.collapse(false, false);
35780         while(this.firstChild){
35781             this.removeChild(this.firstChild);
35782         }
35783         this.childrenRendered = false;
35784         this.loaded = false;
35785         if(this.isHiddenRoot()){
35786             this.expanded = false;
35787         }
35788         this.expand(false, false, callback);
35789     }
35790 });/*
35791  * Based on:
35792  * Ext JS Library 1.1.1
35793  * Copyright(c) 2006-2007, Ext JS, LLC.
35794  *
35795  * Originally Released Under LGPL - original licence link has changed is not relivant.
35796  *
35797  * Fork - LGPL
35798  * <script type="text/javascript">
35799  */
35800  
35801 /**
35802  * @class Roo.tree.TreeNodeUI
35803  * @constructor
35804  * @param {Object} node The node to render
35805  * The TreeNode UI implementation is separate from the
35806  * tree implementation. Unless you are customizing the tree UI,
35807  * you should never have to use this directly.
35808  */
35809 Roo.tree.TreeNodeUI = function(node){
35810     this.node = node;
35811     this.rendered = false;
35812     this.animating = false;
35813     this.emptyIcon = Roo.BLANK_IMAGE_URL;
35814 };
35815
35816 Roo.tree.TreeNodeUI.prototype = {
35817     removeChild : function(node){
35818         if(this.rendered){
35819             this.ctNode.removeChild(node.ui.getEl());
35820         }
35821     },
35822
35823     beforeLoad : function(){
35824          this.addClass("x-tree-node-loading");
35825     },
35826
35827     afterLoad : function(){
35828          this.removeClass("x-tree-node-loading");
35829     },
35830
35831     onTextChange : function(node, text, oldText){
35832         if(this.rendered){
35833             this.textNode.innerHTML = text;
35834         }
35835     },
35836
35837     onDisableChange : function(node, state){
35838         this.disabled = state;
35839         if(state){
35840             this.addClass("x-tree-node-disabled");
35841         }else{
35842             this.removeClass("x-tree-node-disabled");
35843         }
35844     },
35845
35846     onSelectedChange : function(state){
35847         if(state){
35848             this.focus();
35849             this.addClass("x-tree-selected");
35850         }else{
35851             //this.blur();
35852             this.removeClass("x-tree-selected");
35853         }
35854     },
35855
35856     onMove : function(tree, node, oldParent, newParent, index, refNode){
35857         this.childIndent = null;
35858         if(this.rendered){
35859             var targetNode = newParent.ui.getContainer();
35860             if(!targetNode){//target not rendered
35861                 this.holder = document.createElement("div");
35862                 this.holder.appendChild(this.wrap);
35863                 return;
35864             }
35865             var insertBefore = refNode ? refNode.ui.getEl() : null;
35866             if(insertBefore){
35867                 targetNode.insertBefore(this.wrap, insertBefore);
35868             }else{
35869                 targetNode.appendChild(this.wrap);
35870             }
35871             this.node.renderIndent(true);
35872         }
35873     },
35874
35875     addClass : function(cls){
35876         if(this.elNode){
35877             Roo.fly(this.elNode).addClass(cls);
35878         }
35879     },
35880
35881     removeClass : function(cls){
35882         if(this.elNode){
35883             Roo.fly(this.elNode).removeClass(cls);
35884         }
35885     },
35886
35887     remove : function(){
35888         if(this.rendered){
35889             this.holder = document.createElement("div");
35890             this.holder.appendChild(this.wrap);
35891         }
35892     },
35893
35894     fireEvent : function(){
35895         return this.node.fireEvent.apply(this.node, arguments);
35896     },
35897
35898     initEvents : function(){
35899         this.node.on("move", this.onMove, this);
35900         var E = Roo.EventManager;
35901         var a = this.anchor;
35902
35903         var el = Roo.fly(a, '_treeui');
35904
35905         if(Roo.isOpera){ // opera render bug ignores the CSS
35906             el.setStyle("text-decoration", "none");
35907         }
35908
35909         el.on("click", this.onClick, this);
35910         el.on("dblclick", this.onDblClick, this);
35911
35912         if(this.checkbox){
35913             Roo.EventManager.on(this.checkbox,
35914                     Roo.isIE ? 'click' : 'change', this.onCheckChange, this);
35915         }
35916
35917         el.on("contextmenu", this.onContextMenu, this);
35918
35919         var icon = Roo.fly(this.iconNode);
35920         icon.on("click", this.onClick, this);
35921         icon.on("dblclick", this.onDblClick, this);
35922         icon.on("contextmenu", this.onContextMenu, this);
35923         E.on(this.ecNode, "click", this.ecClick, this, true);
35924
35925         if(this.node.disabled){
35926             this.addClass("x-tree-node-disabled");
35927         }
35928         if(this.node.hidden){
35929             this.addClass("x-tree-node-disabled");
35930         }
35931         var ot = this.node.getOwnerTree();
35932         var dd = ot ? (ot.enableDD || ot.enableDrag || ot.enableDrop) : false;
35933         if(dd && (!this.node.isRoot || ot.rootVisible)){
35934             Roo.dd.Registry.register(this.elNode, {
35935                 node: this.node,
35936                 handles: this.getDDHandles(),
35937                 isHandle: false
35938             });
35939         }
35940     },
35941
35942     getDDHandles : function(){
35943         return [this.iconNode, this.textNode];
35944     },
35945
35946     hide : function(){
35947         if(this.rendered){
35948             this.wrap.style.display = "none";
35949         }
35950     },
35951
35952     show : function(){
35953         if(this.rendered){
35954             this.wrap.style.display = "";
35955         }
35956     },
35957
35958     onContextMenu : function(e){
35959         if (this.node.hasListener("contextmenu") || this.node.getOwnerTree().hasListener("contextmenu")) {
35960             e.preventDefault();
35961             this.focus();
35962             this.fireEvent("contextmenu", this.node, e);
35963         }
35964     },
35965
35966     onClick : function(e){
35967         if(this.dropping){
35968             e.stopEvent();
35969             return;
35970         }
35971         if(this.fireEvent("beforeclick", this.node, e) !== false){
35972             if(!this.disabled && this.node.attributes.href){
35973                 this.fireEvent("click", this.node, e);
35974                 return;
35975             }
35976             e.preventDefault();
35977             if(this.disabled){
35978                 return;
35979             }
35980
35981             if(this.node.attributes.singleClickExpand && !this.animating && this.node.hasChildNodes()){
35982                 this.node.toggle();
35983             }
35984
35985             this.fireEvent("click", this.node, e);
35986         }else{
35987             e.stopEvent();
35988         }
35989     },
35990
35991     onDblClick : function(e){
35992         e.preventDefault();
35993         if(this.disabled){
35994             return;
35995         }
35996         if(this.checkbox){
35997             this.toggleCheck();
35998         }
35999         if(!this.animating && this.node.hasChildNodes()){
36000             this.node.toggle();
36001         }
36002         this.fireEvent("dblclick", this.node, e);
36003     },
36004
36005     onCheckChange : function(){
36006         var checked = this.checkbox.checked;
36007         this.node.attributes.checked = checked;
36008         this.fireEvent('checkchange', this.node, checked);
36009     },
36010
36011     ecClick : function(e){
36012         if(!this.animating && this.node.hasChildNodes()){
36013             this.node.toggle();
36014         }
36015     },
36016
36017     startDrop : function(){
36018         this.dropping = true;
36019     },
36020
36021     // delayed drop so the click event doesn't get fired on a drop
36022     endDrop : function(){
36023        setTimeout(function(){
36024            this.dropping = false;
36025        }.createDelegate(this), 50);
36026     },
36027
36028     expand : function(){
36029         this.updateExpandIcon();
36030         this.ctNode.style.display = "";
36031     },
36032
36033     focus : function(){
36034         if(!this.node.preventHScroll){
36035             try{this.anchor.focus();
36036             }catch(e){}
36037         }else if(!Roo.isIE){
36038             try{
36039                 var noscroll = this.node.getOwnerTree().getTreeEl().dom;
36040                 var l = noscroll.scrollLeft;
36041                 this.anchor.focus();
36042                 noscroll.scrollLeft = l;
36043             }catch(e){}
36044         }
36045     },
36046
36047     toggleCheck : function(value){
36048         var cb = this.checkbox;
36049         if(cb){
36050             cb.checked = (value === undefined ? !cb.checked : value);
36051         }
36052     },
36053
36054     blur : function(){
36055         try{
36056             this.anchor.blur();
36057         }catch(e){}
36058     },
36059
36060     animExpand : function(callback){
36061         var ct = Roo.get(this.ctNode);
36062         ct.stopFx();
36063         if(!this.node.hasChildNodes()){
36064             this.updateExpandIcon();
36065             this.ctNode.style.display = "";
36066             Roo.callback(callback);
36067             return;
36068         }
36069         this.animating = true;
36070         this.updateExpandIcon();
36071
36072         ct.slideIn('t', {
36073            callback : function(){
36074                this.animating = false;
36075                Roo.callback(callback);
36076             },
36077             scope: this,
36078             duration: this.node.ownerTree.duration || .25
36079         });
36080     },
36081
36082     highlight : function(){
36083         var tree = this.node.getOwnerTree();
36084         Roo.fly(this.wrap).highlight(
36085             tree.hlColor || "C3DAF9",
36086             {endColor: tree.hlBaseColor}
36087         );
36088     },
36089
36090     collapse : function(){
36091         this.updateExpandIcon();
36092         this.ctNode.style.display = "none";
36093     },
36094
36095     animCollapse : function(callback){
36096         var ct = Roo.get(this.ctNode);
36097         ct.enableDisplayMode('block');
36098         ct.stopFx();
36099
36100         this.animating = true;
36101         this.updateExpandIcon();
36102
36103         ct.slideOut('t', {
36104             callback : function(){
36105                this.animating = false;
36106                Roo.callback(callback);
36107             },
36108             scope: this,
36109             duration: this.node.ownerTree.duration || .25
36110         });
36111     },
36112
36113     getContainer : function(){
36114         return this.ctNode;
36115     },
36116
36117     getEl : function(){
36118         return this.wrap;
36119     },
36120
36121     appendDDGhost : function(ghostNode){
36122         ghostNode.appendChild(this.elNode.cloneNode(true));
36123     },
36124
36125     getDDRepairXY : function(){
36126         return Roo.lib.Dom.getXY(this.iconNode);
36127     },
36128
36129     onRender : function(){
36130         this.render();
36131     },
36132
36133     render : function(bulkRender){
36134         var n = this.node, a = n.attributes;
36135         var targetNode = n.parentNode ?
36136               n.parentNode.ui.getContainer() : n.ownerTree.innerCt.dom;
36137
36138         if(!this.rendered){
36139             this.rendered = true;
36140
36141             this.renderElements(n, a, targetNode, bulkRender);
36142
36143             if(a.qtip){
36144                if(this.textNode.setAttributeNS){
36145                    this.textNode.setAttributeNS("ext", "qtip", a.qtip);
36146                    if(a.qtipTitle){
36147                        this.textNode.setAttributeNS("ext", "qtitle", a.qtipTitle);
36148                    }
36149                }else{
36150                    this.textNode.setAttribute("ext:qtip", a.qtip);
36151                    if(a.qtipTitle){
36152                        this.textNode.setAttribute("ext:qtitle", a.qtipTitle);
36153                    }
36154                }
36155             }else if(a.qtipCfg){
36156                 a.qtipCfg.target = Roo.id(this.textNode);
36157                 Roo.QuickTips.register(a.qtipCfg);
36158             }
36159             this.initEvents();
36160             if(!this.node.expanded){
36161                 this.updateExpandIcon();
36162             }
36163         }else{
36164             if(bulkRender === true) {
36165                 targetNode.appendChild(this.wrap);
36166             }
36167         }
36168     },
36169
36170     renderElements : function(n, a, targetNode, bulkRender)
36171     {
36172         // add some indent caching, this helps performance when rendering a large tree
36173         this.indentMarkup = n.parentNode ? n.parentNode.ui.getChildIndent() : '';
36174         var t = n.getOwnerTree();
36175         var txt = t && t.renderer ? t.renderer(n.attributes) : Roo.util.Format.htmlEncode(n.text);
36176         if (typeof(n.attributes.html) != 'undefined') {
36177             txt = n.attributes.html;
36178         }
36179         var tip = t && t.rendererTip ? t.rendererTip(n.attributes) : txt;
36180         var cb = typeof a.checked == 'boolean';
36181         var href = a.href ? a.href : Roo.isGecko ? "" : "#";
36182         var buf = ['<li class="x-tree-node"><div class="x-tree-node-el ', a.cls,'">',
36183             '<span class="x-tree-node-indent">',this.indentMarkup,"</span>",
36184             '<img src="', this.emptyIcon, '" class="x-tree-ec-icon" />',
36185             '<img src="', a.icon || this.emptyIcon, '" class="x-tree-node-icon',(a.icon ? " x-tree-node-inline-icon" : ""),(a.iconCls ? " "+a.iconCls : ""),'" unselectable="on" />',
36186             cb ? ('<input class="x-tree-node-cb" type="checkbox" ' + (a.checked ? 'checked="checked" />' : ' />')) : '',
36187             '<a hidefocus="on" href="',href,'" tabIndex="1" ',
36188              a.hrefTarget ? ' target="'+a.hrefTarget+'"' : "", 
36189                 '><span unselectable="on" qtip="' , tip ,'">',txt,"</span></a></div>",
36190             '<ul class="x-tree-node-ct" style="display:none;"></ul>',
36191             "</li>"];
36192
36193         if(bulkRender !== true && n.nextSibling && n.nextSibling.ui.getEl()){
36194             this.wrap = Roo.DomHelper.insertHtml("beforeBegin",
36195                                 n.nextSibling.ui.getEl(), buf.join(""));
36196         }else{
36197             this.wrap = Roo.DomHelper.insertHtml("beforeEnd", targetNode, buf.join(""));
36198         }
36199
36200         this.elNode = this.wrap.childNodes[0];
36201         this.ctNode = this.wrap.childNodes[1];
36202         var cs = this.elNode.childNodes;
36203         this.indentNode = cs[0];
36204         this.ecNode = cs[1];
36205         this.iconNode = cs[2];
36206         var index = 3;
36207         if(cb){
36208             this.checkbox = cs[3];
36209             index++;
36210         }
36211         this.anchor = cs[index];
36212         this.textNode = cs[index].firstChild;
36213     },
36214
36215     getAnchor : function(){
36216         return this.anchor;
36217     },
36218
36219     getTextEl : function(){
36220         return this.textNode;
36221     },
36222
36223     getIconEl : function(){
36224         return this.iconNode;
36225     },
36226
36227     isChecked : function(){
36228         return this.checkbox ? this.checkbox.checked : false;
36229     },
36230
36231     updateExpandIcon : function(){
36232         if(this.rendered){
36233             var n = this.node, c1, c2;
36234             var cls = n.isLast() ? "x-tree-elbow-end" : "x-tree-elbow";
36235             var hasChild = n.hasChildNodes();
36236             if(hasChild){
36237                 if(n.expanded){
36238                     cls += "-minus";
36239                     c1 = "x-tree-node-collapsed";
36240                     c2 = "x-tree-node-expanded";
36241                 }else{
36242                     cls += "-plus";
36243                     c1 = "x-tree-node-expanded";
36244                     c2 = "x-tree-node-collapsed";
36245                 }
36246                 if(this.wasLeaf){
36247                     this.removeClass("x-tree-node-leaf");
36248                     this.wasLeaf = false;
36249                 }
36250                 if(this.c1 != c1 || this.c2 != c2){
36251                     Roo.fly(this.elNode).replaceClass(c1, c2);
36252                     this.c1 = c1; this.c2 = c2;
36253                 }
36254             }else{
36255                 // this changes non-leafs into leafs if they have no children.
36256                 // it's not very rational behaviour..
36257                 
36258                 if(!this.wasLeaf && this.node.leaf){
36259                     Roo.fly(this.elNode).replaceClass("x-tree-node-expanded", "x-tree-node-leaf");
36260                     delete this.c1;
36261                     delete this.c2;
36262                     this.wasLeaf = true;
36263                 }
36264             }
36265             var ecc = "x-tree-ec-icon "+cls;
36266             if(this.ecc != ecc){
36267                 this.ecNode.className = ecc;
36268                 this.ecc = ecc;
36269             }
36270         }
36271     },
36272
36273     getChildIndent : function(){
36274         if(!this.childIndent){
36275             var buf = [];
36276             var p = this.node;
36277             while(p){
36278                 if(!p.isRoot || (p.isRoot && p.ownerTree.rootVisible)){
36279                     if(!p.isLast()) {
36280                         buf.unshift('<img src="'+this.emptyIcon+'" class="x-tree-elbow-line" />');
36281                     } else {
36282                         buf.unshift('<img src="'+this.emptyIcon+'" class="x-tree-icon" />');
36283                     }
36284                 }
36285                 p = p.parentNode;
36286             }
36287             this.childIndent = buf.join("");
36288         }
36289         return this.childIndent;
36290     },
36291
36292     renderIndent : function(){
36293         if(this.rendered){
36294             var indent = "";
36295             var p = this.node.parentNode;
36296             if(p){
36297                 indent = p.ui.getChildIndent();
36298             }
36299             if(this.indentMarkup != indent){ // don't rerender if not required
36300                 this.indentNode.innerHTML = indent;
36301                 this.indentMarkup = indent;
36302             }
36303             this.updateExpandIcon();
36304         }
36305     }
36306 };
36307
36308 Roo.tree.RootTreeNodeUI = function(){
36309     Roo.tree.RootTreeNodeUI.superclass.constructor.apply(this, arguments);
36310 };
36311 Roo.extend(Roo.tree.RootTreeNodeUI, Roo.tree.TreeNodeUI, {
36312     render : function(){
36313         if(!this.rendered){
36314             var targetNode = this.node.ownerTree.innerCt.dom;
36315             this.node.expanded = true;
36316             targetNode.innerHTML = '<div class="x-tree-root-node"></div>';
36317             this.wrap = this.ctNode = targetNode.firstChild;
36318         }
36319     },
36320     collapse : function(){
36321     },
36322     expand : function(){
36323     }
36324 });/*
36325  * Based on:
36326  * Ext JS Library 1.1.1
36327  * Copyright(c) 2006-2007, Ext JS, LLC.
36328  *
36329  * Originally Released Under LGPL - original licence link has changed is not relivant.
36330  *
36331  * Fork - LGPL
36332  * <script type="text/javascript">
36333  */
36334 /**
36335  * @class Roo.tree.TreeLoader
36336  * @extends Roo.util.Observable
36337  * A TreeLoader provides for lazy loading of an {@link Roo.tree.TreeNode}'s child
36338  * nodes from a specified URL. The response must be a javascript Array definition
36339  * who's elements are node definition objects. eg:
36340  * <pre><code>
36341 {  success : true,
36342    data :      [
36343    
36344     { 'id': 1, 'text': 'A folder Node', 'leaf': false },
36345     { 'id': 2, 'text': 'A leaf Node', 'leaf': true }
36346     ]
36347 }
36348
36349
36350 </code></pre>
36351  * <br><br>
36352  * The old style respose with just an array is still supported, but not recommended.
36353  * <br><br>
36354  *
36355  * A server request is sent, and child nodes are loaded only when a node is expanded.
36356  * The loading node's id is passed to the server under the parameter name "node" to
36357  * enable the server to produce the correct child nodes.
36358  * <br><br>
36359  * To pass extra parameters, an event handler may be attached to the "beforeload"
36360  * event, and the parameters specified in the TreeLoader's baseParams property:
36361  * <pre><code>
36362     myTreeLoader.on("beforeload", function(treeLoader, node) {
36363         this.baseParams.category = node.attributes.category;
36364     }, this);
36365     
36366 </code></pre>
36367  *
36368  * This would pass an HTTP parameter called "category" to the server containing
36369  * the value of the Node's "category" attribute.
36370  * @constructor
36371  * Creates a new Treeloader.
36372  * @param {Object} config A config object containing config properties.
36373  */
36374 Roo.tree.TreeLoader = function(config){
36375     this.baseParams = {};
36376     this.requestMethod = "POST";
36377     Roo.apply(this, config);
36378
36379     this.addEvents({
36380     
36381         /**
36382          * @event beforeload
36383          * Fires before a network request is made to retrieve the Json text which specifies a node's children.
36384          * @param {Object} This TreeLoader object.
36385          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
36386          * @param {Object} callback The callback function specified in the {@link #load} call.
36387          */
36388         beforeload : true,
36389         /**
36390          * @event load
36391          * Fires when the node has been successfuly loaded.
36392          * @param {Object} This TreeLoader object.
36393          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
36394          * @param {Object} response The response object containing the data from the server.
36395          */
36396         load : true,
36397         /**
36398          * @event loadexception
36399          * Fires if the network request failed.
36400          * @param {Object} This TreeLoader object.
36401          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
36402          * @param {Object} response The response object containing the data from the server.
36403          */
36404         loadexception : true,
36405         /**
36406          * @event create
36407          * Fires before a node is created, enabling you to return custom Node types 
36408          * @param {Object} This TreeLoader object.
36409          * @param {Object} attr - the data returned from the AJAX call (modify it to suit)
36410          */
36411         create : true
36412     });
36413
36414     Roo.tree.TreeLoader.superclass.constructor.call(this);
36415 };
36416
36417 Roo.extend(Roo.tree.TreeLoader, Roo.util.Observable, {
36418     /**
36419     * @cfg {String} dataUrl The URL from which to request a Json string which
36420     * specifies an array of node definition object representing the child nodes
36421     * to be loaded.
36422     */
36423     /**
36424     * @cfg {String} requestMethod either GET or POST
36425     * defaults to POST (due to BC)
36426     * to be loaded.
36427     */
36428     /**
36429     * @cfg {Object} baseParams (optional) An object containing properties which
36430     * specify HTTP parameters to be passed to each request for child nodes.
36431     */
36432     /**
36433     * @cfg {Object} baseAttrs (optional) An object containing attributes to be added to all nodes
36434     * created by this loader. If the attributes sent by the server have an attribute in this object,
36435     * they take priority.
36436     */
36437     /**
36438     * @cfg {Object} uiProviders (optional) An object containing properties which
36439     * 
36440     * DEPRECATED - use 'create' event handler to modify attributes - which affect creation.
36441     * specify custom {@link Roo.tree.TreeNodeUI} implementations. If the optional
36442     * <i>uiProvider</i> attribute of a returned child node is a string rather
36443     * than a reference to a TreeNodeUI implementation, this that string value
36444     * is used as a property name in the uiProviders object. You can define the provider named
36445     * 'default' , and this will be used for all nodes (if no uiProvider is delivered by the node data)
36446     */
36447     uiProviders : {},
36448
36449     /**
36450     * @cfg {Boolean} clearOnLoad (optional) Default to true. Remove previously existing
36451     * child nodes before loading.
36452     */
36453     clearOnLoad : true,
36454
36455     /**
36456     * @cfg {String} root (optional) Default to false. Use this to read data from an object 
36457     * property on loading, rather than expecting an array. (eg. more compatible to a standard
36458     * Grid query { data : [ .....] }
36459     */
36460     
36461     root : false,
36462      /**
36463     * @cfg {String} queryParam (optional) 
36464     * Name of the query as it will be passed on the querystring (defaults to 'node')
36465     * eg. the request will be ?node=[id]
36466     */
36467     
36468     
36469     queryParam: false,
36470     
36471     /**
36472      * Load an {@link Roo.tree.TreeNode} from the URL specified in the constructor.
36473      * This is called automatically when a node is expanded, but may be used to reload
36474      * a node (or append new children if the {@link #clearOnLoad} option is false.)
36475      * @param {Roo.tree.TreeNode} node
36476      * @param {Function} callback
36477      */
36478     load : function(node, callback){
36479         if(this.clearOnLoad){
36480             while(node.firstChild){
36481                 node.removeChild(node.firstChild);
36482             }
36483         }
36484         if(node.attributes.children){ // preloaded json children
36485             var cs = node.attributes.children;
36486             for(var i = 0, len = cs.length; i < len; i++){
36487                 node.appendChild(this.createNode(cs[i]));
36488             }
36489             if(typeof callback == "function"){
36490                 callback();
36491             }
36492         }else if(this.dataUrl){
36493             this.requestData(node, callback);
36494         }
36495     },
36496
36497     getParams: function(node){
36498         var buf = [], bp = this.baseParams;
36499         for(var key in bp){
36500             if(typeof bp[key] != "function"){
36501                 buf.push(encodeURIComponent(key), "=", encodeURIComponent(bp[key]), "&");
36502             }
36503         }
36504         var n = this.queryParam === false ? 'node' : this.queryParam;
36505         buf.push(n + "=", encodeURIComponent(node.id));
36506         return buf.join("");
36507     },
36508
36509     requestData : function(node, callback){
36510         if(this.fireEvent("beforeload", this, node, callback) !== false){
36511             this.transId = Roo.Ajax.request({
36512                 method:this.requestMethod,
36513                 url: this.dataUrl||this.url,
36514                 success: this.handleResponse,
36515                 failure: this.handleFailure,
36516                 scope: this,
36517                 argument: {callback: callback, node: node},
36518                 params: this.getParams(node)
36519             });
36520         }else{
36521             // if the load is cancelled, make sure we notify
36522             // the node that we are done
36523             if(typeof callback == "function"){
36524                 callback();
36525             }
36526         }
36527     },
36528
36529     isLoading : function(){
36530         return this.transId ? true : false;
36531     },
36532
36533     abort : function(){
36534         if(this.isLoading()){
36535             Roo.Ajax.abort(this.transId);
36536         }
36537     },
36538
36539     // private
36540     createNode : function(attr)
36541     {
36542         // apply baseAttrs, nice idea Corey!
36543         if(this.baseAttrs){
36544             Roo.applyIf(attr, this.baseAttrs);
36545         }
36546         if(this.applyLoader !== false){
36547             attr.loader = this;
36548         }
36549         // uiProvider = depreciated..
36550         
36551         if(typeof(attr.uiProvider) == 'string'){
36552            attr.uiProvider = this.uiProviders[attr.uiProvider] || 
36553                 /**  eval:var:attr */ eval(attr.uiProvider);
36554         }
36555         if(typeof(this.uiProviders['default']) != 'undefined') {
36556             attr.uiProvider = this.uiProviders['default'];
36557         }
36558         
36559         this.fireEvent('create', this, attr);
36560         
36561         attr.leaf  = typeof(attr.leaf) == 'string' ? attr.leaf * 1 : attr.leaf;
36562         return(attr.leaf ?
36563                         new Roo.tree.TreeNode(attr) :
36564                         new Roo.tree.AsyncTreeNode(attr));
36565     },
36566
36567     processResponse : function(response, node, callback)
36568     {
36569         var json = response.responseText;
36570         try {
36571             
36572             var o = Roo.decode(json);
36573             
36574             if (this.root === false && typeof(o.success) != undefined) {
36575                 this.root = 'data'; // the default behaviour for list like data..
36576                 }
36577                 
36578             if (this.root !== false &&  !o.success) {
36579                 // it's a failure condition.
36580                 var a = response.argument;
36581                 this.fireEvent("loadexception", this, a.node, response);
36582                 Roo.log("Load failed - should have a handler really");
36583                 return;
36584             }
36585             
36586             
36587             
36588             if (this.root !== false) {
36589                  o = o[this.root];
36590             }
36591             
36592             for(var i = 0, len = o.length; i < len; i++){
36593                 var n = this.createNode(o[i]);
36594                 if(n){
36595                     node.appendChild(n);
36596                 }
36597             }
36598             if(typeof callback == "function"){
36599                 callback(this, node);
36600             }
36601         }catch(e){
36602             this.handleFailure(response);
36603         }
36604     },
36605
36606     handleResponse : function(response){
36607         this.transId = false;
36608         var a = response.argument;
36609         this.processResponse(response, a.node, a.callback);
36610         this.fireEvent("load", this, a.node, response);
36611     },
36612
36613     handleFailure : function(response)
36614     {
36615         // should handle failure better..
36616         this.transId = false;
36617         var a = response.argument;
36618         this.fireEvent("loadexception", this, a.node, response);
36619         if(typeof a.callback == "function"){
36620             a.callback(this, a.node);
36621         }
36622     }
36623 });/*
36624  * Based on:
36625  * Ext JS Library 1.1.1
36626  * Copyright(c) 2006-2007, Ext JS, LLC.
36627  *
36628  * Originally Released Under LGPL - original licence link has changed is not relivant.
36629  *
36630  * Fork - LGPL
36631  * <script type="text/javascript">
36632  */
36633
36634 /**
36635 * @class Roo.tree.TreeFilter
36636 * Note this class is experimental and doesn't update the indent (lines) or expand collapse icons of the nodes
36637 * @param {TreePanel} tree
36638 * @param {Object} config (optional)
36639  */
36640 Roo.tree.TreeFilter = function(tree, config){
36641     this.tree = tree;
36642     this.filtered = {};
36643     Roo.apply(this, config);
36644 };
36645
36646 Roo.tree.TreeFilter.prototype = {
36647     clearBlank:false,
36648     reverse:false,
36649     autoClear:false,
36650     remove:false,
36651
36652      /**
36653      * Filter the data by a specific attribute.
36654      * @param {String/RegExp} value Either string that the attribute value
36655      * should start with or a RegExp to test against the attribute
36656      * @param {String} attr (optional) The attribute passed in your node's attributes collection. Defaults to "text".
36657      * @param {TreeNode} startNode (optional) The node to start the filter at.
36658      */
36659     filter : function(value, attr, startNode){
36660         attr = attr || "text";
36661         var f;
36662         if(typeof value == "string"){
36663             var vlen = value.length;
36664             // auto clear empty filter
36665             if(vlen == 0 && this.clearBlank){
36666                 this.clear();
36667                 return;
36668             }
36669             value = value.toLowerCase();
36670             f = function(n){
36671                 return n.attributes[attr].substr(0, vlen).toLowerCase() == value;
36672             };
36673         }else if(value.exec){ // regex?
36674             f = function(n){
36675                 return value.test(n.attributes[attr]);
36676             };
36677         }else{
36678             throw 'Illegal filter type, must be string or regex';
36679         }
36680         this.filterBy(f, null, startNode);
36681         },
36682
36683     /**
36684      * Filter by a function. The passed function will be called with each
36685      * node in the tree (or from the startNode). If the function returns true, the node is kept
36686      * otherwise it is filtered. If a node is filtered, its children are also filtered.
36687      * @param {Function} fn The filter function
36688      * @param {Object} scope (optional) The scope of the function (defaults to the current node)
36689      */
36690     filterBy : function(fn, scope, startNode){
36691         startNode = startNode || this.tree.root;
36692         if(this.autoClear){
36693             this.clear();
36694         }
36695         var af = this.filtered, rv = this.reverse;
36696         var f = function(n){
36697             if(n == startNode){
36698                 return true;
36699             }
36700             if(af[n.id]){
36701                 return false;
36702             }
36703             var m = fn.call(scope || n, n);
36704             if(!m || rv){
36705                 af[n.id] = n;
36706                 n.ui.hide();
36707                 return false;
36708             }
36709             return true;
36710         };
36711         startNode.cascade(f);
36712         if(this.remove){
36713            for(var id in af){
36714                if(typeof id != "function"){
36715                    var n = af[id];
36716                    if(n && n.parentNode){
36717                        n.parentNode.removeChild(n);
36718                    }
36719                }
36720            }
36721         }
36722     },
36723
36724     /**
36725      * Clears the current filter. Note: with the "remove" option
36726      * set a filter cannot be cleared.
36727      */
36728     clear : function(){
36729         var t = this.tree;
36730         var af = this.filtered;
36731         for(var id in af){
36732             if(typeof id != "function"){
36733                 var n = af[id];
36734                 if(n){
36735                     n.ui.show();
36736                 }
36737             }
36738         }
36739         this.filtered = {};
36740     }
36741 };
36742 /*
36743  * Based on:
36744  * Ext JS Library 1.1.1
36745  * Copyright(c) 2006-2007, Ext JS, LLC.
36746  *
36747  * Originally Released Under LGPL - original licence link has changed is not relivant.
36748  *
36749  * Fork - LGPL
36750  * <script type="text/javascript">
36751  */
36752  
36753
36754 /**
36755  * @class Roo.tree.TreeSorter
36756  * Provides sorting of nodes in a TreePanel
36757  * 
36758  * @cfg {Boolean} folderSort True to sort leaf nodes under non leaf nodes
36759  * @cfg {String} property The named attribute on the node to sort by (defaults to text)
36760  * @cfg {String} dir The direction to sort (asc or desc) (defaults to asc)
36761  * @cfg {String} leafAttr The attribute used to determine leaf nodes in folder sort (defaults to "leaf")
36762  * @cfg {Boolean} caseSensitive true for case sensitive sort (defaults to false)
36763  * @cfg {Function} sortType A custom "casting" function used to convert node values before sorting
36764  * @constructor
36765  * @param {TreePanel} tree
36766  * @param {Object} config
36767  */
36768 Roo.tree.TreeSorter = function(tree, config){
36769     Roo.apply(this, config);
36770     tree.on("beforechildrenrendered", this.doSort, this);
36771     tree.on("append", this.updateSort, this);
36772     tree.on("insert", this.updateSort, this);
36773     
36774     var dsc = this.dir && this.dir.toLowerCase() == "desc";
36775     var p = this.property || "text";
36776     var sortType = this.sortType;
36777     var fs = this.folderSort;
36778     var cs = this.caseSensitive === true;
36779     var leafAttr = this.leafAttr || 'leaf';
36780
36781     this.sortFn = function(n1, n2){
36782         if(fs){
36783             if(n1.attributes[leafAttr] && !n2.attributes[leafAttr]){
36784                 return 1;
36785             }
36786             if(!n1.attributes[leafAttr] && n2.attributes[leafAttr]){
36787                 return -1;
36788             }
36789         }
36790         var v1 = sortType ? sortType(n1) : (cs ? n1.attributes[p] : n1.attributes[p].toUpperCase());
36791         var v2 = sortType ? sortType(n2) : (cs ? n2.attributes[p] : n2.attributes[p].toUpperCase());
36792         if(v1 < v2){
36793                         return dsc ? +1 : -1;
36794                 }else if(v1 > v2){
36795                         return dsc ? -1 : +1;
36796         }else{
36797                 return 0;
36798         }
36799     };
36800 };
36801
36802 Roo.tree.TreeSorter.prototype = {
36803     doSort : function(node){
36804         node.sort(this.sortFn);
36805     },
36806     
36807     compareNodes : function(n1, n2){
36808         return (n1.text.toUpperCase() > n2.text.toUpperCase() ? 1 : -1);
36809     },
36810     
36811     updateSort : function(tree, node){
36812         if(node.childrenRendered){
36813             this.doSort.defer(1, this, [node]);
36814         }
36815     }
36816 };/*
36817  * Based on:
36818  * Ext JS Library 1.1.1
36819  * Copyright(c) 2006-2007, Ext JS, LLC.
36820  *
36821  * Originally Released Under LGPL - original licence link has changed is not relivant.
36822  *
36823  * Fork - LGPL
36824  * <script type="text/javascript">
36825  */
36826
36827 if(Roo.dd.DropZone){
36828     
36829 Roo.tree.TreeDropZone = function(tree, config){
36830     this.allowParentInsert = false;
36831     this.allowContainerDrop = false;
36832     this.appendOnly = false;
36833     Roo.tree.TreeDropZone.superclass.constructor.call(this, tree.innerCt, config);
36834     this.tree = tree;
36835     this.lastInsertClass = "x-tree-no-status";
36836     this.dragOverData = {};
36837 };
36838
36839 Roo.extend(Roo.tree.TreeDropZone, Roo.dd.DropZone, {
36840     ddGroup : "TreeDD",
36841     scroll:  true,
36842     
36843     expandDelay : 1000,
36844     
36845     expandNode : function(node){
36846         if(node.hasChildNodes() && !node.isExpanded()){
36847             node.expand(false, null, this.triggerCacheRefresh.createDelegate(this));
36848         }
36849     },
36850     
36851     queueExpand : function(node){
36852         this.expandProcId = this.expandNode.defer(this.expandDelay, this, [node]);
36853     },
36854     
36855     cancelExpand : function(){
36856         if(this.expandProcId){
36857             clearTimeout(this.expandProcId);
36858             this.expandProcId = false;
36859         }
36860     },
36861     
36862     isValidDropPoint : function(n, pt, dd, e, data){
36863         if(!n || !data){ return false; }
36864         var targetNode = n.node;
36865         var dropNode = data.node;
36866         // default drop rules
36867         if(!(targetNode && targetNode.isTarget && pt)){
36868             return false;
36869         }
36870         if(pt == "append" && targetNode.allowChildren === false){
36871             return false;
36872         }
36873         if((pt == "above" || pt == "below") && (targetNode.parentNode && targetNode.parentNode.allowChildren === false)){
36874             return false;
36875         }
36876         if(dropNode && (targetNode == dropNode || dropNode.contains(targetNode))){
36877             return false;
36878         }
36879         // reuse the object
36880         var overEvent = this.dragOverData;
36881         overEvent.tree = this.tree;
36882         overEvent.target = targetNode;
36883         overEvent.data = data;
36884         overEvent.point = pt;
36885         overEvent.source = dd;
36886         overEvent.rawEvent = e;
36887         overEvent.dropNode = dropNode;
36888         overEvent.cancel = false;  
36889         var result = this.tree.fireEvent("nodedragover", overEvent);
36890         return overEvent.cancel === false && result !== false;
36891     },
36892     
36893     getDropPoint : function(e, n, dd)
36894     {
36895         var tn = n.node;
36896         if(tn.isRoot){
36897             return tn.allowChildren !== false ? "append" : false; // always append for root
36898         }
36899         var dragEl = n.ddel;
36900         var t = Roo.lib.Dom.getY(dragEl), b = t + dragEl.offsetHeight;
36901         var y = Roo.lib.Event.getPageY(e);
36902         //var noAppend = tn.allowChildren === false || tn.isLeaf();
36903         
36904         // we may drop nodes anywhere, as long as allowChildren has not been set to false..
36905         var noAppend = tn.allowChildren === false;
36906         if(this.appendOnly || tn.parentNode.allowChildren === false){
36907             return noAppend ? false : "append";
36908         }
36909         var noBelow = false;
36910         if(!this.allowParentInsert){
36911             noBelow = tn.hasChildNodes() && tn.isExpanded();
36912         }
36913         var q = (b - t) / (noAppend ? 2 : 3);
36914         if(y >= t && y < (t + q)){
36915             return "above";
36916         }else if(!noBelow && (noAppend || y >= b-q && y <= b)){
36917             return "below";
36918         }else{
36919             return "append";
36920         }
36921     },
36922     
36923     onNodeEnter : function(n, dd, e, data)
36924     {
36925         this.cancelExpand();
36926     },
36927     
36928     onNodeOver : function(n, dd, e, data)
36929     {
36930        
36931         var pt = this.getDropPoint(e, n, dd);
36932         var node = n.node;
36933         
36934         // auto node expand check
36935         if(!this.expandProcId && pt == "append" && node.hasChildNodes() && !n.node.isExpanded()){
36936             this.queueExpand(node);
36937         }else if(pt != "append"){
36938             this.cancelExpand();
36939         }
36940         
36941         // set the insert point style on the target node
36942         var returnCls = this.dropNotAllowed;
36943         if(this.isValidDropPoint(n, pt, dd, e, data)){
36944            if(pt){
36945                var el = n.ddel;
36946                var cls;
36947                if(pt == "above"){
36948                    returnCls = n.node.isFirst() ? "x-tree-drop-ok-above" : "x-tree-drop-ok-between";
36949                    cls = "x-tree-drag-insert-above";
36950                }else if(pt == "below"){
36951                    returnCls = n.node.isLast() ? "x-tree-drop-ok-below" : "x-tree-drop-ok-between";
36952                    cls = "x-tree-drag-insert-below";
36953                }else{
36954                    returnCls = "x-tree-drop-ok-append";
36955                    cls = "x-tree-drag-append";
36956                }
36957                if(this.lastInsertClass != cls){
36958                    Roo.fly(el).replaceClass(this.lastInsertClass, cls);
36959                    this.lastInsertClass = cls;
36960                }
36961            }
36962        }
36963        return returnCls;
36964     },
36965     
36966     onNodeOut : function(n, dd, e, data){
36967         
36968         this.cancelExpand();
36969         this.removeDropIndicators(n);
36970     },
36971     
36972     onNodeDrop : function(n, dd, e, data){
36973         var point = this.getDropPoint(e, n, dd);
36974         var targetNode = n.node;
36975         targetNode.ui.startDrop();
36976         if(!this.isValidDropPoint(n, point, dd, e, data)){
36977             targetNode.ui.endDrop();
36978             return false;
36979         }
36980         // first try to find the drop node
36981         var dropNode = data.node || (dd.getTreeNode ? dd.getTreeNode(data, targetNode, point, e) : null);
36982         var dropEvent = {
36983             tree : this.tree,
36984             target: targetNode,
36985             data: data,
36986             point: point,
36987             source: dd,
36988             rawEvent: e,
36989             dropNode: dropNode,
36990             cancel: !dropNode   
36991         };
36992         var retval = this.tree.fireEvent("beforenodedrop", dropEvent);
36993         if(retval === false || dropEvent.cancel === true || !dropEvent.dropNode){
36994             targetNode.ui.endDrop();
36995             return false;
36996         }
36997         // allow target changing
36998         targetNode = dropEvent.target;
36999         if(point == "append" && !targetNode.isExpanded()){
37000             targetNode.expand(false, null, function(){
37001                 this.completeDrop(dropEvent);
37002             }.createDelegate(this));
37003         }else{
37004             this.completeDrop(dropEvent);
37005         }
37006         return true;
37007     },
37008     
37009     completeDrop : function(de){
37010         var ns = de.dropNode, p = de.point, t = de.target;
37011         if(!(ns instanceof Array)){
37012             ns = [ns];
37013         }
37014         var n;
37015         for(var i = 0, len = ns.length; i < len; i++){
37016             n = ns[i];
37017             if(p == "above"){
37018                 t.parentNode.insertBefore(n, t);
37019             }else if(p == "below"){
37020                 t.parentNode.insertBefore(n, t.nextSibling);
37021             }else{
37022                 t.appendChild(n);
37023             }
37024         }
37025         n.ui.focus();
37026         if(this.tree.hlDrop){
37027             n.ui.highlight();
37028         }
37029         t.ui.endDrop();
37030         this.tree.fireEvent("nodedrop", de);
37031     },
37032     
37033     afterNodeMoved : function(dd, data, e, targetNode, dropNode){
37034         if(this.tree.hlDrop){
37035             dropNode.ui.focus();
37036             dropNode.ui.highlight();
37037         }
37038         this.tree.fireEvent("nodedrop", this.tree, targetNode, data, dd, e);
37039     },
37040     
37041     getTree : function(){
37042         return this.tree;
37043     },
37044     
37045     removeDropIndicators : function(n){
37046         if(n && n.ddel){
37047             var el = n.ddel;
37048             Roo.fly(el).removeClass([
37049                     "x-tree-drag-insert-above",
37050                     "x-tree-drag-insert-below",
37051                     "x-tree-drag-append"]);
37052             this.lastInsertClass = "_noclass";
37053         }
37054     },
37055     
37056     beforeDragDrop : function(target, e, id){
37057         this.cancelExpand();
37058         return true;
37059     },
37060     
37061     afterRepair : function(data){
37062         if(data && Roo.enableFx){
37063             data.node.ui.highlight();
37064         }
37065         this.hideProxy();
37066     } 
37067     
37068 });
37069
37070 }
37071 /*
37072  * Based on:
37073  * Ext JS Library 1.1.1
37074  * Copyright(c) 2006-2007, Ext JS, LLC.
37075  *
37076  * Originally Released Under LGPL - original licence link has changed is not relivant.
37077  *
37078  * Fork - LGPL
37079  * <script type="text/javascript">
37080  */
37081  
37082
37083 if(Roo.dd.DragZone){
37084 Roo.tree.TreeDragZone = function(tree, config){
37085     Roo.tree.TreeDragZone.superclass.constructor.call(this, tree.getTreeEl(), config);
37086     this.tree = tree;
37087 };
37088
37089 Roo.extend(Roo.tree.TreeDragZone, Roo.dd.DragZone, {
37090     ddGroup : "TreeDD",
37091    
37092     onBeforeDrag : function(data, e){
37093         var n = data.node;
37094         return n && n.draggable && !n.disabled;
37095     },
37096      
37097     
37098     onInitDrag : function(e){
37099         var data = this.dragData;
37100         this.tree.getSelectionModel().select(data.node);
37101         this.proxy.update("");
37102         data.node.ui.appendDDGhost(this.proxy.ghost.dom);
37103         this.tree.fireEvent("startdrag", this.tree, data.node, e);
37104     },
37105     
37106     getRepairXY : function(e, data){
37107         return data.node.ui.getDDRepairXY();
37108     },
37109     
37110     onEndDrag : function(data, e){
37111         this.tree.fireEvent("enddrag", this.tree, data.node, e);
37112         
37113         
37114     },
37115     
37116     onValidDrop : function(dd, e, id){
37117         this.tree.fireEvent("dragdrop", this.tree, this.dragData.node, dd, e);
37118         this.hideProxy();
37119     },
37120     
37121     beforeInvalidDrop : function(e, id){
37122         // this scrolls the original position back into view
37123         var sm = this.tree.getSelectionModel();
37124         sm.clearSelections();
37125         sm.select(this.dragData.node);
37126     }
37127 });
37128 }/*
37129  * Based on:
37130  * Ext JS Library 1.1.1
37131  * Copyright(c) 2006-2007, Ext JS, LLC.
37132  *
37133  * Originally Released Under LGPL - original licence link has changed is not relivant.
37134  *
37135  * Fork - LGPL
37136  * <script type="text/javascript">
37137  */
37138 /**
37139  * @class Roo.tree.TreeEditor
37140  * @extends Roo.Editor
37141  * Provides editor functionality for inline tree node editing.  Any valid {@link Roo.form.Field} can be used
37142  * as the editor field.
37143  * @constructor
37144  * @param {Object} config (used to be the tree panel.)
37145  * @param {Object} oldconfig DEPRECIATED Either a prebuilt {@link Roo.form.Field} instance or a Field config object
37146  * 
37147  * @cfg {Roo.tree.TreePanel} tree The tree to bind to.
37148  * @cfg {Roo.form.TextField} field [required] The field configuration
37149  *
37150  * 
37151  */
37152 Roo.tree.TreeEditor = function(config, oldconfig) { // was -- (tree, config){
37153     var tree = config;
37154     var field;
37155     if (oldconfig) { // old style..
37156         field = oldconfig.events ? oldconfig : new Roo.form.TextField(oldconfig);
37157     } else {
37158         // new style..
37159         tree = config.tree;
37160         config.field = config.field  || {};
37161         config.field.xtype = 'TextField';
37162         field = Roo.factory(config.field, Roo.form);
37163     }
37164     config = config || {};
37165     
37166     
37167     this.addEvents({
37168         /**
37169          * @event beforenodeedit
37170          * Fires when editing is initiated, but before the value changes.  Editing can be canceled by returning
37171          * false from the handler of this event.
37172          * @param {Editor} this
37173          * @param {Roo.tree.Node} node 
37174          */
37175         "beforenodeedit" : true
37176     });
37177     
37178     //Roo.log(config);
37179     Roo.tree.TreeEditor.superclass.constructor.call(this, field, config);
37180
37181     this.tree = tree;
37182
37183     tree.on('beforeclick', this.beforeNodeClick, this);
37184     tree.getTreeEl().on('mousedown', this.hide, this);
37185     this.on('complete', this.updateNode, this);
37186     this.on('beforestartedit', this.fitToTree, this);
37187     this.on('startedit', this.bindScroll, this, {delay:10});
37188     this.on('specialkey', this.onSpecialKey, this);
37189 };
37190
37191 Roo.extend(Roo.tree.TreeEditor, Roo.Editor, {
37192     /**
37193      * @cfg {String} alignment
37194      * The position to align to (see {@link Roo.Element#alignTo} for more details, defaults to "l-l").
37195      */
37196     alignment: "l-l",
37197     // inherit
37198     autoSize: false,
37199     /**
37200      * @cfg {Boolean} hideEl
37201      * True to hide the bound element while the editor is displayed (defaults to false)
37202      */
37203     hideEl : false,
37204     /**
37205      * @cfg {String} cls
37206      * CSS class to apply to the editor (defaults to "x-small-editor x-tree-editor")
37207      */
37208     cls: "x-small-editor x-tree-editor",
37209     /**
37210      * @cfg {Boolean} shim
37211      * True to shim the editor if selects/iframes could be displayed beneath it (defaults to false)
37212      */
37213     shim:false,
37214     // inherit
37215     shadow:"frame",
37216     /**
37217      * @cfg {Number} maxWidth
37218      * The maximum width in pixels of the editor field (defaults to 250).  Note that if the maxWidth would exceed
37219      * the containing tree element's size, it will be automatically limited for you to the container width, taking
37220      * scroll and client offsets into account prior to each edit.
37221      */
37222     maxWidth: 250,
37223
37224     editDelay : 350,
37225
37226     // private
37227     fitToTree : function(ed, el){
37228         var td = this.tree.getTreeEl().dom, nd = el.dom;
37229         if(td.scrollLeft >  nd.offsetLeft){ // ensure the node left point is visible
37230             td.scrollLeft = nd.offsetLeft;
37231         }
37232         var w = Math.min(
37233                 this.maxWidth,
37234                 (td.clientWidth > 20 ? td.clientWidth : td.offsetWidth) - Math.max(0, nd.offsetLeft-td.scrollLeft) - /*cushion*/5);
37235         this.setSize(w, '');
37236         
37237         return this.fireEvent('beforenodeedit', this, this.editNode);
37238         
37239     },
37240
37241     // private
37242     triggerEdit : function(node){
37243         this.completeEdit();
37244         this.editNode = node;
37245         this.startEdit(node.ui.textNode, node.text);
37246     },
37247
37248     // private
37249     bindScroll : function(){
37250         this.tree.getTreeEl().on('scroll', this.cancelEdit, this);
37251     },
37252
37253     // private
37254     beforeNodeClick : function(node, e){
37255         var sinceLast = (this.lastClick ? this.lastClick.getElapsed() : 0);
37256         this.lastClick = new Date();
37257         if(sinceLast > this.editDelay && this.tree.getSelectionModel().isSelected(node)){
37258             e.stopEvent();
37259             this.triggerEdit(node);
37260             return false;
37261         }
37262         return true;
37263     },
37264
37265     // private
37266     updateNode : function(ed, value){
37267         this.tree.getTreeEl().un('scroll', this.cancelEdit, this);
37268         this.editNode.setText(value);
37269     },
37270
37271     // private
37272     onHide : function(){
37273         Roo.tree.TreeEditor.superclass.onHide.call(this);
37274         if(this.editNode){
37275             this.editNode.ui.focus();
37276         }
37277     },
37278
37279     // private
37280     onSpecialKey : function(field, e){
37281         var k = e.getKey();
37282         if(k == e.ESC){
37283             e.stopEvent();
37284             this.cancelEdit();
37285         }else if(k == e.ENTER && !e.hasModifier()){
37286             e.stopEvent();
37287             this.completeEdit();
37288         }
37289     }
37290 });//<Script type="text/javascript">
37291 /*
37292  * Based on:
37293  * Ext JS Library 1.1.1
37294  * Copyright(c) 2006-2007, Ext JS, LLC.
37295  *
37296  * Originally Released Under LGPL - original licence link has changed is not relivant.
37297  *
37298  * Fork - LGPL
37299  * <script type="text/javascript">
37300  */
37301  
37302 /**
37303  * Not documented??? - probably should be...
37304  */
37305
37306 Roo.tree.ColumnNodeUI = Roo.extend(Roo.tree.TreeNodeUI, {
37307     //focus: Roo.emptyFn, // prevent odd scrolling behavior
37308     
37309     renderElements : function(n, a, targetNode, bulkRender){
37310         //consel.log("renderElements?");
37311         this.indentMarkup = n.parentNode ? n.parentNode.ui.getChildIndent() : '';
37312
37313         var t = n.getOwnerTree();
37314         var tid = Pman.Tab.Document_TypesTree.tree.el.id;
37315         
37316         var cols = t.columns;
37317         var bw = t.borderWidth;
37318         var c = cols[0];
37319         var href = a.href ? a.href : Roo.isGecko ? "" : "#";
37320          var cb = typeof a.checked == "boolean";
37321         var tx = String.format('{0}',n.text || (c.renderer ? c.renderer(a[c.dataIndex], n, a) : a[c.dataIndex]));
37322         var colcls = 'x-t-' + tid + '-c0';
37323         var buf = [
37324             '<li class="x-tree-node">',
37325             
37326                 
37327                 '<div class="x-tree-node-el ', a.cls,'">',
37328                     // extran...
37329                     '<div class="x-tree-col ', colcls, '" style="width:', c.width-bw, 'px;">',
37330                 
37331                 
37332                         '<span class="x-tree-node-indent">',this.indentMarkup,'</span>',
37333                         '<img src="', this.emptyIcon, '" class="x-tree-ec-icon  " />',
37334                         '<img src="', a.icon || this.emptyIcon, '" class="x-tree-node-icon',
37335                            (a.icon ? ' x-tree-node-inline-icon' : ''),
37336                            (a.iconCls ? ' '+a.iconCls : ''),
37337                            '" unselectable="on" />',
37338                         (cb ? ('<input class="x-tree-node-cb" type="checkbox" ' + 
37339                              (a.checked ? 'checked="checked" />' : ' />')) : ''),
37340                              
37341                         '<a class="x-tree-node-anchor" hidefocus="on" href="',href,'" tabIndex="1" ',
37342                             (a.hrefTarget ? ' target="' +a.hrefTarget + '"' : ''), '>',
37343                             '<span unselectable="on" qtip="' + tx + '">',
37344                              tx,
37345                              '</span></a>' ,
37346                     '</div>',
37347                      '<a class="x-tree-node-anchor" hidefocus="on" href="',href,'" tabIndex="1" ',
37348                             (a.hrefTarget ? ' target="' +a.hrefTarget + '"' : ''), '>'
37349                  ];
37350         for(var i = 1, len = cols.length; i < len; i++){
37351             c = cols[i];
37352             colcls = 'x-t-' + tid + '-c' +i;
37353             tx = String.format('{0}', (c.renderer ? c.renderer(a[c.dataIndex], n, a) : a[c.dataIndex]));
37354             buf.push('<div class="x-tree-col ', colcls, ' ' ,(c.cls?c.cls:''),'" style="width:',c.width-bw,'px;">',
37355                         '<div class="x-tree-col-text" qtip="' + tx +'">',tx,"</div>",
37356                       "</div>");
37357          }
37358          
37359          buf.push(
37360             '</a>',
37361             '<div class="x-clear"></div></div>',
37362             '<ul class="x-tree-node-ct" style="display:none;"></ul>',
37363             "</li>");
37364         
37365         if(bulkRender !== true && n.nextSibling && n.nextSibling.ui.getEl()){
37366             this.wrap = Roo.DomHelper.insertHtml("beforeBegin",
37367                                 n.nextSibling.ui.getEl(), buf.join(""));
37368         }else{
37369             this.wrap = Roo.DomHelper.insertHtml("beforeEnd", targetNode, buf.join(""));
37370         }
37371         var el = this.wrap.firstChild;
37372         this.elRow = el;
37373         this.elNode = el.firstChild;
37374         this.ranchor = el.childNodes[1];
37375         this.ctNode = this.wrap.childNodes[1];
37376         var cs = el.firstChild.childNodes;
37377         this.indentNode = cs[0];
37378         this.ecNode = cs[1];
37379         this.iconNode = cs[2];
37380         var index = 3;
37381         if(cb){
37382             this.checkbox = cs[3];
37383             index++;
37384         }
37385         this.anchor = cs[index];
37386         
37387         this.textNode = cs[index].firstChild;
37388         
37389         //el.on("click", this.onClick, this);
37390         //el.on("dblclick", this.onDblClick, this);
37391         
37392         
37393        // console.log(this);
37394     },
37395     initEvents : function(){
37396         Roo.tree.ColumnNodeUI.superclass.initEvents.call(this);
37397         
37398             
37399         var a = this.ranchor;
37400
37401         var el = Roo.get(a);
37402
37403         if(Roo.isOpera){ // opera render bug ignores the CSS
37404             el.setStyle("text-decoration", "none");
37405         }
37406
37407         el.on("click", this.onClick, this);
37408         el.on("dblclick", this.onDblClick, this);
37409         el.on("contextmenu", this.onContextMenu, this);
37410         
37411     },
37412     
37413     /*onSelectedChange : function(state){
37414         if(state){
37415             this.focus();
37416             this.addClass("x-tree-selected");
37417         }else{
37418             //this.blur();
37419             this.removeClass("x-tree-selected");
37420         }
37421     },*/
37422     addClass : function(cls){
37423         if(this.elRow){
37424             Roo.fly(this.elRow).addClass(cls);
37425         }
37426         
37427     },
37428     
37429     
37430     removeClass : function(cls){
37431         if(this.elRow){
37432             Roo.fly(this.elRow).removeClass(cls);
37433         }
37434     }
37435
37436     
37437     
37438 });//<Script type="text/javascript">
37439
37440 /*
37441  * Based on:
37442  * Ext JS Library 1.1.1
37443  * Copyright(c) 2006-2007, Ext JS, LLC.
37444  *
37445  * Originally Released Under LGPL - original licence link has changed is not relivant.
37446  *
37447  * Fork - LGPL
37448  * <script type="text/javascript">
37449  */
37450  
37451
37452 /**
37453  * @class Roo.tree.ColumnTree
37454  * @extends Roo.tree.TreePanel
37455  * @cfg {Object} columns  Including width, header, renderer, cls, dataIndex 
37456  * @cfg {int} borderWidth  compined right/left border allowance
37457  * @constructor
37458  * @param {String/HTMLElement/Element} el The container element
37459  * @param {Object} config
37460  */
37461 Roo.tree.ColumnTree =  function(el, config)
37462 {
37463    Roo.tree.ColumnTree.superclass.constructor.call(this, el , config);
37464    this.addEvents({
37465         /**
37466         * @event resize
37467         * Fire this event on a container when it resizes
37468         * @param {int} w Width
37469         * @param {int} h Height
37470         */
37471        "resize" : true
37472     });
37473     this.on('resize', this.onResize, this);
37474 };
37475
37476 Roo.extend(Roo.tree.ColumnTree, Roo.tree.TreePanel, {
37477     //lines:false,
37478     
37479     
37480     borderWidth: Roo.isBorderBox ? 0 : 2, 
37481     headEls : false,
37482     
37483     render : function(){
37484         // add the header.....
37485        
37486         Roo.tree.ColumnTree.superclass.render.apply(this);
37487         
37488         this.el.addClass('x-column-tree');
37489         
37490         this.headers = this.el.createChild(
37491             {cls:'x-tree-headers'},this.innerCt.dom);
37492    
37493         var cols = this.columns, c;
37494         var totalWidth = 0;
37495         this.headEls = [];
37496         var  len = cols.length;
37497         for(var i = 0; i < len; i++){
37498              c = cols[i];
37499              totalWidth += c.width;
37500             this.headEls.push(this.headers.createChild({
37501                  cls:'x-tree-hd ' + (c.cls?c.cls+'-hd':''),
37502                  cn: {
37503                      cls:'x-tree-hd-text',
37504                      html: c.header
37505                  },
37506                  style:'width:'+(c.width-this.borderWidth)+'px;'
37507              }));
37508         }
37509         this.headers.createChild({cls:'x-clear'});
37510         // prevent floats from wrapping when clipped
37511         this.headers.setWidth(totalWidth);
37512         //this.innerCt.setWidth(totalWidth);
37513         this.innerCt.setStyle({ overflow: 'auto' });
37514         this.onResize(this.width, this.height);
37515              
37516         
37517     },
37518     onResize : function(w,h)
37519     {
37520         this.height = h;
37521         this.width = w;
37522         // resize cols..
37523         this.innerCt.setWidth(this.width);
37524         this.innerCt.setHeight(this.height-20);
37525         
37526         // headers...
37527         var cols = this.columns, c;
37528         var totalWidth = 0;
37529         var expEl = false;
37530         var len = cols.length;
37531         for(var i = 0; i < len; i++){
37532             c = cols[i];
37533             if (this.autoExpandColumn !== false && c.dataIndex == this.autoExpandColumn) {
37534                 // it's the expander..
37535                 expEl  = this.headEls[i];
37536                 continue;
37537             }
37538             totalWidth += c.width;
37539             
37540         }
37541         if (expEl) {
37542             expEl.setWidth(  ((w - totalWidth)-this.borderWidth - 20));
37543         }
37544         this.headers.setWidth(w-20);
37545
37546         
37547         
37548         
37549     }
37550 });
37551 /*
37552  * Based on:
37553  * Ext JS Library 1.1.1
37554  * Copyright(c) 2006-2007, Ext JS, LLC.
37555  *
37556  * Originally Released Under LGPL - original licence link has changed is not relivant.
37557  *
37558  * Fork - LGPL
37559  * <script type="text/javascript">
37560  */
37561  
37562 /**
37563  * @class Roo.menu.Menu
37564  * @extends Roo.util.Observable
37565  * @children Roo.menu.BaseItem
37566  * A menu object.  This is the container to which you add all other menu items.  Menu can also serve a as a base class
37567  * when you want a specialzed menu based off of another component (like {@link Roo.menu.DateMenu} for example).
37568  * @constructor
37569  * Creates a new Menu
37570  * @param {Object} config Configuration options
37571  */
37572 Roo.menu.Menu = function(config){
37573     
37574     Roo.menu.Menu.superclass.constructor.call(this, config);
37575     
37576     this.id = this.id || Roo.id();
37577     this.addEvents({
37578         /**
37579          * @event beforeshow
37580          * Fires before this menu is displayed
37581          * @param {Roo.menu.Menu} this
37582          */
37583         beforeshow : true,
37584         /**
37585          * @event beforehide
37586          * Fires before this menu is hidden
37587          * @param {Roo.menu.Menu} this
37588          */
37589         beforehide : true,
37590         /**
37591          * @event show
37592          * Fires after this menu is displayed
37593          * @param {Roo.menu.Menu} this
37594          */
37595         show : true,
37596         /**
37597          * @event hide
37598          * Fires after this menu is hidden
37599          * @param {Roo.menu.Menu} this
37600          */
37601         hide : true,
37602         /**
37603          * @event click
37604          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
37605          * @param {Roo.menu.Menu} this
37606          * @param {Roo.menu.Item} menuItem The menu item that was clicked
37607          * @param {Roo.EventObject} e
37608          */
37609         click : true,
37610         /**
37611          * @event mouseover
37612          * Fires when the mouse is hovering over this menu
37613          * @param {Roo.menu.Menu} this
37614          * @param {Roo.EventObject} e
37615          * @param {Roo.menu.Item} menuItem The menu item that was clicked
37616          */
37617         mouseover : true,
37618         /**
37619          * @event mouseout
37620          * Fires when the mouse exits this menu
37621          * @param {Roo.menu.Menu} this
37622          * @param {Roo.EventObject} e
37623          * @param {Roo.menu.Item} menuItem The menu item that was clicked
37624          */
37625         mouseout : true,
37626         /**
37627          * @event itemclick
37628          * Fires when a menu item contained in this menu is clicked
37629          * @param {Roo.menu.BaseItem} baseItem The BaseItem that was clicked
37630          * @param {Roo.EventObject} e
37631          */
37632         itemclick: true
37633     });
37634     if (this.registerMenu) {
37635         Roo.menu.MenuMgr.register(this);
37636     }
37637     
37638     var mis = this.items;
37639     this.items = new Roo.util.MixedCollection();
37640     if(mis){
37641         this.add.apply(this, mis);
37642     }
37643 };
37644
37645 Roo.extend(Roo.menu.Menu, Roo.util.Observable, {
37646     /**
37647      * @cfg {Number} minWidth The minimum width of the menu in pixels (defaults to 120)
37648      */
37649     minWidth : 120,
37650     /**
37651      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop"
37652      * for bottom-right shadow (defaults to "sides")
37653      */
37654     shadow : "sides",
37655     /**
37656      * @cfg {String} subMenuAlign The {@link Roo.Element#alignTo} anchor position value to use for submenus of
37657      * this menu (defaults to "tl-tr?")
37658      */
37659     subMenuAlign : "tl-tr?",
37660     /**
37661      * @cfg {String} defaultAlign The default {@link Roo.Element#alignTo) anchor position value for this menu
37662      * relative to its element of origin (defaults to "tl-bl?")
37663      */
37664     defaultAlign : "tl-bl?",
37665     /**
37666      * @cfg {Boolean} allowOtherMenus True to allow multiple menus to be displayed at the same time (defaults to false)
37667      */
37668     allowOtherMenus : false,
37669     /**
37670      * @cfg {Boolean} registerMenu True (default) - means that clicking on screen etc. hides it.
37671      */
37672     registerMenu : true,
37673
37674     hidden:true,
37675
37676     // private
37677     render : function(){
37678         if(this.el){
37679             return;
37680         }
37681         var el = this.el = new Roo.Layer({
37682             cls: "x-menu",
37683             shadow:this.shadow,
37684             constrain: false,
37685             parentEl: this.parentEl || document.body,
37686             zindex:15000
37687         });
37688
37689         this.keyNav = new Roo.menu.MenuNav(this);
37690
37691         if(this.plain){
37692             el.addClass("x-menu-plain");
37693         }
37694         if(this.cls){
37695             el.addClass(this.cls);
37696         }
37697         // generic focus element
37698         this.focusEl = el.createChild({
37699             tag: "a", cls: "x-menu-focus", href: "#", onclick: "return false;", tabIndex:"-1"
37700         });
37701         var ul = el.createChild({tag: "ul", cls: "x-menu-list"});
37702         //disabling touch- as it's causing issues ..
37703         //ul.on(Roo.isTouch ? 'touchstart' : 'click'   , this.onClick, this);
37704         ul.on('click'   , this.onClick, this);
37705         
37706         
37707         ul.on("mouseover", this.onMouseOver, this);
37708         ul.on("mouseout", this.onMouseOut, this);
37709         this.items.each(function(item){
37710             if (item.hidden) {
37711                 return;
37712             }
37713             
37714             var li = document.createElement("li");
37715             li.className = "x-menu-list-item";
37716             ul.dom.appendChild(li);
37717             item.render(li, this);
37718         }, this);
37719         this.ul = ul;
37720         this.autoWidth();
37721     },
37722
37723     // private
37724     autoWidth : function(){
37725         var el = this.el, ul = this.ul;
37726         if(!el){
37727             return;
37728         }
37729         var w = this.width;
37730         if(w){
37731             el.setWidth(w);
37732         }else if(Roo.isIE){
37733             el.setWidth(this.minWidth);
37734             var t = el.dom.offsetWidth; // force recalc
37735             el.setWidth(ul.getWidth()+el.getFrameWidth("lr"));
37736         }
37737     },
37738
37739     // private
37740     delayAutoWidth : function(){
37741         if(this.rendered){
37742             if(!this.awTask){
37743                 this.awTask = new Roo.util.DelayedTask(this.autoWidth, this);
37744             }
37745             this.awTask.delay(20);
37746         }
37747     },
37748
37749     // private
37750     findTargetItem : function(e){
37751         var t = e.getTarget(".x-menu-list-item", this.ul,  true);
37752         if(t && t.menuItemId){
37753             return this.items.get(t.menuItemId);
37754         }
37755     },
37756
37757     // private
37758     onClick : function(e){
37759         Roo.log("menu.onClick");
37760         var t = this.findTargetItem(e);
37761         if(!t){
37762             return;
37763         }
37764         Roo.log(e);
37765         if (Roo.isTouch && e.type == 'touchstart' && t.menu  && !t.disabled) {
37766             if(t == this.activeItem && t.shouldDeactivate(e)){
37767                 this.activeItem.deactivate();
37768                 delete this.activeItem;
37769                 return;
37770             }
37771             if(t.canActivate){
37772                 this.setActiveItem(t, true);
37773             }
37774             return;
37775             
37776             
37777         }
37778         
37779         t.onClick(e);
37780         this.fireEvent("click", this, t, e);
37781     },
37782
37783     // private
37784     setActiveItem : function(item, autoExpand){
37785         if(item != this.activeItem){
37786             if(this.activeItem){
37787                 this.activeItem.deactivate();
37788             }
37789             this.activeItem = item;
37790             item.activate(autoExpand);
37791         }else if(autoExpand){
37792             item.expandMenu();
37793         }
37794     },
37795
37796     // private
37797     tryActivate : function(start, step){
37798         var items = this.items;
37799         for(var i = start, len = items.length; i >= 0 && i < len; i+= step){
37800             var item = items.get(i);
37801             if(!item.disabled && item.canActivate){
37802                 this.setActiveItem(item, false);
37803                 return item;
37804             }
37805         }
37806         return false;
37807     },
37808
37809     // private
37810     onMouseOver : function(e){
37811         var t;
37812         if(t = this.findTargetItem(e)){
37813             if(t.canActivate && !t.disabled){
37814                 this.setActiveItem(t, true);
37815             }
37816         }
37817         this.fireEvent("mouseover", this, e, t);
37818     },
37819
37820     // private
37821     onMouseOut : function(e){
37822         var t;
37823         if(t = this.findTargetItem(e)){
37824             if(t == this.activeItem && t.shouldDeactivate(e)){
37825                 this.activeItem.deactivate();
37826                 delete this.activeItem;
37827             }
37828         }
37829         this.fireEvent("mouseout", this, e, t);
37830     },
37831
37832     /**
37833      * Read-only.  Returns true if the menu is currently displayed, else false.
37834      * @type Boolean
37835      */
37836     isVisible : function(){
37837         return this.el && !this.hidden;
37838     },
37839
37840     /**
37841      * Displays this menu relative to another element
37842      * @param {String/HTMLElement/Roo.Element} element The element to align to
37843      * @param {String} position (optional) The {@link Roo.Element#alignTo} anchor position to use in aligning to
37844      * the element (defaults to this.defaultAlign)
37845      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
37846      */
37847     show : function(el, pos, parentMenu){
37848         this.parentMenu = parentMenu;
37849         if(!this.el){
37850             this.render();
37851         }
37852         this.fireEvent("beforeshow", this);
37853         this.showAt(this.el.getAlignToXY(el, pos || this.defaultAlign), parentMenu, false);
37854     },
37855
37856     /**
37857      * Displays this menu at a specific xy position
37858      * @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)
37859      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
37860      */
37861     showAt : function(xy, parentMenu, /* private: */_e){
37862         this.parentMenu = parentMenu;
37863         if(!this.el){
37864             this.render();
37865         }
37866         if(_e !== false){
37867             this.fireEvent("beforeshow", this);
37868             xy = this.el.adjustForConstraints(xy);
37869         }
37870         this.el.setXY(xy);
37871         this.el.show();
37872         this.hidden = false;
37873         this.focus();
37874         this.fireEvent("show", this);
37875     },
37876
37877     focus : function(){
37878         if(!this.hidden){
37879             this.doFocus.defer(50, this);
37880         }
37881     },
37882
37883     doFocus : function(){
37884         if(!this.hidden){
37885             this.focusEl.focus();
37886         }
37887     },
37888
37889     /**
37890      * Hides this menu and optionally all parent menus
37891      * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
37892      */
37893     hide : function(deep){
37894         if(this.el && this.isVisible()){
37895             this.fireEvent("beforehide", this);
37896             if(this.activeItem){
37897                 this.activeItem.deactivate();
37898                 this.activeItem = null;
37899             }
37900             this.el.hide();
37901             this.hidden = true;
37902             this.fireEvent("hide", this);
37903         }
37904         if(deep === true && this.parentMenu){
37905             this.parentMenu.hide(true);
37906         }
37907     },
37908
37909     /**
37910      * Addds one or more items of any type supported by the Menu class, or that can be converted into menu items.
37911      * Any of the following are valid:
37912      * <ul>
37913      * <li>Any menu item object based on {@link Roo.menu.Item}</li>
37914      * <li>An HTMLElement object which will be converted to a menu item</li>
37915      * <li>A menu item config object that will be created as a new menu item</li>
37916      * <li>A string, which can either be '-' or 'separator' to add a menu separator, otherwise
37917      * it will be converted into a {@link Roo.menu.TextItem} and added</li>
37918      * </ul>
37919      * Usage:
37920      * <pre><code>
37921 // Create the menu
37922 var menu = new Roo.menu.Menu();
37923
37924 // Create a menu item to add by reference
37925 var menuItem = new Roo.menu.Item({ text: 'New Item!' });
37926
37927 // Add a bunch of items at once using different methods.
37928 // Only the last item added will be returned.
37929 var item = menu.add(
37930     menuItem,                // add existing item by ref
37931     'Dynamic Item',          // new TextItem
37932     '-',                     // new separator
37933     { text: 'Config Item' }  // new item by config
37934 );
37935 </code></pre>
37936      * @param {Mixed} args One or more menu items, menu item configs or other objects that can be converted to menu items
37937      * @return {Roo.menu.Item} The menu item that was added, or the last one if multiple items were added
37938      */
37939     add : function(){
37940         var a = arguments, l = a.length, item;
37941         for(var i = 0; i < l; i++){
37942             var el = a[i];
37943             if ((typeof(el) == "object") && el.xtype && el.xns) {
37944                 el = Roo.factory(el, Roo.menu);
37945             }
37946             
37947             if(el.render){ // some kind of Item
37948                 item = this.addItem(el);
37949             }else if(typeof el == "string"){ // string
37950                 if(el == "separator" || el == "-"){
37951                     item = this.addSeparator();
37952                 }else{
37953                     item = this.addText(el);
37954                 }
37955             }else if(el.tagName || el.el){ // element
37956                 item = this.addElement(el);
37957             }else if(typeof el == "object"){ // must be menu item config?
37958                 item = this.addMenuItem(el);
37959             }
37960         }
37961         return item;
37962     },
37963
37964     /**
37965      * Returns this menu's underlying {@link Roo.Element} object
37966      * @return {Roo.Element} The element
37967      */
37968     getEl : function(){
37969         if(!this.el){
37970             this.render();
37971         }
37972         return this.el;
37973     },
37974
37975     /**
37976      * Adds a separator bar to the menu
37977      * @return {Roo.menu.Item} The menu item that was added
37978      */
37979     addSeparator : function(){
37980         return this.addItem(new Roo.menu.Separator());
37981     },
37982
37983     /**
37984      * Adds an {@link Roo.Element} object to the menu
37985      * @param {String/HTMLElement/Roo.Element} el The element or DOM node to add, or its id
37986      * @return {Roo.menu.Item} The menu item that was added
37987      */
37988     addElement : function(el){
37989         return this.addItem(new Roo.menu.BaseItem(el));
37990     },
37991
37992     /**
37993      * Adds an existing object based on {@link Roo.menu.Item} to the menu
37994      * @param {Roo.menu.Item} item The menu item to add
37995      * @return {Roo.menu.Item} The menu item that was added
37996      */
37997     addItem : function(item){
37998         this.items.add(item);
37999         if(this.ul){
38000             var li = document.createElement("li");
38001             li.className = "x-menu-list-item";
38002             this.ul.dom.appendChild(li);
38003             item.render(li, this);
38004             this.delayAutoWidth();
38005         }
38006         return item;
38007     },
38008
38009     /**
38010      * Creates a new {@link Roo.menu.Item} based an the supplied config object and adds it to the menu
38011      * @param {Object} config A MenuItem config object
38012      * @return {Roo.menu.Item} The menu item that was added
38013      */
38014     addMenuItem : function(config){
38015         if(!(config instanceof Roo.menu.Item)){
38016             if(typeof config.checked == "boolean"){ // must be check menu item config?
38017                 config = new Roo.menu.CheckItem(config);
38018             }else{
38019                 config = new Roo.menu.Item(config);
38020             }
38021         }
38022         return this.addItem(config);
38023     },
38024
38025     /**
38026      * Creates a new {@link Roo.menu.TextItem} with the supplied text and adds it to the menu
38027      * @param {String} text The text to display in the menu item
38028      * @return {Roo.menu.Item} The menu item that was added
38029      */
38030     addText : function(text){
38031         return this.addItem(new Roo.menu.TextItem({ text : text }));
38032     },
38033
38034     /**
38035      * Inserts an existing object based on {@link Roo.menu.Item} to the menu at a specified index
38036      * @param {Number} index The index in the menu's list of current items where the new item should be inserted
38037      * @param {Roo.menu.Item} item The menu item to add
38038      * @return {Roo.menu.Item} The menu item that was added
38039      */
38040     insert : function(index, item){
38041         this.items.insert(index, item);
38042         if(this.ul){
38043             var li = document.createElement("li");
38044             li.className = "x-menu-list-item";
38045             this.ul.dom.insertBefore(li, this.ul.dom.childNodes[index]);
38046             item.render(li, this);
38047             this.delayAutoWidth();
38048         }
38049         return item;
38050     },
38051
38052     /**
38053      * Removes an {@link Roo.menu.Item} from the menu and destroys the object
38054      * @param {Roo.menu.Item} item The menu item to remove
38055      */
38056     remove : function(item){
38057         this.items.removeKey(item.id);
38058         item.destroy();
38059     },
38060
38061     /**
38062      * Removes and destroys all items in the menu
38063      */
38064     removeAll : function(){
38065         var f;
38066         while(f = this.items.first()){
38067             this.remove(f);
38068         }
38069     }
38070 });
38071
38072 // MenuNav is a private utility class used internally by the Menu
38073 Roo.menu.MenuNav = function(menu){
38074     Roo.menu.MenuNav.superclass.constructor.call(this, menu.el);
38075     this.scope = this.menu = menu;
38076 };
38077
38078 Roo.extend(Roo.menu.MenuNav, Roo.KeyNav, {
38079     doRelay : function(e, h){
38080         var k = e.getKey();
38081         if(!this.menu.activeItem && e.isNavKeyPress() && k != e.SPACE && k != e.RETURN){
38082             this.menu.tryActivate(0, 1);
38083             return false;
38084         }
38085         return h.call(this.scope || this, e, this.menu);
38086     },
38087
38088     up : function(e, m){
38089         if(!m.tryActivate(m.items.indexOf(m.activeItem)-1, -1)){
38090             m.tryActivate(m.items.length-1, -1);
38091         }
38092     },
38093
38094     down : function(e, m){
38095         if(!m.tryActivate(m.items.indexOf(m.activeItem)+1, 1)){
38096             m.tryActivate(0, 1);
38097         }
38098     },
38099
38100     right : function(e, m){
38101         if(m.activeItem){
38102             m.activeItem.expandMenu(true);
38103         }
38104     },
38105
38106     left : function(e, m){
38107         m.hide();
38108         if(m.parentMenu && m.parentMenu.activeItem){
38109             m.parentMenu.activeItem.activate();
38110         }
38111     },
38112
38113     enter : function(e, m){
38114         if(m.activeItem){
38115             e.stopPropagation();
38116             m.activeItem.onClick(e);
38117             m.fireEvent("click", this, m.activeItem);
38118             return true;
38119         }
38120     }
38121 });/*
38122  * Based on:
38123  * Ext JS Library 1.1.1
38124  * Copyright(c) 2006-2007, Ext JS, LLC.
38125  *
38126  * Originally Released Under LGPL - original licence link has changed is not relivant.
38127  *
38128  * Fork - LGPL
38129  * <script type="text/javascript">
38130  */
38131  
38132 /**
38133  * @class Roo.menu.MenuMgr
38134  * Provides a common registry of all menu items on a page so that they can be easily accessed by id.
38135  * @static
38136  */
38137 Roo.menu.MenuMgr = function(){
38138    var menus, active, groups = {}, attached = false, lastShow = new Date();
38139
38140    // private - called when first menu is created
38141    function init(){
38142        menus = {};
38143        active = new Roo.util.MixedCollection();
38144        Roo.get(document).addKeyListener(27, function(){
38145            if(active.length > 0){
38146                hideAll();
38147            }
38148        });
38149    }
38150
38151    // private
38152    function hideAll(){
38153        if(active && active.length > 0){
38154            var c = active.clone();
38155            c.each(function(m){
38156                m.hide();
38157            });
38158        }
38159    }
38160
38161    // private
38162    function onHide(m){
38163        active.remove(m);
38164        if(active.length < 1){
38165            Roo.get(document).un("mousedown", onMouseDown);
38166            attached = false;
38167        }
38168    }
38169
38170    // private
38171    function onShow(m){
38172        var last = active.last();
38173        lastShow = new Date();
38174        active.add(m);
38175        if(!attached){
38176            Roo.get(document).on("mousedown", onMouseDown);
38177            attached = true;
38178        }
38179        if(m.parentMenu){
38180           m.getEl().setZIndex(parseInt(m.parentMenu.getEl().getStyle("z-index"), 10) + 3);
38181           m.parentMenu.activeChild = m;
38182        }else if(last && last.isVisible()){
38183           m.getEl().setZIndex(parseInt(last.getEl().getStyle("z-index"), 10) + 3);
38184        }
38185    }
38186
38187    // private
38188    function onBeforeHide(m){
38189        if(m.activeChild){
38190            m.activeChild.hide();
38191        }
38192        if(m.autoHideTimer){
38193            clearTimeout(m.autoHideTimer);
38194            delete m.autoHideTimer;
38195        }
38196    }
38197
38198    // private
38199    function onBeforeShow(m){
38200        var pm = m.parentMenu;
38201        if(!pm && !m.allowOtherMenus){
38202            hideAll();
38203        }else if(pm && pm.activeChild && active != m){
38204            pm.activeChild.hide();
38205        }
38206    }
38207
38208    // private
38209    function onMouseDown(e){
38210        if(lastShow.getElapsed() > 50 && active.length > 0 && !e.getTarget(".x-menu")){
38211            hideAll();
38212        }
38213    }
38214
38215    // private
38216    function onBeforeCheck(mi, state){
38217        if(state){
38218            var g = groups[mi.group];
38219            for(var i = 0, l = g.length; i < l; i++){
38220                if(g[i] != mi){
38221                    g[i].setChecked(false);
38222                }
38223            }
38224        }
38225    }
38226
38227    return {
38228
38229        /**
38230         * Hides all menus that are currently visible
38231         */
38232        hideAll : function(){
38233             hideAll();  
38234        },
38235
38236        // private
38237        register : function(menu){
38238            if(!menus){
38239                init();
38240            }
38241            menus[menu.id] = menu;
38242            menu.on("beforehide", onBeforeHide);
38243            menu.on("hide", onHide);
38244            menu.on("beforeshow", onBeforeShow);
38245            menu.on("show", onShow);
38246            var g = menu.group;
38247            if(g && menu.events["checkchange"]){
38248                if(!groups[g]){
38249                    groups[g] = [];
38250                }
38251                groups[g].push(menu);
38252                menu.on("checkchange", onCheck);
38253            }
38254        },
38255
38256         /**
38257          * Returns a {@link Roo.menu.Menu} object
38258          * @param {String/Object} menu The string menu id, an existing menu object reference, or a Menu config that will
38259          * be used to generate and return a new Menu instance.
38260          */
38261        get : function(menu){
38262            if(typeof menu == "string"){ // menu id
38263                return menus[menu];
38264            }else if(menu.events){  // menu instance
38265                return menu;
38266            }else if(typeof menu.length == 'number'){ // array of menu items?
38267                return new Roo.menu.Menu({items:menu});
38268            }else{ // otherwise, must be a config
38269                return new Roo.menu.Menu(menu);
38270            }
38271        },
38272
38273        // private
38274        unregister : function(menu){
38275            delete menus[menu.id];
38276            menu.un("beforehide", onBeforeHide);
38277            menu.un("hide", onHide);
38278            menu.un("beforeshow", onBeforeShow);
38279            menu.un("show", onShow);
38280            var g = menu.group;
38281            if(g && menu.events["checkchange"]){
38282                groups[g].remove(menu);
38283                menu.un("checkchange", onCheck);
38284            }
38285        },
38286
38287        // private
38288        registerCheckable : function(menuItem){
38289            var g = menuItem.group;
38290            if(g){
38291                if(!groups[g]){
38292                    groups[g] = [];
38293                }
38294                groups[g].push(menuItem);
38295                menuItem.on("beforecheckchange", onBeforeCheck);
38296            }
38297        },
38298
38299        // private
38300        unregisterCheckable : function(menuItem){
38301            var g = menuItem.group;
38302            if(g){
38303                groups[g].remove(menuItem);
38304                menuItem.un("beforecheckchange", onBeforeCheck);
38305            }
38306        }
38307    };
38308 }();/*
38309  * Based on:
38310  * Ext JS Library 1.1.1
38311  * Copyright(c) 2006-2007, Ext JS, LLC.
38312  *
38313  * Originally Released Under LGPL - original licence link has changed is not relivant.
38314  *
38315  * Fork - LGPL
38316  * <script type="text/javascript">
38317  */
38318  
38319
38320 /**
38321  * @class Roo.menu.BaseItem
38322  * @extends Roo.Component
38323  * @abstract
38324  * The base class for all items that render into menus.  BaseItem provides default rendering, activated state
38325  * management and base configuration options shared by all menu components.
38326  * @constructor
38327  * Creates a new BaseItem
38328  * @param {Object} config Configuration options
38329  */
38330 Roo.menu.BaseItem = function(config){
38331     Roo.menu.BaseItem.superclass.constructor.call(this, config);
38332
38333     this.addEvents({
38334         /**
38335          * @event click
38336          * Fires when this item is clicked
38337          * @param {Roo.menu.BaseItem} this
38338          * @param {Roo.EventObject} e
38339          */
38340         click: true,
38341         /**
38342          * @event activate
38343          * Fires when this item is activated
38344          * @param {Roo.menu.BaseItem} this
38345          */
38346         activate : true,
38347         /**
38348          * @event deactivate
38349          * Fires when this item is deactivated
38350          * @param {Roo.menu.BaseItem} this
38351          */
38352         deactivate : true
38353     });
38354
38355     if(this.handler){
38356         this.on("click", this.handler, this.scope, true);
38357     }
38358 };
38359
38360 Roo.extend(Roo.menu.BaseItem, Roo.Component, {
38361     /**
38362      * @cfg {Function} handler
38363      * A function that will handle the click event of this menu item (defaults to undefined)
38364      */
38365     /**
38366      * @cfg {Boolean} canActivate True if this item can be visually activated (defaults to false)
38367      */
38368     canActivate : false,
38369     
38370      /**
38371      * @cfg {Boolean} hidden True to prevent creation of this menu item (defaults to false)
38372      */
38373     hidden: false,
38374     
38375     /**
38376      * @cfg {String} activeClass The CSS class to use when the item becomes activated (defaults to "x-menu-item-active")
38377      */
38378     activeClass : "x-menu-item-active",
38379     /**
38380      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to true)
38381      */
38382     hideOnClick : true,
38383     /**
38384      * @cfg {Number} hideDelay Length of time in milliseconds to wait before hiding after a click (defaults to 100)
38385      */
38386     hideDelay : 100,
38387
38388     // private
38389     ctype: "Roo.menu.BaseItem",
38390
38391     // private
38392     actionMode : "container",
38393
38394     // private
38395     render : function(container, parentMenu){
38396         this.parentMenu = parentMenu;
38397         Roo.menu.BaseItem.superclass.render.call(this, container);
38398         this.container.menuItemId = this.id;
38399     },
38400
38401     // private
38402     onRender : function(container, position){
38403         this.el = Roo.get(this.el);
38404         container.dom.appendChild(this.el.dom);
38405     },
38406
38407     // private
38408     onClick : function(e){
38409         if(!this.disabled && this.fireEvent("click", this, e) !== false
38410                 && this.parentMenu.fireEvent("itemclick", this, e) !== false){
38411             this.handleClick(e);
38412         }else{
38413             e.stopEvent();
38414         }
38415     },
38416
38417     // private
38418     activate : function(){
38419         if(this.disabled){
38420             return false;
38421         }
38422         var li = this.container;
38423         li.addClass(this.activeClass);
38424         this.region = li.getRegion().adjust(2, 2, -2, -2);
38425         this.fireEvent("activate", this);
38426         return true;
38427     },
38428
38429     // private
38430     deactivate : function(){
38431         this.container.removeClass(this.activeClass);
38432         this.fireEvent("deactivate", this);
38433     },
38434
38435     // private
38436     shouldDeactivate : function(e){
38437         return !this.region || !this.region.contains(e.getPoint());
38438     },
38439
38440     // private
38441     handleClick : function(e){
38442         if(this.hideOnClick){
38443             this.parentMenu.hide.defer(this.hideDelay, this.parentMenu, [true]);
38444         }
38445     },
38446
38447     // private
38448     expandMenu : function(autoActivate){
38449         // do nothing
38450     },
38451
38452     // private
38453     hideMenu : function(){
38454         // do nothing
38455     }
38456 });/*
38457  * Based on:
38458  * Ext JS Library 1.1.1
38459  * Copyright(c) 2006-2007, Ext JS, LLC.
38460  *
38461  * Originally Released Under LGPL - original licence link has changed is not relivant.
38462  *
38463  * Fork - LGPL
38464  * <script type="text/javascript">
38465  */
38466  
38467 /**
38468  * @class Roo.menu.Adapter
38469  * @extends Roo.menu.BaseItem
38470  * @abstract
38471  * 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.
38472  * It provides basic rendering, activation management and enable/disable logic required to work in menus.
38473  * @constructor
38474  * Creates a new Adapter
38475  * @param {Object} config Configuration options
38476  */
38477 Roo.menu.Adapter = function(component, config){
38478     Roo.menu.Adapter.superclass.constructor.call(this, config);
38479     this.component = component;
38480 };
38481 Roo.extend(Roo.menu.Adapter, Roo.menu.BaseItem, {
38482     // private
38483     canActivate : true,
38484
38485     // private
38486     onRender : function(container, position){
38487         this.component.render(container);
38488         this.el = this.component.getEl();
38489     },
38490
38491     // private
38492     activate : function(){
38493         if(this.disabled){
38494             return false;
38495         }
38496         this.component.focus();
38497         this.fireEvent("activate", this);
38498         return true;
38499     },
38500
38501     // private
38502     deactivate : function(){
38503         this.fireEvent("deactivate", this);
38504     },
38505
38506     // private
38507     disable : function(){
38508         this.component.disable();
38509         Roo.menu.Adapter.superclass.disable.call(this);
38510     },
38511
38512     // private
38513     enable : function(){
38514         this.component.enable();
38515         Roo.menu.Adapter.superclass.enable.call(this);
38516     }
38517 });/*
38518  * Based on:
38519  * Ext JS Library 1.1.1
38520  * Copyright(c) 2006-2007, Ext JS, LLC.
38521  *
38522  * Originally Released Under LGPL - original licence link has changed is not relivant.
38523  *
38524  * Fork - LGPL
38525  * <script type="text/javascript">
38526  */
38527
38528 /**
38529  * @class Roo.menu.TextItem
38530  * @extends Roo.menu.BaseItem
38531  * Adds a static text string to a menu, usually used as either a heading or group separator.
38532  * Note: old style constructor with text is still supported.
38533  * 
38534  * @constructor
38535  * Creates a new TextItem
38536  * @param {Object} cfg Configuration
38537  */
38538 Roo.menu.TextItem = function(cfg){
38539     if (typeof(cfg) == 'string') {
38540         this.text = cfg;
38541     } else {
38542         Roo.apply(this,cfg);
38543     }
38544     
38545     Roo.menu.TextItem.superclass.constructor.call(this);
38546 };
38547
38548 Roo.extend(Roo.menu.TextItem, Roo.menu.BaseItem, {
38549     /**
38550      * @cfg {String} text Text to show on item.
38551      */
38552     text : '',
38553     
38554     /**
38555      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to false)
38556      */
38557     hideOnClick : false,
38558     /**
38559      * @cfg {String} itemCls The default CSS class to use for text items (defaults to "x-menu-text")
38560      */
38561     itemCls : "x-menu-text",
38562
38563     // private
38564     onRender : function(){
38565         var s = document.createElement("span");
38566         s.className = this.itemCls;
38567         s.innerHTML = this.text;
38568         this.el = s;
38569         Roo.menu.TextItem.superclass.onRender.apply(this, arguments);
38570     }
38571 });/*
38572  * Based on:
38573  * Ext JS Library 1.1.1
38574  * Copyright(c) 2006-2007, Ext JS, LLC.
38575  *
38576  * Originally Released Under LGPL - original licence link has changed is not relivant.
38577  *
38578  * Fork - LGPL
38579  * <script type="text/javascript">
38580  */
38581
38582 /**
38583  * @class Roo.menu.Separator
38584  * @extends Roo.menu.BaseItem
38585  * Adds a separator bar to a menu, used to divide logical groups of menu items. Generally you will
38586  * add one of these by using "-" in you call to add() or in your items config rather than creating one directly.
38587  * @constructor
38588  * @param {Object} config Configuration options
38589  */
38590 Roo.menu.Separator = function(config){
38591     Roo.menu.Separator.superclass.constructor.call(this, config);
38592 };
38593
38594 Roo.extend(Roo.menu.Separator, Roo.menu.BaseItem, {
38595     /**
38596      * @cfg {String} itemCls The default CSS class to use for separators (defaults to "x-menu-sep")
38597      */
38598     itemCls : "x-menu-sep",
38599     /**
38600      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to false)
38601      */
38602     hideOnClick : false,
38603
38604     // private
38605     onRender : function(li){
38606         var s = document.createElement("span");
38607         s.className = this.itemCls;
38608         s.innerHTML = "&#160;";
38609         this.el = s;
38610         li.addClass("x-menu-sep-li");
38611         Roo.menu.Separator.superclass.onRender.apply(this, arguments);
38612     }
38613 });/*
38614  * Based on:
38615  * Ext JS Library 1.1.1
38616  * Copyright(c) 2006-2007, Ext JS, LLC.
38617  *
38618  * Originally Released Under LGPL - original licence link has changed is not relivant.
38619  *
38620  * Fork - LGPL
38621  * <script type="text/javascript">
38622  */
38623 /**
38624  * @class Roo.menu.Item
38625  * @extends Roo.menu.BaseItem
38626  * A base class for all menu items that require menu-related functionality (like sub-menus) and are not static
38627  * display items.  Item extends the base functionality of {@link Roo.menu.BaseItem} by adding menu-specific
38628  * activation and click handling.
38629  * @constructor
38630  * Creates a new Item
38631  * @param {Object} config Configuration options
38632  */
38633 Roo.menu.Item = function(config){
38634     Roo.menu.Item.superclass.constructor.call(this, config);
38635     if(this.menu){
38636         this.menu = Roo.menu.MenuMgr.get(this.menu);
38637     }
38638 };
38639 Roo.extend(Roo.menu.Item, Roo.menu.BaseItem, {
38640     /**
38641      * @cfg {Roo.menu.Menu} menu
38642      * A Sub menu
38643      */
38644     /**
38645      * @cfg {String} text
38646      * The text to show on the menu item.
38647      */
38648     text: '',
38649      /**
38650      * @cfg {String} HTML to render in menu
38651      * The text to show on the menu item (HTML version).
38652      */
38653     html: '',
38654     /**
38655      * @cfg {String} icon
38656      * The path to an icon to display in this menu item (defaults to Roo.BLANK_IMAGE_URL)
38657      */
38658     icon: undefined,
38659     /**
38660      * @cfg {String} itemCls The default CSS class to use for menu items (defaults to "x-menu-item")
38661      */
38662     itemCls : "x-menu-item",
38663     /**
38664      * @cfg {Boolean} canActivate True if this item can be visually activated (defaults to true)
38665      */
38666     canActivate : true,
38667     /**
38668      * @cfg {Number} showDelay Length of time in milliseconds to wait before showing this item (defaults to 200)
38669      */
38670     showDelay: 200,
38671     // doc'd in BaseItem
38672     hideDelay: 200,
38673
38674     // private
38675     ctype: "Roo.menu.Item",
38676     
38677     // private
38678     onRender : function(container, position){
38679         var el = document.createElement("a");
38680         el.hideFocus = true;
38681         el.unselectable = "on";
38682         el.href = this.href || "#";
38683         if(this.hrefTarget){
38684             el.target = this.hrefTarget;
38685         }
38686         el.className = this.itemCls + (this.menu ?  " x-menu-item-arrow" : "") + (this.cls ?  " " + this.cls : "");
38687         
38688         var html = this.html.length ? this.html  : String.format('{0}',this.text);
38689         
38690         el.innerHTML = String.format(
38691                 '<img src="{0}" class="x-menu-item-icon {1}" />' + html,
38692                 this.icon || Roo.BLANK_IMAGE_URL, this.iconCls || '');
38693         this.el = el;
38694         Roo.menu.Item.superclass.onRender.call(this, container, position);
38695     },
38696
38697     /**
38698      * Sets the text to display in this menu item
38699      * @param {String} text The text to display
38700      * @param {Boolean} isHTML true to indicate text is pure html.
38701      */
38702     setText : function(text, isHTML){
38703         if (isHTML) {
38704             this.html = text;
38705         } else {
38706             this.text = text;
38707             this.html = '';
38708         }
38709         if(this.rendered){
38710             var html = this.html.length ? this.html  : String.format('{0}',this.text);
38711      
38712             this.el.update(String.format(
38713                 '<img src="{0}" class="x-menu-item-icon {2}">' + html,
38714                 this.icon || Roo.BLANK_IMAGE_URL, this.text, this.iconCls || ''));
38715             this.parentMenu.autoWidth();
38716         }
38717     },
38718
38719     // private
38720     handleClick : function(e){
38721         if(!this.href){ // if no link defined, stop the event automatically
38722             e.stopEvent();
38723         }
38724         Roo.menu.Item.superclass.handleClick.apply(this, arguments);
38725     },
38726
38727     // private
38728     activate : function(autoExpand){
38729         if(Roo.menu.Item.superclass.activate.apply(this, arguments)){
38730             this.focus();
38731             if(autoExpand){
38732                 this.expandMenu();
38733             }
38734         }
38735         return true;
38736     },
38737
38738     // private
38739     shouldDeactivate : function(e){
38740         if(Roo.menu.Item.superclass.shouldDeactivate.call(this, e)){
38741             if(this.menu && this.menu.isVisible()){
38742                 return !this.menu.getEl().getRegion().contains(e.getPoint());
38743             }
38744             return true;
38745         }
38746         return false;
38747     },
38748
38749     // private
38750     deactivate : function(){
38751         Roo.menu.Item.superclass.deactivate.apply(this, arguments);
38752         this.hideMenu();
38753     },
38754
38755     // private
38756     expandMenu : function(autoActivate){
38757         if(!this.disabled && this.menu){
38758             clearTimeout(this.hideTimer);
38759             delete this.hideTimer;
38760             if(!this.menu.isVisible() && !this.showTimer){
38761                 this.showTimer = this.deferExpand.defer(this.showDelay, this, [autoActivate]);
38762             }else if (this.menu.isVisible() && autoActivate){
38763                 this.menu.tryActivate(0, 1);
38764             }
38765         }
38766     },
38767
38768     // private
38769     deferExpand : function(autoActivate){
38770         delete this.showTimer;
38771         this.menu.show(this.container, this.parentMenu.subMenuAlign || "tl-tr?", this.parentMenu);
38772         if(autoActivate){
38773             this.menu.tryActivate(0, 1);
38774         }
38775     },
38776
38777     // private
38778     hideMenu : function(){
38779         clearTimeout(this.showTimer);
38780         delete this.showTimer;
38781         if(!this.hideTimer && this.menu && this.menu.isVisible()){
38782             this.hideTimer = this.deferHide.defer(this.hideDelay, this);
38783         }
38784     },
38785
38786     // private
38787     deferHide : function(){
38788         delete this.hideTimer;
38789         this.menu.hide();
38790     }
38791 });/*
38792  * Based on:
38793  * Ext JS Library 1.1.1
38794  * Copyright(c) 2006-2007, Ext JS, LLC.
38795  *
38796  * Originally Released Under LGPL - original licence link has changed is not relivant.
38797  *
38798  * Fork - LGPL
38799  * <script type="text/javascript">
38800  */
38801  
38802 /**
38803  * @class Roo.menu.CheckItem
38804  * @extends Roo.menu.Item
38805  * Adds a menu item that contains a checkbox by default, but can also be part of a radio group.
38806  * @constructor
38807  * Creates a new CheckItem
38808  * @param {Object} config Configuration options
38809  */
38810 Roo.menu.CheckItem = function(config){
38811     Roo.menu.CheckItem.superclass.constructor.call(this, config);
38812     this.addEvents({
38813         /**
38814          * @event beforecheckchange
38815          * Fires before the checked value is set, providing an opportunity to cancel if needed
38816          * @param {Roo.menu.CheckItem} this
38817          * @param {Boolean} checked The new checked value that will be set
38818          */
38819         "beforecheckchange" : true,
38820         /**
38821          * @event checkchange
38822          * Fires after the checked value has been set
38823          * @param {Roo.menu.CheckItem} this
38824          * @param {Boolean} checked The checked value that was set
38825          */
38826         "checkchange" : true
38827     });
38828     if(this.checkHandler){
38829         this.on('checkchange', this.checkHandler, this.scope);
38830     }
38831 };
38832 Roo.extend(Roo.menu.CheckItem, Roo.menu.Item, {
38833     /**
38834      * @cfg {String} group
38835      * All check items with the same group name will automatically be grouped into a single-select
38836      * radio button group (defaults to '')
38837      */
38838     /**
38839      * @cfg {String} itemCls The default CSS class to use for check items (defaults to "x-menu-item x-menu-check-item")
38840      */
38841     itemCls : "x-menu-item x-menu-check-item",
38842     /**
38843      * @cfg {String} groupClass The default CSS class to use for radio group check items (defaults to "x-menu-group-item")
38844      */
38845     groupClass : "x-menu-group-item",
38846
38847     /**
38848      * @cfg {Boolean} checked True to initialize this checkbox as checked (defaults to false).  Note that
38849      * if this checkbox is part of a radio group (group = true) only the last item in the group that is
38850      * initialized with checked = true will be rendered as checked.
38851      */
38852     checked: false,
38853
38854     // private
38855     ctype: "Roo.menu.CheckItem",
38856
38857     // private
38858     onRender : function(c){
38859         Roo.menu.CheckItem.superclass.onRender.apply(this, arguments);
38860         if(this.group){
38861             this.el.addClass(this.groupClass);
38862         }
38863         Roo.menu.MenuMgr.registerCheckable(this);
38864         if(this.checked){
38865             this.checked = false;
38866             this.setChecked(true, true);
38867         }
38868     },
38869
38870     // private
38871     destroy : function(){
38872         if(this.rendered){
38873             Roo.menu.MenuMgr.unregisterCheckable(this);
38874         }
38875         Roo.menu.CheckItem.superclass.destroy.apply(this, arguments);
38876     },
38877
38878     /**
38879      * Set the checked state of this item
38880      * @param {Boolean} checked The new checked value
38881      * @param {Boolean} suppressEvent (optional) True to prevent the checkchange event from firing (defaults to false)
38882      */
38883     setChecked : function(state, suppressEvent){
38884         if(this.checked != state && this.fireEvent("beforecheckchange", this, state) !== false){
38885             if(this.container){
38886                 this.container[state ? "addClass" : "removeClass"]("x-menu-item-checked");
38887             }
38888             this.checked = state;
38889             if(suppressEvent !== true){
38890                 this.fireEvent("checkchange", this, state);
38891             }
38892         }
38893     },
38894
38895     // private
38896     handleClick : function(e){
38897        if(!this.disabled && !(this.checked && this.group)){// disable unselect on radio item
38898            this.setChecked(!this.checked);
38899        }
38900        Roo.menu.CheckItem.superclass.handleClick.apply(this, arguments);
38901     }
38902 });/*
38903  * Based on:
38904  * Ext JS Library 1.1.1
38905  * Copyright(c) 2006-2007, Ext JS, LLC.
38906  *
38907  * Originally Released Under LGPL - original licence link has changed is not relivant.
38908  *
38909  * Fork - LGPL
38910  * <script type="text/javascript">
38911  */
38912  
38913 /**
38914  * @class Roo.menu.DateItem
38915  * @extends Roo.menu.Adapter
38916  * A menu item that wraps the {@link Roo.DatPicker} component.
38917  * @constructor
38918  * Creates a new DateItem
38919  * @param {Object} config Configuration options
38920  */
38921 Roo.menu.DateItem = function(config){
38922     Roo.menu.DateItem.superclass.constructor.call(this, new Roo.DatePicker(config), config);
38923     /** The Roo.DatePicker object @type Roo.DatePicker */
38924     this.picker = this.component;
38925     this.addEvents({select: true});
38926     
38927     this.picker.on("render", function(picker){
38928         picker.getEl().swallowEvent("click");
38929         picker.container.addClass("x-menu-date-item");
38930     });
38931
38932     this.picker.on("select", this.onSelect, this);
38933 };
38934
38935 Roo.extend(Roo.menu.DateItem, Roo.menu.Adapter, {
38936     // private
38937     onSelect : function(picker, date){
38938         this.fireEvent("select", this, date, picker);
38939         Roo.menu.DateItem.superclass.handleClick.call(this);
38940     }
38941 });/*
38942  * Based on:
38943  * Ext JS Library 1.1.1
38944  * Copyright(c) 2006-2007, Ext JS, LLC.
38945  *
38946  * Originally Released Under LGPL - original licence link has changed is not relivant.
38947  *
38948  * Fork - LGPL
38949  * <script type="text/javascript">
38950  */
38951  
38952 /**
38953  * @class Roo.menu.ColorItem
38954  * @extends Roo.menu.Adapter
38955  * A menu item that wraps the {@link Roo.ColorPalette} component.
38956  * @constructor
38957  * Creates a new ColorItem
38958  * @param {Object} config Configuration options
38959  */
38960 Roo.menu.ColorItem = function(config){
38961     Roo.menu.ColorItem.superclass.constructor.call(this, new Roo.ColorPalette(config), config);
38962     /** The Roo.ColorPalette object @type Roo.ColorPalette */
38963     this.palette = this.component;
38964     this.relayEvents(this.palette, ["select"]);
38965     if(this.selectHandler){
38966         this.on('select', this.selectHandler, this.scope);
38967     }
38968 };
38969 Roo.extend(Roo.menu.ColorItem, Roo.menu.Adapter);/*
38970  * Based on:
38971  * Ext JS Library 1.1.1
38972  * Copyright(c) 2006-2007, Ext JS, LLC.
38973  *
38974  * Originally Released Under LGPL - original licence link has changed is not relivant.
38975  *
38976  * Fork - LGPL
38977  * <script type="text/javascript">
38978  */
38979  
38980
38981 /**
38982  * @class Roo.menu.DateMenu
38983  * @extends Roo.menu.Menu
38984  * A menu containing a {@link Roo.menu.DateItem} component (which provides a date picker).
38985  * @constructor
38986  * Creates a new DateMenu
38987  * @param {Object} config Configuration options
38988  */
38989 Roo.menu.DateMenu = function(config){
38990     Roo.menu.DateMenu.superclass.constructor.call(this, config);
38991     this.plain = true;
38992     var di = new Roo.menu.DateItem(config);
38993     this.add(di);
38994     /**
38995      * The {@link Roo.DatePicker} instance for this DateMenu
38996      * @type DatePicker
38997      */
38998     this.picker = di.picker;
38999     /**
39000      * @event select
39001      * @param {DatePicker} picker
39002      * @param {Date} date
39003      */
39004     this.relayEvents(di, ["select"]);
39005     this.on('beforeshow', function(){
39006         if(this.picker){
39007             this.picker.hideMonthPicker(false);
39008         }
39009     }, this);
39010 };
39011 Roo.extend(Roo.menu.DateMenu, Roo.menu.Menu, {
39012     cls:'x-date-menu'
39013 });/*
39014  * Based on:
39015  * Ext JS Library 1.1.1
39016  * Copyright(c) 2006-2007, Ext JS, LLC.
39017  *
39018  * Originally Released Under LGPL - original licence link has changed is not relivant.
39019  *
39020  * Fork - LGPL
39021  * <script type="text/javascript">
39022  */
39023  
39024
39025 /**
39026  * @class Roo.menu.ColorMenu
39027  * @extends Roo.menu.Menu
39028  * A menu containing a {@link Roo.menu.ColorItem} component (which provides a basic color picker).
39029  * @constructor
39030  * Creates a new ColorMenu
39031  * @param {Object} config Configuration options
39032  */
39033 Roo.menu.ColorMenu = function(config){
39034     Roo.menu.ColorMenu.superclass.constructor.call(this, config);
39035     this.plain = true;
39036     var ci = new Roo.menu.ColorItem(config);
39037     this.add(ci);
39038     /**
39039      * The {@link Roo.ColorPalette} instance for this ColorMenu
39040      * @type ColorPalette
39041      */
39042     this.palette = ci.palette;
39043     /**
39044      * @event select
39045      * @param {ColorPalette} palette
39046      * @param {String} color
39047      */
39048     this.relayEvents(ci, ["select"]);
39049 };
39050 Roo.extend(Roo.menu.ColorMenu, Roo.menu.Menu);/*
39051  * Based on:
39052  * Ext JS Library 1.1.1
39053  * Copyright(c) 2006-2007, Ext JS, LLC.
39054  *
39055  * Originally Released Under LGPL - original licence link has changed is not relivant.
39056  *
39057  * Fork - LGPL
39058  * <script type="text/javascript">
39059  */
39060  
39061 /**
39062  * @class Roo.form.TextItem
39063  * @extends Roo.BoxComponent
39064  * Base class for form fields that provides default event handling, sizing, value handling and other functionality.
39065  * @constructor
39066  * Creates a new TextItem
39067  * @param {Object} config Configuration options
39068  */
39069 Roo.form.TextItem = function(config){
39070     Roo.form.TextItem.superclass.constructor.call(this, config);
39071 };
39072
39073 Roo.extend(Roo.form.TextItem, Roo.BoxComponent,  {
39074     
39075     /**
39076      * @cfg {String} tag the tag for this item (default div)
39077      */
39078     tag : 'div',
39079     /**
39080      * @cfg {String} html the content for this item
39081      */
39082     html : '',
39083     
39084     getAutoCreate : function()
39085     {
39086         var cfg = {
39087             id: this.id,
39088             tag: this.tag,
39089             html: this.html,
39090             cls: 'x-form-item'
39091         };
39092         
39093         return cfg;
39094         
39095     },
39096     
39097     onRender : function(ct, position)
39098     {
39099         Roo.form.TextItem.superclass.onRender.call(this, ct, position);
39100         
39101         if(!this.el){
39102             var cfg = this.getAutoCreate();
39103             if(!cfg.name){
39104                 cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
39105             }
39106             if (!cfg.name.length) {
39107                 delete cfg.name;
39108             }
39109             this.el = ct.createChild(cfg, position);
39110         }
39111     },
39112     /*
39113      * setHTML
39114      * @param {String} html update the Contents of the element.
39115      */
39116     setHTML : function(html)
39117     {
39118         this.fieldEl.dom.innerHTML = html;
39119     }
39120     
39121 });/*
39122  * Based on:
39123  * Ext JS Library 1.1.1
39124  * Copyright(c) 2006-2007, Ext JS, LLC.
39125  *
39126  * Originally Released Under LGPL - original licence link has changed is not relivant.
39127  *
39128  * Fork - LGPL
39129  * <script type="text/javascript">
39130  */
39131  
39132 /**
39133  * @class Roo.form.Field
39134  * @extends Roo.BoxComponent
39135  * Base class for form fields that provides default event handling, sizing, value handling and other functionality.
39136  * @constructor
39137  * Creates a new Field
39138  * @param {Object} config Configuration options
39139  */
39140 Roo.form.Field = function(config){
39141     Roo.form.Field.superclass.constructor.call(this, config);
39142 };
39143
39144 Roo.extend(Roo.form.Field, Roo.BoxComponent,  {
39145     /**
39146      * @cfg {String} fieldLabel Label to use when rendering a form.
39147      */
39148        /**
39149      * @cfg {String} qtip Mouse over tip
39150      */
39151      
39152     /**
39153      * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
39154      */
39155     invalidClass : "x-form-invalid",
39156     /**
39157      * @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")
39158      */
39159     invalidText : "The value in this field is invalid",
39160     /**
39161      * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
39162      */
39163     focusClass : "x-form-focus",
39164     /**
39165      * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
39166       automatic validation (defaults to "keyup").
39167      */
39168     validationEvent : "keyup",
39169     /**
39170      * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
39171      */
39172     validateOnBlur : true,
39173     /**
39174      * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
39175      */
39176     validationDelay : 250,
39177     /**
39178      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
39179      * {tag: "input", type: "text", size: "20", autocomplete: "off"})
39180      */
39181     defaultAutoCreate : {tag: "input", type: "text", size: "20", autocomplete: "new-password"},
39182     /**
39183      * @cfg {String} fieldClass The default CSS class for the field (defaults to "x-form-field")
39184      */
39185     fieldClass : "x-form-field",
39186     /**
39187      * @cfg {String} msgTarget The location where error text should display.  Should be one of the following values (defaults to 'qtip'):
39188      *<pre>
39189 Value         Description
39190 -----------   ----------------------------------------------------------------------
39191 qtip          Display a quick tip when the user hovers over the field
39192 title         Display a default browser title attribute popup
39193 under         Add a block div beneath the field containing the error text
39194 side          Add an error icon to the right of the field with a popup on hover
39195 [element id]  Add the error text directly to the innerHTML of the specified element
39196 </pre>
39197      */
39198     msgTarget : 'qtip',
39199     /**
39200      * @cfg {String} msgFx <b>Experimental</b> The effect used when displaying a validation message under the field (defaults to 'normal').
39201      */
39202     msgFx : 'normal',
39203
39204     /**
39205      * @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.
39206      */
39207     readOnly : false,
39208
39209     /**
39210      * @cfg {Boolean} disabled True to disable the field (defaults to false).
39211      */
39212     disabled : false,
39213
39214     /**
39215      * @cfg {String} inputType The type attribute for input fields -- e.g. radio, text, password (defaults to "text").
39216      */
39217     inputType : undefined,
39218     
39219     /**
39220      * @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).
39221          */
39222         tabIndex : undefined,
39223         
39224     // private
39225     isFormField : true,
39226
39227     // private
39228     hasFocus : false,
39229     /**
39230      * @property {Roo.Element} fieldEl
39231      * Element Containing the rendered Field (with label etc.)
39232      */
39233     /**
39234      * @cfg {Mixed} value A value to initialize this field with.
39235      */
39236     value : undefined,
39237
39238     /**
39239      * @cfg {String} name The field's HTML name attribute.
39240      */
39241     /**
39242      * @cfg {String} cls A CSS class to apply to the field's underlying element.
39243      */
39244     // private
39245     loadedValue : false,
39246      
39247      
39248         // private ??
39249         initComponent : function(){
39250         Roo.form.Field.superclass.initComponent.call(this);
39251         this.addEvents({
39252             /**
39253              * @event focus
39254              * Fires when this field receives input focus.
39255              * @param {Roo.form.Field} this
39256              */
39257             focus : true,
39258             /**
39259              * @event blur
39260              * Fires when this field loses input focus.
39261              * @param {Roo.form.Field} this
39262              */
39263             blur : true,
39264             /**
39265              * @event specialkey
39266              * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
39267              * {@link Roo.EventObject#getKey} to determine which key was pressed.
39268              * @param {Roo.form.Field} this
39269              * @param {Roo.EventObject} e The event object
39270              */
39271             specialkey : true,
39272             /**
39273              * @event change
39274              * Fires just before the field blurs if the field value has changed.
39275              * @param {Roo.form.Field} this
39276              * @param {Mixed} newValue The new value
39277              * @param {Mixed} oldValue The original value
39278              */
39279             change : true,
39280             /**
39281              * @event invalid
39282              * Fires after the field has been marked as invalid.
39283              * @param {Roo.form.Field} this
39284              * @param {String} msg The validation message
39285              */
39286             invalid : true,
39287             /**
39288              * @event valid
39289              * Fires after the field has been validated with no errors.
39290              * @param {Roo.form.Field} this
39291              */
39292             valid : true,
39293              /**
39294              * @event keyup
39295              * Fires after the key up
39296              * @param {Roo.form.Field} this
39297              * @param {Roo.EventObject}  e The event Object
39298              */
39299             keyup : true
39300         });
39301     },
39302
39303     /**
39304      * Returns the name attribute of the field if available
39305      * @return {String} name The field name
39306      */
39307     getName: function(){
39308          return this.rendered && this.el.dom.name ? this.el.dom.name : (this.hiddenName || '');
39309     },
39310
39311     // private
39312     onRender : function(ct, position){
39313         Roo.form.Field.superclass.onRender.call(this, ct, position);
39314         if(!this.el){
39315             var cfg = this.getAutoCreate();
39316             if(!cfg.name){
39317                 cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
39318             }
39319             if (!cfg.name.length) {
39320                 delete cfg.name;
39321             }
39322             if(this.inputType){
39323                 cfg.type = this.inputType;
39324             }
39325             this.el = ct.createChild(cfg, position);
39326         }
39327         var type = this.el.dom.type;
39328         if(type){
39329             if(type == 'password'){
39330                 type = 'text';
39331             }
39332             this.el.addClass('x-form-'+type);
39333         }
39334         if(this.readOnly){
39335             this.el.dom.readOnly = true;
39336         }
39337         if(this.tabIndex !== undefined){
39338             this.el.dom.setAttribute('tabIndex', this.tabIndex);
39339         }
39340
39341         this.el.addClass([this.fieldClass, this.cls]);
39342         this.initValue();
39343     },
39344
39345     /**
39346      * Apply the behaviors of this component to an existing element. <b>This is used instead of render().</b>
39347      * @param {String/HTMLElement/Element} el The id of the node, a DOM node or an existing Element
39348      * @return {Roo.form.Field} this
39349      */
39350     applyTo : function(target){
39351         this.allowDomMove = false;
39352         this.el = Roo.get(target);
39353         this.render(this.el.dom.parentNode);
39354         return this;
39355     },
39356
39357     // private
39358     initValue : function(){
39359         if(this.value !== undefined){
39360             this.setValue(this.value);
39361         }else if(this.el.dom.value.length > 0){
39362             this.setValue(this.el.dom.value);
39363         }
39364     },
39365
39366     /**
39367      * Returns true if this field has been changed since it was originally loaded and is not disabled.
39368      * DEPRICATED  - it never worked well - use hasChanged/resetHasChanged.
39369      */
39370     isDirty : function() {
39371         if(this.disabled) {
39372             return false;
39373         }
39374         return String(this.getValue()) !== String(this.originalValue);
39375     },
39376
39377     /**
39378      * stores the current value in loadedValue
39379      */
39380     resetHasChanged : function()
39381     {
39382         this.loadedValue = String(this.getValue());
39383     },
39384     /**
39385      * checks the current value against the 'loaded' value.
39386      * Note - will return false if 'resetHasChanged' has not been called first.
39387      */
39388     hasChanged : function()
39389     {
39390         if(this.disabled || this.readOnly) {
39391             return false;
39392         }
39393         return this.loadedValue !== false && String(this.getValue()) !== this.loadedValue;
39394     },
39395     
39396     
39397     
39398     // private
39399     afterRender : function(){
39400         Roo.form.Field.superclass.afterRender.call(this);
39401         this.initEvents();
39402     },
39403
39404     // private
39405     fireKey : function(e){
39406         //Roo.log('field ' + e.getKey());
39407         if(e.isNavKeyPress()){
39408             this.fireEvent("specialkey", this, e);
39409         }
39410     },
39411
39412     /**
39413      * Resets the current field value to the originally loaded value and clears any validation messages
39414      */
39415     reset : function(){
39416         this.setValue(this.resetValue);
39417         this.originalValue = this.getValue();
39418         this.clearInvalid();
39419     },
39420
39421     // private
39422     initEvents : function(){
39423         // safari killled keypress - so keydown is now used..
39424         this.el.on("keydown" , this.fireKey,  this);
39425         this.el.on("focus", this.onFocus,  this);
39426         this.el.on("blur", this.onBlur,  this);
39427         this.el.relayEvent('keyup', this);
39428
39429         // reference to original value for reset
39430         this.originalValue = this.getValue();
39431         this.resetValue =  this.getValue();
39432     },
39433
39434     // private
39435     onFocus : function(){
39436         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
39437             this.el.addClass(this.focusClass);
39438         }
39439         if(!this.hasFocus){
39440             this.hasFocus = true;
39441             this.startValue = this.getValue();
39442             this.fireEvent("focus", this);
39443         }
39444     },
39445
39446     beforeBlur : Roo.emptyFn,
39447
39448     // private
39449     onBlur : function(){
39450         this.beforeBlur();
39451         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
39452             this.el.removeClass(this.focusClass);
39453         }
39454         this.hasFocus = false;
39455         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
39456             this.validate();
39457         }
39458         var v = this.getValue();
39459         if(String(v) !== String(this.startValue)){
39460             this.fireEvent('change', this, v, this.startValue);
39461         }
39462         this.fireEvent("blur", this);
39463     },
39464
39465     /**
39466      * Returns whether or not the field value is currently valid
39467      * @param {Boolean} preventMark True to disable marking the field invalid
39468      * @return {Boolean} True if the value is valid, else false
39469      */
39470     isValid : function(preventMark){
39471         if(this.disabled){
39472             return true;
39473         }
39474         var restore = this.preventMark;
39475         this.preventMark = preventMark === true;
39476         var v = this.validateValue(this.processValue(this.getRawValue()));
39477         this.preventMark = restore;
39478         return v;
39479     },
39480
39481     /**
39482      * Validates the field value
39483      * @return {Boolean} True if the value is valid, else false
39484      */
39485     validate : function(){
39486         if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
39487             this.clearInvalid();
39488             return true;
39489         }
39490         return false;
39491     },
39492
39493     processValue : function(value){
39494         return value;
39495     },
39496
39497     // private
39498     // Subclasses should provide the validation implementation by overriding this
39499     validateValue : function(value){
39500         return true;
39501     },
39502
39503     /**
39504      * Mark this field as invalid
39505      * @param {String} msg The validation message
39506      */
39507     markInvalid : function(msg){
39508         if(!this.rendered || this.preventMark){ // not rendered
39509             return;
39510         }
39511         
39512         var obj = (typeof(this.combo) != 'undefined') ? this.combo : this; // fix the combox array!!
39513         
39514         obj.el.addClass(this.invalidClass);
39515         msg = msg || this.invalidText;
39516         switch(this.msgTarget){
39517             case 'qtip':
39518                 obj.el.dom.qtip = msg;
39519                 obj.el.dom.qclass = 'x-form-invalid-tip';
39520                 if(Roo.QuickTips){ // fix for floating editors interacting with DND
39521                     Roo.QuickTips.enable();
39522                 }
39523                 break;
39524             case 'title':
39525                 this.el.dom.title = msg;
39526                 break;
39527             case 'under':
39528                 if(!this.errorEl){
39529                     var elp = this.el.findParent('.x-form-element', 5, true);
39530                     this.errorEl = elp.createChild({cls:'x-form-invalid-msg'});
39531                     this.errorEl.setWidth(elp.getWidth(true)-20);
39532                 }
39533                 this.errorEl.update(msg);
39534                 Roo.form.Field.msgFx[this.msgFx].show(this.errorEl, this);
39535                 break;
39536             case 'side':
39537                 if(!this.errorIcon){
39538                     var elp = this.el.findParent('.x-form-element', 5, true);
39539                     this.errorIcon = elp.createChild({cls:'x-form-invalid-icon'});
39540                 }
39541                 this.alignErrorIcon();
39542                 this.errorIcon.dom.qtip = msg;
39543                 this.errorIcon.dom.qclass = 'x-form-invalid-tip';
39544                 this.errorIcon.show();
39545                 this.on('resize', this.alignErrorIcon, this);
39546                 break;
39547             default:
39548                 var t = Roo.getDom(this.msgTarget);
39549                 t.innerHTML = msg;
39550                 t.style.display = this.msgDisplay;
39551                 break;
39552         }
39553         this.fireEvent('invalid', this, msg);
39554     },
39555
39556     // private
39557     alignErrorIcon : function(){
39558         this.errorIcon.alignTo(this.el, 'tl-tr', [2, 0]);
39559     },
39560
39561     /**
39562      * Clear any invalid styles/messages for this field
39563      */
39564     clearInvalid : function(){
39565         if(!this.rendered || this.preventMark){ // not rendered
39566             return;
39567         }
39568         var obj = (typeof(this.combo) != 'undefined') ? this.combo : this; // fix the combox array!!
39569         
39570         obj.el.removeClass(this.invalidClass);
39571         switch(this.msgTarget){
39572             case 'qtip':
39573                 obj.el.dom.qtip = '';
39574                 break;
39575             case 'title':
39576                 this.el.dom.title = '';
39577                 break;
39578             case 'under':
39579                 if(this.errorEl){
39580                     Roo.form.Field.msgFx[this.msgFx].hide(this.errorEl, this);
39581                 }
39582                 break;
39583             case 'side':
39584                 if(this.errorIcon){
39585                     this.errorIcon.dom.qtip = '';
39586                     this.errorIcon.hide();
39587                     this.un('resize', this.alignErrorIcon, this);
39588                 }
39589                 break;
39590             default:
39591                 var t = Roo.getDom(this.msgTarget);
39592                 t.innerHTML = '';
39593                 t.style.display = 'none';
39594                 break;
39595         }
39596         this.fireEvent('valid', this);
39597     },
39598
39599     /**
39600      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
39601      * @return {Mixed} value The field value
39602      */
39603     getRawValue : function(){
39604         var v = this.el.getValue();
39605         
39606         return v;
39607     },
39608
39609     /**
39610      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
39611      * @return {Mixed} value The field value
39612      */
39613     getValue : function(){
39614         var v = this.el.getValue();
39615          
39616         return v;
39617     },
39618
39619     /**
39620      * Sets the underlying DOM field's value directly, bypassing validation.  To set the value with validation see {@link #setValue}.
39621      * @param {Mixed} value The value to set
39622      */
39623     setRawValue : function(v){
39624         return this.el.dom.value = (v === null || v === undefined ? '' : v);
39625     },
39626
39627     /**
39628      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
39629      * @param {Mixed} value The value to set
39630      */
39631     setValue : function(v){
39632         this.value = v;
39633         if(this.rendered){
39634             this.el.dom.value = (v === null || v === undefined ? '' : v);
39635              this.validate();
39636         }
39637     },
39638
39639     adjustSize : function(w, h){
39640         var s = Roo.form.Field.superclass.adjustSize.call(this, w, h);
39641         s.width = this.adjustWidth(this.el.dom.tagName, s.width);
39642         return s;
39643     },
39644
39645     adjustWidth : function(tag, w){
39646         tag = tag.toLowerCase();
39647         if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
39648             if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
39649                 if(tag == 'input'){
39650                     return w + 2;
39651                 }
39652                 if(tag == 'textarea'){
39653                     return w-2;
39654                 }
39655             }else if(Roo.isOpera){
39656                 if(tag == 'input'){
39657                     return w + 2;
39658                 }
39659                 if(tag == 'textarea'){
39660                     return w-2;
39661                 }
39662             }
39663         }
39664         return w;
39665     }
39666 });
39667
39668
39669 // anything other than normal should be considered experimental
39670 Roo.form.Field.msgFx = {
39671     normal : {
39672         show: function(msgEl, f){
39673             msgEl.setDisplayed('block');
39674         },
39675
39676         hide : function(msgEl, f){
39677             msgEl.setDisplayed(false).update('');
39678         }
39679     },
39680
39681     slide : {
39682         show: function(msgEl, f){
39683             msgEl.slideIn('t', {stopFx:true});
39684         },
39685
39686         hide : function(msgEl, f){
39687             msgEl.slideOut('t', {stopFx:true,useDisplay:true});
39688         }
39689     },
39690
39691     slideRight : {
39692         show: function(msgEl, f){
39693             msgEl.fixDisplay();
39694             msgEl.alignTo(f.el, 'tl-tr');
39695             msgEl.slideIn('l', {stopFx:true});
39696         },
39697
39698         hide : function(msgEl, f){
39699             msgEl.slideOut('l', {stopFx:true,useDisplay:true});
39700         }
39701     }
39702 };/*
39703  * Based on:
39704  * Ext JS Library 1.1.1
39705  * Copyright(c) 2006-2007, Ext JS, LLC.
39706  *
39707  * Originally Released Under LGPL - original licence link has changed is not relivant.
39708  *
39709  * Fork - LGPL
39710  * <script type="text/javascript">
39711  */
39712  
39713
39714 /**
39715  * @class Roo.form.TextField
39716  * @extends Roo.form.Field
39717  * Basic text field.  Can be used as a direct replacement for traditional text inputs, or as the base
39718  * class for more sophisticated input controls (like {@link Roo.form.TextArea} and {@link Roo.form.ComboBox}).
39719  * @constructor
39720  * Creates a new TextField
39721  * @param {Object} config Configuration options
39722  */
39723 Roo.form.TextField = function(config){
39724     Roo.form.TextField.superclass.constructor.call(this, config);
39725     this.addEvents({
39726         /**
39727          * @event autosize
39728          * Fires when the autosize function is triggered.  The field may or may not have actually changed size
39729          * according to the default logic, but this event provides a hook for the developer to apply additional
39730          * logic at runtime to resize the field if needed.
39731              * @param {Roo.form.Field} this This text field
39732              * @param {Number} width The new field width
39733              */
39734         autosize : true
39735     });
39736 };
39737
39738 Roo.extend(Roo.form.TextField, Roo.form.Field,  {
39739     /**
39740      * @cfg {Boolean} grow True if this field should automatically grow and shrink to its content
39741      */
39742     grow : false,
39743     /**
39744      * @cfg {Number} growMin The minimum width to allow when grow = true (defaults to 30)
39745      */
39746     growMin : 30,
39747     /**
39748      * @cfg {Number} growMax The maximum width to allow when grow = true (defaults to 800)
39749      */
39750     growMax : 800,
39751     /**
39752      * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
39753      */
39754     vtype : null,
39755     /**
39756      * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
39757      */
39758     maskRe : null,
39759     /**
39760      * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
39761      */
39762     disableKeyFilter : false,
39763     /**
39764      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
39765      */
39766     allowBlank : true,
39767     /**
39768      * @cfg {Number} minLength Minimum input field length required (defaults to 0)
39769      */
39770     minLength : 0,
39771     /**
39772      * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
39773      */
39774     maxLength : Number.MAX_VALUE,
39775     /**
39776      * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
39777      */
39778     minLengthText : "The minimum length for this field is {0}",
39779     /**
39780      * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
39781      */
39782     maxLengthText : "The maximum length for this field is {0}",
39783     /**
39784      * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
39785      */
39786     selectOnFocus : false,
39787     /**
39788      * @cfg {Boolean} allowLeadingSpace True to prevent the stripping of leading white space 
39789      */    
39790     allowLeadingSpace : false,
39791     /**
39792      * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
39793      */
39794     blankText : "This field is required",
39795     /**
39796      * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
39797      * If available, this function will be called only after the basic validators all return true, and will be passed the
39798      * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
39799      */
39800     validator : null,
39801     /**
39802      * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
39803      * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
39804      * current field value.  If the test fails, the field will be marked invalid using {@link #regexText}.
39805      */
39806     regex : null,
39807     /**
39808      * @cfg {String} regexText The error text to display if {@link #regex} is used and the test fails during validation (defaults to "")
39809      */
39810     regexText : "",
39811     /**
39812      * @cfg {String} emptyText The default text to display in an empty field - placeholder... (defaults to null).
39813      */
39814     emptyText : null,
39815    
39816
39817     // private
39818     initEvents : function()
39819     {
39820         if (this.emptyText) {
39821             this.el.attr('placeholder', this.emptyText);
39822         }
39823         
39824         Roo.form.TextField.superclass.initEvents.call(this);
39825         if(this.validationEvent == 'keyup'){
39826             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
39827             this.el.on('keyup', this.filterValidation, this);
39828         }
39829         else if(this.validationEvent !== false){
39830             this.el.on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
39831         }
39832         
39833         if(this.selectOnFocus){
39834             this.on("focus", this.preFocus, this);
39835         }
39836         if (!this.allowLeadingSpace) {
39837             this.on('blur', this.cleanLeadingSpace, this);
39838         }
39839         
39840         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
39841             this.el.on("keypress", this.filterKeys, this);
39842         }
39843         if(this.grow){
39844             this.el.on("keyup", this.onKeyUp,  this, {buffer:50});
39845             this.el.on("click", this.autoSize,  this);
39846         }
39847         if(this.el.is('input[type=password]') && Roo.isSafari){
39848             this.el.on('keydown', this.SafariOnKeyDown, this);
39849         }
39850     },
39851
39852     processValue : function(value){
39853         if(this.stripCharsRe){
39854             var newValue = value.replace(this.stripCharsRe, '');
39855             if(newValue !== value){
39856                 this.setRawValue(newValue);
39857                 return newValue;
39858             }
39859         }
39860         return value;
39861     },
39862
39863     filterValidation : function(e){
39864         if(!e.isNavKeyPress()){
39865             this.validationTask.delay(this.validationDelay);
39866         }
39867     },
39868
39869     // private
39870     onKeyUp : function(e){
39871         if(!e.isNavKeyPress()){
39872             this.autoSize();
39873         }
39874     },
39875     // private - clean the leading white space
39876     cleanLeadingSpace : function(e)
39877     {
39878         if ( this.inputType == 'file') {
39879             return;
39880         }
39881         
39882         this.setValue((this.getValue() + '').replace(/^\s+/,''));
39883     },
39884     /**
39885      * Resets the current field value to the originally-loaded value and clears any validation messages.
39886      *  
39887      */
39888     reset : function(){
39889         Roo.form.TextField.superclass.reset.call(this);
39890        
39891     }, 
39892     // private
39893     preFocus : function(){
39894         
39895         if(this.selectOnFocus){
39896             this.el.dom.select();
39897         }
39898     },
39899
39900     
39901     // private
39902     filterKeys : function(e){
39903         var k = e.getKey();
39904         if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
39905             return;
39906         }
39907         var c = e.getCharCode(), cc = String.fromCharCode(c);
39908         if(Roo.isIE && (e.isSpecialKey() || !cc)){
39909             return;
39910         }
39911         if(!this.maskRe.test(cc)){
39912             e.stopEvent();
39913         }
39914     },
39915
39916     setValue : function(v){
39917         
39918         Roo.form.TextField.superclass.setValue.apply(this, arguments);
39919         
39920         this.autoSize();
39921     },
39922
39923     /**
39924      * Validates a value according to the field's validation rules and marks the field as invalid
39925      * if the validation fails
39926      * @param {Mixed} value The value to validate
39927      * @return {Boolean} True if the value is valid, else false
39928      */
39929     validateValue : function(value){
39930         if(value.length < 1)  { // if it's blank
39931              if(this.allowBlank){
39932                 this.clearInvalid();
39933                 return true;
39934              }else{
39935                 this.markInvalid(this.blankText);
39936                 return false;
39937              }
39938         }
39939         if(value.length < this.minLength){
39940             this.markInvalid(String.format(this.minLengthText, this.minLength));
39941             return false;
39942         }
39943         if(value.length > this.maxLength){
39944             this.markInvalid(String.format(this.maxLengthText, this.maxLength));
39945             return false;
39946         }
39947         if(this.vtype){
39948             var vt = Roo.form.VTypes;
39949             if(!vt[this.vtype](value, this)){
39950                 this.markInvalid(this.vtypeText || vt[this.vtype +'Text']);
39951                 return false;
39952             }
39953         }
39954         if(typeof this.validator == "function"){
39955             var msg = this.validator(value);
39956             if(msg !== true){
39957                 this.markInvalid(msg);
39958                 return false;
39959             }
39960         }
39961         if(this.regex && !this.regex.test(value)){
39962             this.markInvalid(this.regexText);
39963             return false;
39964         }
39965         return true;
39966     },
39967
39968     /**
39969      * Selects text in this field
39970      * @param {Number} start (optional) The index where the selection should start (defaults to 0)
39971      * @param {Number} end (optional) The index where the selection should end (defaults to the text length)
39972      */
39973     selectText : function(start, end){
39974         var v = this.getRawValue();
39975         if(v.length > 0){
39976             start = start === undefined ? 0 : start;
39977             end = end === undefined ? v.length : end;
39978             var d = this.el.dom;
39979             if(d.setSelectionRange){
39980                 d.setSelectionRange(start, end);
39981             }else if(d.createTextRange){
39982                 var range = d.createTextRange();
39983                 range.moveStart("character", start);
39984                 range.moveEnd("character", v.length-end);
39985                 range.select();
39986             }
39987         }
39988     },
39989
39990     /**
39991      * Automatically grows the field to accomodate the width of the text up to the maximum field width allowed.
39992      * This only takes effect if grow = true, and fires the autosize event.
39993      */
39994     autoSize : function(){
39995         if(!this.grow || !this.rendered){
39996             return;
39997         }
39998         if(!this.metrics){
39999             this.metrics = Roo.util.TextMetrics.createInstance(this.el);
40000         }
40001         var el = this.el;
40002         var v = el.dom.value;
40003         var d = document.createElement('div');
40004         d.appendChild(document.createTextNode(v));
40005         v = d.innerHTML;
40006         d = null;
40007         v += "&#160;";
40008         var w = Math.min(this.growMax, Math.max(this.metrics.getWidth(v) + /* add extra padding */ 10, this.growMin));
40009         this.el.setWidth(w);
40010         this.fireEvent("autosize", this, w);
40011     },
40012     
40013     // private
40014     SafariOnKeyDown : function(event)
40015     {
40016         // this is a workaround for a password hang bug on chrome/ webkit.
40017         
40018         var isSelectAll = false;
40019         
40020         if(this.el.dom.selectionEnd > 0){
40021             isSelectAll = (this.el.dom.selectionEnd - this.el.dom.selectionStart - this.getValue().length == 0) ? true : false;
40022         }
40023         if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
40024             event.preventDefault();
40025             this.setValue('');
40026             return;
40027         }
40028         
40029         if(isSelectAll && event.getCharCode() > 31){ // backspace and delete key
40030             
40031             event.preventDefault();
40032             // this is very hacky as keydown always get's upper case.
40033             
40034             var cc = String.fromCharCode(event.getCharCode());
40035             
40036             
40037             this.setValue( event.shiftKey ?  cc : cc.toLowerCase());
40038             
40039         }
40040         
40041         
40042     }
40043 });/*
40044  * Based on:
40045  * Ext JS Library 1.1.1
40046  * Copyright(c) 2006-2007, Ext JS, LLC.
40047  *
40048  * Originally Released Under LGPL - original licence link has changed is not relivant.
40049  *
40050  * Fork - LGPL
40051  * <script type="text/javascript">
40052  */
40053  
40054 /**
40055  * @class Roo.form.Hidden
40056  * @extends Roo.form.TextField
40057  * Simple Hidden element used on forms 
40058  * 
40059  * usage: form.add(new Roo.form.HiddenField({ 'name' : 'test1' }));
40060  * 
40061  * @constructor
40062  * Creates a new Hidden form element.
40063  * @param {Object} config Configuration options
40064  */
40065
40066
40067
40068 // easy hidden field...
40069 Roo.form.Hidden = function(config){
40070     Roo.form.Hidden.superclass.constructor.call(this, config);
40071 };
40072   
40073 Roo.extend(Roo.form.Hidden, Roo.form.TextField, {
40074     fieldLabel:      '',
40075     inputType:      'hidden',
40076     width:          50,
40077     allowBlank:     true,
40078     labelSeparator: '',
40079     hidden:         true,
40080     itemCls :       'x-form-item-display-none'
40081
40082
40083 });
40084
40085
40086 /*
40087  * Based on:
40088  * Ext JS Library 1.1.1
40089  * Copyright(c) 2006-2007, Ext JS, LLC.
40090  *
40091  * Originally Released Under LGPL - original licence link has changed is not relivant.
40092  *
40093  * Fork - LGPL
40094  * <script type="text/javascript">
40095  */
40096  
40097 /**
40098  * @class Roo.form.TriggerField
40099  * @extends Roo.form.TextField
40100  * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
40101  * The trigger has no default action, so you must assign a function to implement the trigger click handler by
40102  * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
40103  * for which you can provide a custom implementation.  For example:
40104  * <pre><code>
40105 var trigger = new Roo.form.TriggerField();
40106 trigger.onTriggerClick = myTriggerFn;
40107 trigger.applyTo('my-field');
40108 </code></pre>
40109  *
40110  * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
40111  * {@link Roo.form.DateField} and {@link Roo.form.ComboBox} are perfect examples of this.
40112  * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
40113  * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
40114  * @constructor
40115  * Create a new TriggerField.
40116  * @param {Object} config Configuration options (valid {@Roo.form.TextField} config options will also be applied
40117  * to the base TextField)
40118  */
40119 Roo.form.TriggerField = function(config){
40120     this.mimicing = false;
40121     Roo.form.TriggerField.superclass.constructor.call(this, config);
40122 };
40123
40124 Roo.extend(Roo.form.TriggerField, Roo.form.TextField,  {
40125     /**
40126      * @cfg {String} triggerClass A CSS class to apply to the trigger
40127      */
40128     /**
40129      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
40130      * {tag: "input", type: "text", size: "16", autocomplete: "off"})
40131      */
40132     defaultAutoCreate : {tag: "input", type: "text", size: "16", autocomplete: "new-password"},
40133     /**
40134      * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
40135      */
40136     hideTrigger:false,
40137
40138     /** @cfg {Boolean} grow @hide */
40139     /** @cfg {Number} growMin @hide */
40140     /** @cfg {Number} growMax @hide */
40141
40142     /**
40143      * @hide 
40144      * @method
40145      */
40146     autoSize: Roo.emptyFn,
40147     // private
40148     monitorTab : true,
40149     // private
40150     deferHeight : true,
40151
40152     
40153     actionMode : 'wrap',
40154     // private
40155     onResize : function(w, h){
40156         Roo.form.TriggerField.superclass.onResize.apply(this, arguments);
40157         if(typeof w == 'number'){
40158             var x = w - this.trigger.getWidth();
40159             this.el.setWidth(this.adjustWidth('input', x));
40160             this.trigger.setStyle('left', x+'px');
40161         }
40162     },
40163
40164     // private
40165     adjustSize : Roo.BoxComponent.prototype.adjustSize,
40166
40167     // private
40168     getResizeEl : function(){
40169         return this.wrap;
40170     },
40171
40172     // private
40173     getPositionEl : function(){
40174         return this.wrap;
40175     },
40176
40177     // private
40178     alignErrorIcon : function(){
40179         this.errorIcon.alignTo(this.wrap, 'tl-tr', [2, 0]);
40180     },
40181
40182     // private
40183     onRender : function(ct, position){
40184         Roo.form.TriggerField.superclass.onRender.call(this, ct, position);
40185         this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
40186         this.trigger = this.wrap.createChild(this.triggerConfig ||
40187                 {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.triggerClass});
40188         if(this.hideTrigger){
40189             this.trigger.setDisplayed(false);
40190         }
40191         this.initTrigger();
40192         if(!this.width){
40193             this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
40194         }
40195     },
40196
40197     // private
40198     initTrigger : function(){
40199         this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
40200         this.trigger.addClassOnOver('x-form-trigger-over');
40201         this.trigger.addClassOnClick('x-form-trigger-click');
40202     },
40203
40204     // private
40205     onDestroy : function(){
40206         if(this.trigger){
40207             this.trigger.removeAllListeners();
40208             this.trigger.remove();
40209         }
40210         if(this.wrap){
40211             this.wrap.remove();
40212         }
40213         Roo.form.TriggerField.superclass.onDestroy.call(this);
40214     },
40215
40216     // private
40217     onFocus : function(){
40218         Roo.form.TriggerField.superclass.onFocus.call(this);
40219         if(!this.mimicing){
40220             this.wrap.addClass('x-trigger-wrap-focus');
40221             this.mimicing = true;
40222             Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
40223             if(this.monitorTab){
40224                 this.el.on("keydown", this.checkTab, this);
40225             }
40226         }
40227     },
40228
40229     // private
40230     checkTab : function(e){
40231         if(e.getKey() == e.TAB){
40232             this.triggerBlur();
40233         }
40234     },
40235
40236     // private
40237     onBlur : function(){
40238         // do nothing
40239     },
40240
40241     // private
40242     mimicBlur : function(e, t){
40243         if(!this.wrap.contains(t) && this.validateBlur()){
40244             this.triggerBlur();
40245         }
40246     },
40247
40248     // private
40249     triggerBlur : function(){
40250         this.mimicing = false;
40251         Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
40252         if(this.monitorTab){
40253             this.el.un("keydown", this.checkTab, this);
40254         }
40255         this.wrap.removeClass('x-trigger-wrap-focus');
40256         Roo.form.TriggerField.superclass.onBlur.call(this);
40257     },
40258
40259     // private
40260     // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
40261     validateBlur : function(e, t){
40262         return true;
40263     },
40264
40265     // private
40266     onDisable : function(){
40267         Roo.form.TriggerField.superclass.onDisable.call(this);
40268         if(this.wrap){
40269             this.wrap.addClass('x-item-disabled');
40270         }
40271     },
40272
40273     // private
40274     onEnable : function(){
40275         Roo.form.TriggerField.superclass.onEnable.call(this);
40276         if(this.wrap){
40277             this.wrap.removeClass('x-item-disabled');
40278         }
40279     },
40280
40281     // private
40282     onShow : function(){
40283         var ae = this.getActionEl();
40284         
40285         if(ae){
40286             ae.dom.style.display = '';
40287             ae.dom.style.visibility = 'visible';
40288         }
40289     },
40290
40291     // private
40292     
40293     onHide : function(){
40294         var ae = this.getActionEl();
40295         ae.dom.style.display = 'none';
40296     },
40297
40298     /**
40299      * The function that should handle the trigger's click event.  This method does nothing by default until overridden
40300      * by an implementing function.
40301      * @method
40302      * @param {EventObject} e
40303      */
40304     onTriggerClick : Roo.emptyFn
40305 });
40306
40307 // TwinTriggerField is not a public class to be used directly.  It is meant as an abstract base class
40308 // to be extended by an implementing class.  For an example of implementing this class, see the custom
40309 // SearchField implementation here: http://extjs.com/deploy/ext/examples/form/custom.html
40310 Roo.form.TwinTriggerField = Roo.extend(Roo.form.TriggerField, {
40311     initComponent : function(){
40312         Roo.form.TwinTriggerField.superclass.initComponent.call(this);
40313
40314         this.triggerConfig = {
40315             tag:'span', cls:'x-form-twin-triggers', cn:[
40316             {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.trigger1Class},
40317             {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.trigger2Class}
40318         ]};
40319     },
40320
40321     getTrigger : function(index){
40322         return this.triggers[index];
40323     },
40324
40325     initTrigger : function(){
40326         var ts = this.trigger.select('.x-form-trigger', true);
40327         this.wrap.setStyle('overflow', 'hidden');
40328         var triggerField = this;
40329         ts.each(function(t, all, index){
40330             t.hide = function(){
40331                 var w = triggerField.wrap.getWidth();
40332                 this.dom.style.display = 'none';
40333                 triggerField.el.setWidth(w-triggerField.trigger.getWidth());
40334             };
40335             t.show = function(){
40336                 var w = triggerField.wrap.getWidth();
40337                 this.dom.style.display = '';
40338                 triggerField.el.setWidth(w-triggerField.trigger.getWidth());
40339             };
40340             var triggerIndex = 'Trigger'+(index+1);
40341
40342             if(this['hide'+triggerIndex]){
40343                 t.dom.style.display = 'none';
40344             }
40345             t.on("click", this['on'+triggerIndex+'Click'], this, {preventDefault:true});
40346             t.addClassOnOver('x-form-trigger-over');
40347             t.addClassOnClick('x-form-trigger-click');
40348         }, this);
40349         this.triggers = ts.elements;
40350     },
40351
40352     onTrigger1Click : Roo.emptyFn,
40353     onTrigger2Click : Roo.emptyFn
40354 });/*
40355  * Based on:
40356  * Ext JS Library 1.1.1
40357  * Copyright(c) 2006-2007, Ext JS, LLC.
40358  *
40359  * Originally Released Under LGPL - original licence link has changed is not relivant.
40360  *
40361  * Fork - LGPL
40362  * <script type="text/javascript">
40363  */
40364  
40365 /**
40366  * @class Roo.form.TextArea
40367  * @extends Roo.form.TextField
40368  * Multiline text field.  Can be used as a direct replacement for traditional textarea fields, plus adds
40369  * support for auto-sizing.
40370  * @constructor
40371  * Creates a new TextArea
40372  * @param {Object} config Configuration options
40373  */
40374 Roo.form.TextArea = function(config){
40375     Roo.form.TextArea.superclass.constructor.call(this, config);
40376     // these are provided exchanges for backwards compat
40377     // minHeight/maxHeight were replaced by growMin/growMax to be
40378     // compatible with TextField growing config values
40379     if(this.minHeight !== undefined){
40380         this.growMin = this.minHeight;
40381     }
40382     if(this.maxHeight !== undefined){
40383         this.growMax = this.maxHeight;
40384     }
40385 };
40386
40387 Roo.extend(Roo.form.TextArea, Roo.form.TextField,  {
40388     /**
40389      * @cfg {Number} growMin The minimum height to allow when grow = true (defaults to 60)
40390      */
40391     growMin : 60,
40392     /**
40393      * @cfg {Number} growMax The maximum height to allow when grow = true (defaults to 1000)
40394      */
40395     growMax: 1000,
40396     /**
40397      * @cfg {Boolean} preventScrollbars True to prevent scrollbars from appearing regardless of how much text is
40398      * in the field (equivalent to setting overflow: hidden, defaults to false)
40399      */
40400     preventScrollbars: false,
40401     /**
40402      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
40403      * {tag: "textarea", style: "width:300px;height:60px;", autocomplete: "off"})
40404      */
40405
40406     // private
40407     onRender : function(ct, position){
40408         if(!this.el){
40409             this.defaultAutoCreate = {
40410                 tag: "textarea",
40411                 style:"width:300px;height:60px;",
40412                 autocomplete: "new-password"
40413             };
40414         }
40415         Roo.form.TextArea.superclass.onRender.call(this, ct, position);
40416         if(this.grow){
40417             this.textSizeEl = Roo.DomHelper.append(document.body, {
40418                 tag: "pre", cls: "x-form-grow-sizer"
40419             });
40420             if(this.preventScrollbars){
40421                 this.el.setStyle("overflow", "hidden");
40422             }
40423             this.el.setHeight(this.growMin);
40424         }
40425     },
40426
40427     onDestroy : function(){
40428         if(this.textSizeEl){
40429             this.textSizeEl.parentNode.removeChild(this.textSizeEl);
40430         }
40431         Roo.form.TextArea.superclass.onDestroy.call(this);
40432     },
40433
40434     // private
40435     onKeyUp : function(e){
40436         if(!e.isNavKeyPress() || e.getKey() == e.ENTER){
40437             this.autoSize();
40438         }
40439     },
40440
40441     /**
40442      * Automatically grows the field to accomodate the height of the text up to the maximum field height allowed.
40443      * This only takes effect if grow = true, and fires the autosize event if the height changes.
40444      */
40445     autoSize : function(){
40446         if(!this.grow || !this.textSizeEl){
40447             return;
40448         }
40449         var el = this.el;
40450         var v = el.dom.value;
40451         var ts = this.textSizeEl;
40452
40453         ts.innerHTML = '';
40454         ts.appendChild(document.createTextNode(v));
40455         v = ts.innerHTML;
40456
40457         Roo.fly(ts).setWidth(this.el.getWidth());
40458         if(v.length < 1){
40459             v = "&#160;&#160;";
40460         }else{
40461             if(Roo.isIE){
40462                 v = v.replace(/\n/g, '<p>&#160;</p>');
40463             }
40464             v += "&#160;\n&#160;";
40465         }
40466         ts.innerHTML = v;
40467         var h = Math.min(this.growMax, Math.max(ts.offsetHeight, this.growMin));
40468         if(h != this.lastHeight){
40469             this.lastHeight = h;
40470             this.el.setHeight(h);
40471             this.fireEvent("autosize", this, h);
40472         }
40473     }
40474 });/*
40475  * Based on:
40476  * Ext JS Library 1.1.1
40477  * Copyright(c) 2006-2007, Ext JS, LLC.
40478  *
40479  * Originally Released Under LGPL - original licence link has changed is not relivant.
40480  *
40481  * Fork - LGPL
40482  * <script type="text/javascript">
40483  */
40484  
40485
40486 /**
40487  * @class Roo.form.NumberField
40488  * @extends Roo.form.TextField
40489  * Numeric text field that provides automatic keystroke filtering and numeric validation.
40490  * @constructor
40491  * Creates a new NumberField
40492  * @param {Object} config Configuration options
40493  */
40494 Roo.form.NumberField = function(config){
40495     Roo.form.NumberField.superclass.constructor.call(this, config);
40496 };
40497
40498 Roo.extend(Roo.form.NumberField, Roo.form.TextField,  {
40499     /**
40500      * @cfg {String} fieldClass The default CSS class for the field (defaults to "x-form-field x-form-num-field")
40501      */
40502     fieldClass: "x-form-field x-form-num-field",
40503     /**
40504      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
40505      */
40506     allowDecimals : true,
40507     /**
40508      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
40509      */
40510     decimalSeparator : ".",
40511     /**
40512      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
40513      */
40514     decimalPrecision : 2,
40515     /**
40516      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
40517      */
40518     allowNegative : true,
40519     /**
40520      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
40521      */
40522     minValue : Number.NEGATIVE_INFINITY,
40523     /**
40524      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
40525      */
40526     maxValue : Number.MAX_VALUE,
40527     /**
40528      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
40529      */
40530     minText : "The minimum value for this field is {0}",
40531     /**
40532      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
40533      */
40534     maxText : "The maximum value for this field is {0}",
40535     /**
40536      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
40537      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
40538      */
40539     nanText : "{0} is not a valid number",
40540
40541     // private
40542     initEvents : function(){
40543         Roo.form.NumberField.superclass.initEvents.call(this);
40544         var allowed = "0123456789";
40545         if(this.allowDecimals){
40546             allowed += this.decimalSeparator;
40547         }
40548         if(this.allowNegative){
40549             allowed += "-";
40550         }
40551         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
40552         var keyPress = function(e){
40553             var k = e.getKey();
40554             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
40555                 return;
40556             }
40557             var c = e.getCharCode();
40558             if(allowed.indexOf(String.fromCharCode(c)) === -1){
40559                 e.stopEvent();
40560             }
40561         };
40562         this.el.on("keypress", keyPress, this);
40563     },
40564
40565     // private
40566     validateValue : function(value){
40567         if(!Roo.form.NumberField.superclass.validateValue.call(this, value)){
40568             return false;
40569         }
40570         if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
40571              return true;
40572         }
40573         var num = this.parseValue(value);
40574         if(isNaN(num)){
40575             this.markInvalid(String.format(this.nanText, value));
40576             return false;
40577         }
40578         if(num < this.minValue){
40579             this.markInvalid(String.format(this.minText, this.minValue));
40580             return false;
40581         }
40582         if(num > this.maxValue){
40583             this.markInvalid(String.format(this.maxText, this.maxValue));
40584             return false;
40585         }
40586         return true;
40587     },
40588
40589     getValue : function(){
40590         return this.fixPrecision(this.parseValue(Roo.form.NumberField.superclass.getValue.call(this)));
40591     },
40592
40593     // private
40594     parseValue : function(value){
40595         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
40596         return isNaN(value) ? '' : value;
40597     },
40598
40599     // private
40600     fixPrecision : function(value){
40601         var nan = isNaN(value);
40602         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
40603             return nan ? '' : value;
40604         }
40605         return parseFloat(value).toFixed(this.decimalPrecision);
40606     },
40607
40608     setValue : function(v){
40609         v = this.fixPrecision(v);
40610         Roo.form.NumberField.superclass.setValue.call(this, String(v).replace(".", this.decimalSeparator));
40611     },
40612
40613     // private
40614     decimalPrecisionFcn : function(v){
40615         return Math.floor(v);
40616     },
40617
40618     beforeBlur : function(){
40619         var v = this.parseValue(this.getRawValue());
40620         if(v){
40621             this.setValue(v);
40622         }
40623     }
40624 });/*
40625  * Based on:
40626  * Ext JS Library 1.1.1
40627  * Copyright(c) 2006-2007, Ext JS, LLC.
40628  *
40629  * Originally Released Under LGPL - original licence link has changed is not relivant.
40630  *
40631  * Fork - LGPL
40632  * <script type="text/javascript">
40633  */
40634  
40635 /**
40636  * @class Roo.form.DateField
40637  * @extends Roo.form.TriggerField
40638  * Provides a date input field with a {@link Roo.DatePicker} dropdown and automatic date validation.
40639 * @constructor
40640 * Create a new DateField
40641 * @param {Object} config
40642  */
40643 Roo.form.DateField = function(config)
40644 {
40645     Roo.form.DateField.superclass.constructor.call(this, config);
40646     
40647       this.addEvents({
40648          
40649         /**
40650          * @event select
40651          * Fires when a date is selected
40652              * @param {Roo.form.DateField} combo This combo box
40653              * @param {Date} date The date selected
40654              */
40655         'select' : true
40656          
40657     });
40658     
40659     
40660     if(typeof this.minValue == "string") {
40661         this.minValue = this.parseDate(this.minValue);
40662     }
40663     if(typeof this.maxValue == "string") {
40664         this.maxValue = this.parseDate(this.maxValue);
40665     }
40666     this.ddMatch = null;
40667     if(this.disabledDates){
40668         var dd = this.disabledDates;
40669         var re = "(?:";
40670         for(var i = 0; i < dd.length; i++){
40671             re += dd[i];
40672             if(i != dd.length-1) {
40673                 re += "|";
40674             }
40675         }
40676         this.ddMatch = new RegExp(re + ")");
40677     }
40678 };
40679
40680 Roo.extend(Roo.form.DateField, Roo.form.TriggerField,  {
40681     /**
40682      * @cfg {String} format
40683      * The default date format string which can be overriden for localization support.  The format must be
40684      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
40685      */
40686     format : "m/d/y",
40687     /**
40688      * @cfg {String} altFormats
40689      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
40690      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
40691      */
40692     altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
40693     /**
40694      * @cfg {Array} disabledDays
40695      * An array of days to disable, 0 based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
40696      */
40697     disabledDays : null,
40698     /**
40699      * @cfg {String} disabledDaysText
40700      * The tooltip to display when the date falls on a disabled day (defaults to 'Disabled')
40701      */
40702     disabledDaysText : "Disabled",
40703     /**
40704      * @cfg {Array} disabledDates
40705      * An array of "dates" to disable, as strings. These strings will be used to build a dynamic regular
40706      * expression so they are very powerful. Some examples:
40707      * <ul>
40708      * <li>["03/08/2003", "09/16/2003"] would disable those exact dates</li>
40709      * <li>["03/08", "09/16"] would disable those days for every year</li>
40710      * <li>["^03/08"] would only match the beginning (useful if you are using short years)</li>
40711      * <li>["03/../2006"] would disable every day in March 2006</li>
40712      * <li>["^03"] would disable every day in every March</li>
40713      * </ul>
40714      * In order to support regular expressions, if you are using a date format that has "." in it, you will have to
40715      * escape the dot when restricting dates. For example: ["03\\.08\\.03"].
40716      */
40717     disabledDates : null,
40718     /**
40719      * @cfg {String} disabledDatesText
40720      * The tooltip text to display when the date falls on a disabled date (defaults to 'Disabled')
40721      */
40722     disabledDatesText : "Disabled",
40723     /**
40724      * @cfg {Date/String} minValue
40725      * The minimum allowed date. Can be either a Javascript date object or a string date in a
40726      * valid format (defaults to null).
40727      */
40728     minValue : null,
40729     /**
40730      * @cfg {Date/String} maxValue
40731      * The maximum allowed date. Can be either a Javascript date object or a string date in a
40732      * valid format (defaults to null).
40733      */
40734     maxValue : null,
40735     /**
40736      * @cfg {String} minText
40737      * The error text to display when the date in the cell is before minValue (defaults to
40738      * 'The date in this field must be after {minValue}').
40739      */
40740     minText : "The date in this field must be equal to or after {0}",
40741     /**
40742      * @cfg {String} maxText
40743      * The error text to display when the date in the cell is after maxValue (defaults to
40744      * 'The date in this field must be before {maxValue}').
40745      */
40746     maxText : "The date in this field must be equal to or before {0}",
40747     /**
40748      * @cfg {String} invalidText
40749      * The error text to display when the date in the field is invalid (defaults to
40750      * '{value} is not a valid date - it must be in the format {format}').
40751      */
40752     invalidText : "{0} is not a valid date - it must be in the format {1}",
40753     /**
40754      * @cfg {String} triggerClass
40755      * An additional CSS class used to style the trigger button.  The trigger will always get the
40756      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-date-trigger'
40757      * which displays a calendar icon).
40758      */
40759     triggerClass : 'x-form-date-trigger',
40760     
40761
40762     /**
40763      * @cfg {Boolean} useIso
40764      * if enabled, then the date field will use a hidden field to store the 
40765      * real value as iso formated date. default (false)
40766      */ 
40767     useIso : false,
40768     /**
40769      * @cfg {String/Object} autoCreate
40770      * A DomHelper element spec, or true for a default element spec (defaults to
40771      * {tag: "input", type: "text", size: "10", autocomplete: "off"})
40772      */ 
40773     // private
40774     defaultAutoCreate : {tag: "input", type: "text", size: "10", autocomplete: "off"},
40775     
40776     // private
40777     hiddenField: false,
40778     
40779     onRender : function(ct, position)
40780     {
40781         Roo.form.DateField.superclass.onRender.call(this, ct, position);
40782         if (this.useIso) {
40783             //this.el.dom.removeAttribute('name'); 
40784             Roo.log("Changing name?");
40785             this.el.dom.setAttribute('name', this.name + '____hidden___' ); 
40786             this.hiddenField = this.el.insertSibling({ tag:'input', type:'hidden', name: this.name },
40787                     'before', true);
40788             this.hiddenField.value = this.value ? this.formatDate(this.value, 'Y-m-d') : '';
40789             // prevent input submission
40790             this.hiddenName = this.name;
40791         }
40792             
40793             
40794     },
40795     
40796     // private
40797     validateValue : function(value)
40798     {
40799         value = this.formatDate(value);
40800         if(!Roo.form.DateField.superclass.validateValue.call(this, value)){
40801             Roo.log('super failed');
40802             return false;
40803         }
40804         if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
40805              return true;
40806         }
40807         var svalue = value;
40808         value = this.parseDate(value);
40809         if(!value){
40810             Roo.log('parse date failed' + svalue);
40811             this.markInvalid(String.format(this.invalidText, svalue, this.format));
40812             return false;
40813         }
40814         var time = value.getTime();
40815         if(this.minValue && time < this.minValue.getTime()){
40816             this.markInvalid(String.format(this.minText, this.formatDate(this.minValue)));
40817             return false;
40818         }
40819         if(this.maxValue && time > this.maxValue.getTime()){
40820             this.markInvalid(String.format(this.maxText, this.formatDate(this.maxValue)));
40821             return false;
40822         }
40823         if(this.disabledDays){
40824             var day = value.getDay();
40825             for(var i = 0; i < this.disabledDays.length; i++) {
40826                 if(day === this.disabledDays[i]){
40827                     this.markInvalid(this.disabledDaysText);
40828                     return false;
40829                 }
40830             }
40831         }
40832         var fvalue = this.formatDate(value);
40833         if(this.ddMatch && this.ddMatch.test(fvalue)){
40834             this.markInvalid(String.format(this.disabledDatesText, fvalue));
40835             return false;
40836         }
40837         return true;
40838     },
40839
40840     // private
40841     // Provides logic to override the default TriggerField.validateBlur which just returns true
40842     validateBlur : function(){
40843         return !this.menu || !this.menu.isVisible();
40844     },
40845     
40846     getName: function()
40847     {
40848         // returns hidden if it's set..
40849         if (!this.rendered) {return ''};
40850         return !this.hiddenName && this.el.dom.name  ? this.el.dom.name : (this.hiddenName || '');
40851         
40852     },
40853
40854     /**
40855      * Returns the current date value of the date field.
40856      * @return {Date} The date value
40857      */
40858     getValue : function(){
40859         
40860         return  this.hiddenField ?
40861                 this.hiddenField.value :
40862                 this.parseDate(Roo.form.DateField.superclass.getValue.call(this)) || "";
40863     },
40864
40865     /**
40866      * Sets the value of the date field.  You can pass a date object or any string that can be parsed into a valid
40867      * date, using DateField.format as the date format, according to the same rules as {@link Date#parseDate}
40868      * (the default format used is "m/d/y").
40869      * <br />Usage:
40870      * <pre><code>
40871 //All of these calls set the same date value (May 4, 2006)
40872
40873 //Pass a date object:
40874 var dt = new Date('5/4/06');
40875 dateField.setValue(dt);
40876
40877 //Pass a date string (default format):
40878 dateField.setValue('5/4/06');
40879
40880 //Pass a date string (custom format):
40881 dateField.format = 'Y-m-d';
40882 dateField.setValue('2006-5-4');
40883 </code></pre>
40884      * @param {String/Date} date The date or valid date string
40885      */
40886     setValue : function(date){
40887         if (this.hiddenField) {
40888             this.hiddenField.value = this.formatDate(this.parseDate(date), 'Y-m-d');
40889         }
40890         Roo.form.DateField.superclass.setValue.call(this, this.formatDate(this.parseDate(date)));
40891         // make sure the value field is always stored as a date..
40892         this.value = this.parseDate(date);
40893         
40894         
40895     },
40896
40897     // private
40898     parseDate : function(value){
40899         if(!value || value instanceof Date){
40900             return value;
40901         }
40902         var v = Date.parseDate(value, this.format);
40903          if (!v && this.useIso) {
40904             v = Date.parseDate(value, 'Y-m-d');
40905         }
40906         if(!v && this.altFormats){
40907             if(!this.altFormatsArray){
40908                 this.altFormatsArray = this.altFormats.split("|");
40909             }
40910             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
40911                 v = Date.parseDate(value, this.altFormatsArray[i]);
40912             }
40913         }
40914         return v;
40915     },
40916
40917     // private
40918     formatDate : function(date, fmt){
40919         return (!date || !(date instanceof Date)) ?
40920                date : date.dateFormat(fmt || this.format);
40921     },
40922
40923     // private
40924     menuListeners : {
40925         select: function(m, d){
40926             
40927             this.setValue(d);
40928             this.fireEvent('select', this, d);
40929         },
40930         show : function(){ // retain focus styling
40931             this.onFocus();
40932         },
40933         hide : function(){
40934             this.focus.defer(10, this);
40935             var ml = this.menuListeners;
40936             this.menu.un("select", ml.select,  this);
40937             this.menu.un("show", ml.show,  this);
40938             this.menu.un("hide", ml.hide,  this);
40939         }
40940     },
40941
40942     // private
40943     // Implements the default empty TriggerField.onTriggerClick function to display the DatePicker
40944     onTriggerClick : function(){
40945         if(this.disabled){
40946             return;
40947         }
40948         if(this.menu == null){
40949             this.menu = new Roo.menu.DateMenu();
40950         }
40951         Roo.apply(this.menu.picker,  {
40952             showClear: this.allowBlank,
40953             minDate : this.minValue,
40954             maxDate : this.maxValue,
40955             disabledDatesRE : this.ddMatch,
40956             disabledDatesText : this.disabledDatesText,
40957             disabledDays : this.disabledDays,
40958             disabledDaysText : this.disabledDaysText,
40959             format : this.useIso ? 'Y-m-d' : this.format,
40960             minText : String.format(this.minText, this.formatDate(this.minValue)),
40961             maxText : String.format(this.maxText, this.formatDate(this.maxValue))
40962         });
40963         this.menu.on(Roo.apply({}, this.menuListeners, {
40964             scope:this
40965         }));
40966         this.menu.picker.setValue(this.getValue() || new Date());
40967         this.menu.show(this.el, "tl-bl?");
40968     },
40969
40970     beforeBlur : function(){
40971         var v = this.parseDate(this.getRawValue());
40972         if(v){
40973             this.setValue(v);
40974         }
40975     },
40976
40977     /*@
40978      * overide
40979      * 
40980      */
40981     isDirty : function() {
40982         if(this.disabled) {
40983             return false;
40984         }
40985         
40986         if(typeof(this.startValue) === 'undefined'){
40987             return false;
40988         }
40989         
40990         return String(this.getValue()) !== String(this.startValue);
40991         
40992     },
40993     // @overide
40994     cleanLeadingSpace : function(e)
40995     {
40996        return;
40997     }
40998     
40999 });/*
41000  * Based on:
41001  * Ext JS Library 1.1.1
41002  * Copyright(c) 2006-2007, Ext JS, LLC.
41003  *
41004  * Originally Released Under LGPL - original licence link has changed is not relivant.
41005  *
41006  * Fork - LGPL
41007  * <script type="text/javascript">
41008  */
41009  
41010 /**
41011  * @class Roo.form.MonthField
41012  * @extends Roo.form.TriggerField
41013  * Provides a date input field with a {@link Roo.DatePicker} dropdown and automatic date validation.
41014 * @constructor
41015 * Create a new MonthField
41016 * @param {Object} config
41017  */
41018 Roo.form.MonthField = function(config){
41019     
41020     Roo.form.MonthField.superclass.constructor.call(this, config);
41021     
41022       this.addEvents({
41023          
41024         /**
41025          * @event select
41026          * Fires when a date is selected
41027              * @param {Roo.form.MonthFieeld} combo This combo box
41028              * @param {Date} date The date selected
41029              */
41030         'select' : true
41031          
41032     });
41033     
41034     
41035     if(typeof this.minValue == "string") {
41036         this.minValue = this.parseDate(this.minValue);
41037     }
41038     if(typeof this.maxValue == "string") {
41039         this.maxValue = this.parseDate(this.maxValue);
41040     }
41041     this.ddMatch = null;
41042     if(this.disabledDates){
41043         var dd = this.disabledDates;
41044         var re = "(?:";
41045         for(var i = 0; i < dd.length; i++){
41046             re += dd[i];
41047             if(i != dd.length-1) {
41048                 re += "|";
41049             }
41050         }
41051         this.ddMatch = new RegExp(re + ")");
41052     }
41053 };
41054
41055 Roo.extend(Roo.form.MonthField, Roo.form.TriggerField,  {
41056     /**
41057      * @cfg {String} format
41058      * The default date format string which can be overriden for localization support.  The format must be
41059      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
41060      */
41061     format : "M Y",
41062     /**
41063      * @cfg {String} altFormats
41064      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
41065      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
41066      */
41067     altFormats : "M Y|m/Y|m-y|m-Y|my|mY",
41068     /**
41069      * @cfg {Array} disabledDays
41070      * An array of days to disable, 0 based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
41071      */
41072     disabledDays : [0,1,2,3,4,5,6],
41073     /**
41074      * @cfg {String} disabledDaysText
41075      * The tooltip to display when the date falls on a disabled day (defaults to 'Disabled')
41076      */
41077     disabledDaysText : "Disabled",
41078     /**
41079      * @cfg {Array} disabledDates
41080      * An array of "dates" to disable, as strings. These strings will be used to build a dynamic regular
41081      * expression so they are very powerful. Some examples:
41082      * <ul>
41083      * <li>["03/08/2003", "09/16/2003"] would disable those exact dates</li>
41084      * <li>["03/08", "09/16"] would disable those days for every year</li>
41085      * <li>["^03/08"] would only match the beginning (useful if you are using short years)</li>
41086      * <li>["03/../2006"] would disable every day in March 2006</li>
41087      * <li>["^03"] would disable every day in every March</li>
41088      * </ul>
41089      * In order to support regular expressions, if you are using a date format that has "." in it, you will have to
41090      * escape the dot when restricting dates. For example: ["03\\.08\\.03"].
41091      */
41092     disabledDates : null,
41093     /**
41094      * @cfg {String} disabledDatesText
41095      * The tooltip text to display when the date falls on a disabled date (defaults to 'Disabled')
41096      */
41097     disabledDatesText : "Disabled",
41098     /**
41099      * @cfg {Date/String} minValue
41100      * The minimum allowed date. Can be either a Javascript date object or a string date in a
41101      * valid format (defaults to null).
41102      */
41103     minValue : null,
41104     /**
41105      * @cfg {Date/String} maxValue
41106      * The maximum allowed date. Can be either a Javascript date object or a string date in a
41107      * valid format (defaults to null).
41108      */
41109     maxValue : null,
41110     /**
41111      * @cfg {String} minText
41112      * The error text to display when the date in the cell is before minValue (defaults to
41113      * 'The date in this field must be after {minValue}').
41114      */
41115     minText : "The date in this field must be equal to or after {0}",
41116     /**
41117      * @cfg {String} maxTextf
41118      * The error text to display when the date in the cell is after maxValue (defaults to
41119      * 'The date in this field must be before {maxValue}').
41120      */
41121     maxText : "The date in this field must be equal to or before {0}",
41122     /**
41123      * @cfg {String} invalidText
41124      * The error text to display when the date in the field is invalid (defaults to
41125      * '{value} is not a valid date - it must be in the format {format}').
41126      */
41127     invalidText : "{0} is not a valid date - it must be in the format {1}",
41128     /**
41129      * @cfg {String} triggerClass
41130      * An additional CSS class used to style the trigger button.  The trigger will always get the
41131      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-date-trigger'
41132      * which displays a calendar icon).
41133      */
41134     triggerClass : 'x-form-date-trigger',
41135     
41136
41137     /**
41138      * @cfg {Boolean} useIso
41139      * if enabled, then the date field will use a hidden field to store the 
41140      * real value as iso formated date. default (true)
41141      */ 
41142     useIso : true,
41143     /**
41144      * @cfg {String/Object} autoCreate
41145      * A DomHelper element spec, or true for a default element spec (defaults to
41146      * {tag: "input", type: "text", size: "10", autocomplete: "off"})
41147      */ 
41148     // private
41149     defaultAutoCreate : {tag: "input", type: "text", size: "10", autocomplete: "new-password"},
41150     
41151     // private
41152     hiddenField: false,
41153     
41154     hideMonthPicker : false,
41155     
41156     onRender : function(ct, position)
41157     {
41158         Roo.form.MonthField.superclass.onRender.call(this, ct, position);
41159         if (this.useIso) {
41160             this.el.dom.removeAttribute('name'); 
41161             this.hiddenField = this.el.insertSibling({ tag:'input', type:'hidden', name: this.name },
41162                     'before', true);
41163             this.hiddenField.value = this.value ? this.formatDate(this.value, 'Y-m-d') : '';
41164             // prevent input submission
41165             this.hiddenName = this.name;
41166         }
41167             
41168             
41169     },
41170     
41171     // private
41172     validateValue : function(value)
41173     {
41174         value = this.formatDate(value);
41175         if(!Roo.form.MonthField.superclass.validateValue.call(this, value)){
41176             return false;
41177         }
41178         if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
41179              return true;
41180         }
41181         var svalue = value;
41182         value = this.parseDate(value);
41183         if(!value){
41184             this.markInvalid(String.format(this.invalidText, svalue, this.format));
41185             return false;
41186         }
41187         var time = value.getTime();
41188         if(this.minValue && time < this.minValue.getTime()){
41189             this.markInvalid(String.format(this.minText, this.formatDate(this.minValue)));
41190             return false;
41191         }
41192         if(this.maxValue && time > this.maxValue.getTime()){
41193             this.markInvalid(String.format(this.maxText, this.formatDate(this.maxValue)));
41194             return false;
41195         }
41196         /*if(this.disabledDays){
41197             var day = value.getDay();
41198             for(var i = 0; i < this.disabledDays.length; i++) {
41199                 if(day === this.disabledDays[i]){
41200                     this.markInvalid(this.disabledDaysText);
41201                     return false;
41202                 }
41203             }
41204         }
41205         */
41206         var fvalue = this.formatDate(value);
41207         /*if(this.ddMatch && this.ddMatch.test(fvalue)){
41208             this.markInvalid(String.format(this.disabledDatesText, fvalue));
41209             return false;
41210         }
41211         */
41212         return true;
41213     },
41214
41215     // private
41216     // Provides logic to override the default TriggerField.validateBlur which just returns true
41217     validateBlur : function(){
41218         return !this.menu || !this.menu.isVisible();
41219     },
41220
41221     /**
41222      * Returns the current date value of the date field.
41223      * @return {Date} The date value
41224      */
41225     getValue : function(){
41226         
41227         
41228         
41229         return  this.hiddenField ?
41230                 this.hiddenField.value :
41231                 this.parseDate(Roo.form.MonthField.superclass.getValue.call(this)) || "";
41232     },
41233
41234     /**
41235      * Sets the value of the date field.  You can pass a date object or any string that can be parsed into a valid
41236      * date, using MonthField.format as the date format, according to the same rules as {@link Date#parseDate}
41237      * (the default format used is "m/d/y").
41238      * <br />Usage:
41239      * <pre><code>
41240 //All of these calls set the same date value (May 4, 2006)
41241
41242 //Pass a date object:
41243 var dt = new Date('5/4/06');
41244 monthField.setValue(dt);
41245
41246 //Pass a date string (default format):
41247 monthField.setValue('5/4/06');
41248
41249 //Pass a date string (custom format):
41250 monthField.format = 'Y-m-d';
41251 monthField.setValue('2006-5-4');
41252 </code></pre>
41253      * @param {String/Date} date The date or valid date string
41254      */
41255     setValue : function(date){
41256         Roo.log('month setValue' + date);
41257         // can only be first of month..
41258         
41259         var val = this.parseDate(date);
41260         
41261         if (this.hiddenField) {
41262             this.hiddenField.value = this.formatDate(this.parseDate(date), 'Y-m-d');
41263         }
41264         Roo.form.MonthField.superclass.setValue.call(this, this.formatDate(this.parseDate(date)));
41265         this.value = this.parseDate(date);
41266     },
41267
41268     // private
41269     parseDate : function(value){
41270         if(!value || value instanceof Date){
41271             value = value ? Date.parseDate(value.format('Y-m') + '-01', 'Y-m-d') : null;
41272             return value;
41273         }
41274         var v = Date.parseDate(value, this.format);
41275         if (!v && this.useIso) {
41276             v = Date.parseDate(value, 'Y-m-d');
41277         }
41278         if (v) {
41279             // 
41280             v = Date.parseDate(v.format('Y-m') +'-01', 'Y-m-d');
41281         }
41282         
41283         
41284         if(!v && this.altFormats){
41285             if(!this.altFormatsArray){
41286                 this.altFormatsArray = this.altFormats.split("|");
41287             }
41288             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
41289                 v = Date.parseDate(value, this.altFormatsArray[i]);
41290             }
41291         }
41292         return v;
41293     },
41294
41295     // private
41296     formatDate : function(date, fmt){
41297         return (!date || !(date instanceof Date)) ?
41298                date : date.dateFormat(fmt || this.format);
41299     },
41300
41301     // private
41302     menuListeners : {
41303         select: function(m, d){
41304             this.setValue(d);
41305             this.fireEvent('select', this, d);
41306         },
41307         show : function(){ // retain focus styling
41308             this.onFocus();
41309         },
41310         hide : function(){
41311             this.focus.defer(10, this);
41312             var ml = this.menuListeners;
41313             this.menu.un("select", ml.select,  this);
41314             this.menu.un("show", ml.show,  this);
41315             this.menu.un("hide", ml.hide,  this);
41316         }
41317     },
41318     // private
41319     // Implements the default empty TriggerField.onTriggerClick function to display the DatePicker
41320     onTriggerClick : function(){
41321         if(this.disabled){
41322             return;
41323         }
41324         if(this.menu == null){
41325             this.menu = new Roo.menu.DateMenu();
41326            
41327         }
41328         
41329         Roo.apply(this.menu.picker,  {
41330             
41331             showClear: this.allowBlank,
41332             minDate : this.minValue,
41333             maxDate : this.maxValue,
41334             disabledDatesRE : this.ddMatch,
41335             disabledDatesText : this.disabledDatesText,
41336             
41337             format : this.useIso ? 'Y-m-d' : this.format,
41338             minText : String.format(this.minText, this.formatDate(this.minValue)),
41339             maxText : String.format(this.maxText, this.formatDate(this.maxValue))
41340             
41341         });
41342          this.menu.on(Roo.apply({}, this.menuListeners, {
41343             scope:this
41344         }));
41345        
41346         
41347         var m = this.menu;
41348         var p = m.picker;
41349         
41350         // hide month picker get's called when we called by 'before hide';
41351         
41352         var ignorehide = true;
41353         p.hideMonthPicker  = function(disableAnim){
41354             if (ignorehide) {
41355                 return;
41356             }
41357              if(this.monthPicker){
41358                 Roo.log("hideMonthPicker called");
41359                 if(disableAnim === true){
41360                     this.monthPicker.hide();
41361                 }else{
41362                     this.monthPicker.slideOut('t', {duration:.2});
41363                     p.setValue(new Date(m.picker.mpSelYear, m.picker.mpSelMonth, 1));
41364                     p.fireEvent("select", this, this.value);
41365                     m.hide();
41366                 }
41367             }
41368         }
41369         
41370         Roo.log('picker set value');
41371         Roo.log(this.getValue());
41372         p.setValue(this.getValue() ? this.parseDate(this.getValue()) : new Date());
41373         m.show(this.el, 'tl-bl?');
41374         ignorehide  = false;
41375         // this will trigger hideMonthPicker..
41376         
41377         
41378         // hidden the day picker
41379         Roo.select('.x-date-picker table', true).first().dom.style.visibility = "hidden";
41380         
41381         
41382         
41383       
41384         
41385         p.showMonthPicker.defer(100, p);
41386     
41387         
41388        
41389     },
41390
41391     beforeBlur : function(){
41392         var v = this.parseDate(this.getRawValue());
41393         if(v){
41394             this.setValue(v);
41395         }
41396     }
41397
41398     /** @cfg {Boolean} grow @hide */
41399     /** @cfg {Number} growMin @hide */
41400     /** @cfg {Number} growMax @hide */
41401     /**
41402      * @hide
41403      * @method autoSize
41404      */
41405 });/*
41406  * Based on:
41407  * Ext JS Library 1.1.1
41408  * Copyright(c) 2006-2007, Ext JS, LLC.
41409  *
41410  * Originally Released Under LGPL - original licence link has changed is not relivant.
41411  *
41412  * Fork - LGPL
41413  * <script type="text/javascript">
41414  */
41415  
41416
41417 /**
41418  * @class Roo.form.ComboBox
41419  * @extends Roo.form.TriggerField
41420  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
41421  * @constructor
41422  * Create a new ComboBox.
41423  * @param {Object} config Configuration options
41424  */
41425 Roo.form.ComboBox = function(config){
41426     Roo.form.ComboBox.superclass.constructor.call(this, config);
41427     this.addEvents({
41428         /**
41429          * @event expand
41430          * Fires when the dropdown list is expanded
41431              * @param {Roo.form.ComboBox} combo This combo box
41432              */
41433         'expand' : true,
41434         /**
41435          * @event collapse
41436          * Fires when the dropdown list is collapsed
41437              * @param {Roo.form.ComboBox} combo This combo box
41438              */
41439         'collapse' : true,
41440         /**
41441          * @event beforeselect
41442          * Fires before a list item is selected. Return false to cancel the selection.
41443              * @param {Roo.form.ComboBox} combo This combo box
41444              * @param {Roo.data.Record} record The data record returned from the underlying store
41445              * @param {Number} index The index of the selected item in the dropdown list
41446              */
41447         'beforeselect' : true,
41448         /**
41449          * @event select
41450          * Fires when a list item is selected
41451              * @param {Roo.form.ComboBox} combo This combo box
41452              * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
41453              * @param {Number} index The index of the selected item in the dropdown list
41454              */
41455         'select' : true,
41456         /**
41457          * @event beforequery
41458          * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
41459          * The event object passed has these properties:
41460              * @param {Roo.form.ComboBox} combo This combo box
41461              * @param {String} query The query
41462              * @param {Boolean} forceAll true to force "all" query
41463              * @param {Boolean} cancel true to cancel the query
41464              * @param {Object} e The query event object
41465              */
41466         'beforequery': true,
41467          /**
41468          * @event add
41469          * Fires when the 'add' icon is pressed (add a listener to enable add button)
41470              * @param {Roo.form.ComboBox} combo This combo box
41471              */
41472         'add' : true,
41473         /**
41474          * @event edit
41475          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
41476              * @param {Roo.form.ComboBox} combo This combo box
41477              * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
41478              */
41479         'edit' : true
41480         
41481         
41482     });
41483     if(this.transform){
41484         this.allowDomMove = false;
41485         var s = Roo.getDom(this.transform);
41486         if(!this.hiddenName){
41487             this.hiddenName = s.name;
41488         }
41489         if(!this.store){
41490             this.mode = 'local';
41491             var d = [], opts = s.options;
41492             for(var i = 0, len = opts.length;i < len; i++){
41493                 var o = opts[i];
41494                 var value = (Roo.isIE ? o.getAttributeNode('value').specified : o.hasAttribute('value')) ? o.value : o.text;
41495                 if(o.selected) {
41496                     this.value = value;
41497                 }
41498                 d.push([value, o.text]);
41499             }
41500             this.store = new Roo.data.SimpleStore({
41501                 'id': 0,
41502                 fields: ['value', 'text'],
41503                 data : d
41504             });
41505             this.valueField = 'value';
41506             this.displayField = 'text';
41507         }
41508         s.name = Roo.id(); // wipe out the name in case somewhere else they have a reference
41509         if(!this.lazyRender){
41510             this.target = true;
41511             this.el = Roo.DomHelper.insertBefore(s, this.autoCreate || this.defaultAutoCreate);
41512             s.parentNode.removeChild(s); // remove it
41513             this.render(this.el.parentNode);
41514         }else{
41515             s.parentNode.removeChild(s); // remove it
41516         }
41517
41518     }
41519     if (this.store) {
41520         this.store = Roo.factory(this.store, Roo.data);
41521     }
41522     
41523     this.selectedIndex = -1;
41524     if(this.mode == 'local'){
41525         if(config.queryDelay === undefined){
41526             this.queryDelay = 10;
41527         }
41528         if(config.minChars === undefined){
41529             this.minChars = 0;
41530         }
41531     }
41532 };
41533
41534 Roo.extend(Roo.form.ComboBox, Roo.form.TriggerField, {
41535     /**
41536      * @cfg {String/HTMLElement/Element} transform The id, DOM node or element of an existing select to convert to a ComboBox
41537      */
41538     /**
41539      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
41540      * rendering into an Roo.Editor, defaults to false)
41541      */
41542     /**
41543      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
41544      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
41545      */
41546     /**
41547      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
41548      */
41549     /**
41550      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
41551      * the dropdown list (defaults to undefined, with no header element)
41552      */
41553
41554      /**
41555      * @cfg {String/Roo.Template} tpl The template to use to render the output
41556      */
41557      
41558     // private
41559     defaultAutoCreate : {tag: "input", type: "text", size: "24", autocomplete: "off"},
41560     /**
41561      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
41562      */
41563     listWidth: undefined,
41564     /**
41565      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
41566      * mode = 'remote' or 'text' if mode = 'local')
41567      */
41568     displayField: undefined,
41569     /**
41570      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
41571      * mode = 'remote' or 'value' if mode = 'local'). 
41572      * Note: use of a valueField requires the user make a selection
41573      * in order for a value to be mapped.
41574      */
41575     valueField: undefined,
41576     
41577     
41578     /**
41579      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
41580      * field's data value (defaults to the underlying DOM element's name)
41581      */
41582     hiddenName: undefined,
41583     /**
41584      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
41585      */
41586     listClass: '',
41587     /**
41588      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
41589      */
41590     selectedClass: 'x-combo-selected',
41591     /**
41592      * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
41593      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-arrow-trigger'
41594      * which displays a downward arrow icon).
41595      */
41596     triggerClass : 'x-form-arrow-trigger',
41597     /**
41598      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
41599      */
41600     shadow:'sides',
41601     /**
41602      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
41603      * anchor positions (defaults to 'tl-bl')
41604      */
41605     listAlign: 'tl-bl?',
41606     /**
41607      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
41608      */
41609     maxHeight: 300,
41610     /**
41611      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
41612      * query specified by the allQuery config option (defaults to 'query')
41613      */
41614     triggerAction: 'query',
41615     /**
41616      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
41617      * (defaults to 4, does not apply if editable = false)
41618      */
41619     minChars : 4,
41620     /**
41621      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
41622      * delay (typeAheadDelay) if it matches a known value (defaults to false)
41623      */
41624     typeAhead: false,
41625     /**
41626      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
41627      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
41628      */
41629     queryDelay: 500,
41630     /**
41631      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
41632      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
41633      */
41634     pageSize: 0,
41635     /**
41636      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
41637      * when editable = true (defaults to false)
41638      */
41639     selectOnFocus:false,
41640     /**
41641      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
41642      */
41643     queryParam: 'query',
41644     /**
41645      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
41646      * when mode = 'remote' (defaults to 'Loading...')
41647      */
41648     loadingText: 'Loading...',
41649     /**
41650      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
41651      */
41652     resizable: false,
41653     /**
41654      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
41655      */
41656     handleHeight : 8,
41657     /**
41658      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
41659      * traditional select (defaults to true)
41660      */
41661     editable: true,
41662     /**
41663      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
41664      */
41665     allQuery: '',
41666     /**
41667      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
41668      */
41669     mode: 'remote',
41670     /**
41671      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
41672      * listWidth has a higher value)
41673      */
41674     minListWidth : 70,
41675     /**
41676      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
41677      * allow the user to set arbitrary text into the field (defaults to false)
41678      */
41679     forceSelection:false,
41680     /**
41681      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
41682      * if typeAhead = true (defaults to 250)
41683      */
41684     typeAheadDelay : 250,
41685     /**
41686      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
41687      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
41688      */
41689     valueNotFoundText : undefined,
41690     /**
41691      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
41692      */
41693     blockFocus : false,
41694     
41695     /**
41696      * @cfg {Boolean} disableClear Disable showing of clear button.
41697      */
41698     disableClear : false,
41699     /**
41700      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
41701      */
41702     alwaysQuery : false,
41703     
41704     //private
41705     addicon : false,
41706     editicon: false,
41707     
41708     // element that contains real text value.. (when hidden is used..)
41709      
41710     // private
41711     onRender : function(ct, position)
41712     {
41713         Roo.form.ComboBox.superclass.onRender.call(this, ct, position);
41714         
41715         if(this.hiddenName){
41716             this.hiddenField = this.el.insertSibling({tag:'input', type:'hidden', name: this.hiddenName, id:  (this.hiddenId||this.hiddenName)},
41717                     'before', true);
41718             this.hiddenField.value =
41719                 this.hiddenValue !== undefined ? this.hiddenValue :
41720                 this.value !== undefined ? this.value : '';
41721
41722             // prevent input submission
41723             this.el.dom.removeAttribute('name');
41724              
41725              
41726         }
41727         
41728         if(Roo.isGecko){
41729             this.el.dom.setAttribute('autocomplete', 'off');
41730         }
41731
41732         var cls = 'x-combo-list';
41733
41734         this.list = new Roo.Layer({
41735             shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
41736         });
41737
41738         var lw = this.listWidth || Math.max(this.wrap.getWidth(), this.minListWidth);
41739         this.list.setWidth(lw);
41740         this.list.swallowEvent('mousewheel');
41741         this.assetHeight = 0;
41742
41743         if(this.title){
41744             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
41745             this.assetHeight += this.header.getHeight();
41746         }
41747
41748         this.innerList = this.list.createChild({cls:cls+'-inner'});
41749         this.innerList.on('mouseover', this.onViewOver, this);
41750         this.innerList.on('mousemove', this.onViewMove, this);
41751         this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
41752         
41753         if(this.allowBlank && !this.pageSize && !this.disableClear){
41754             this.footer = this.list.createChild({cls:cls+'-ft'});
41755             this.pageTb = new Roo.Toolbar(this.footer);
41756            
41757         }
41758         if(this.pageSize){
41759             this.footer = this.list.createChild({cls:cls+'-ft'});
41760             this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
41761                     {pageSize: this.pageSize});
41762             
41763         }
41764         
41765         if (this.pageTb && this.allowBlank && !this.disableClear) {
41766             var _this = this;
41767             this.pageTb.add(new Roo.Toolbar.Fill(), {
41768                 cls: 'x-btn-icon x-btn-clear',
41769                 text: '&#160;',
41770                 handler: function()
41771                 {
41772                     _this.collapse();
41773                     _this.clearValue();
41774                     _this.onSelect(false, -1);
41775                 }
41776             });
41777         }
41778         if (this.footer) {
41779             this.assetHeight += this.footer.getHeight();
41780         }
41781         
41782
41783         if(!this.tpl){
41784             this.tpl = '<div class="'+cls+'-item">{' + this.displayField + '}</div>';
41785         }
41786
41787         this.view = new Roo.View(this.innerList, this.tpl, {
41788             singleSelect:true,
41789             store: this.store,
41790             selectedClass: this.selectedClass
41791         });
41792
41793         this.view.on('click', this.onViewClick, this);
41794
41795         this.store.on('beforeload', this.onBeforeLoad, this);
41796         this.store.on('load', this.onLoad, this);
41797         this.store.on('loadexception', this.onLoadException, this);
41798
41799         if(this.resizable){
41800             this.resizer = new Roo.Resizable(this.list,  {
41801                pinned:true, handles:'se'
41802             });
41803             this.resizer.on('resize', function(r, w, h){
41804                 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
41805                 this.listWidth = w;
41806                 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
41807                 this.restrictHeight();
41808             }, this);
41809             this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
41810         }
41811         if(!this.editable){
41812             this.editable = true;
41813             this.setEditable(false);
41814         }  
41815         
41816         
41817         if (typeof(this.events.add.listeners) != 'undefined') {
41818             
41819             this.addicon = this.wrap.createChild(
41820                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });  
41821        
41822             this.addicon.on('click', function(e) {
41823                 this.fireEvent('add', this);
41824             }, this);
41825         }
41826         if (typeof(this.events.edit.listeners) != 'undefined') {
41827             
41828             this.editicon = this.wrap.createChild(
41829                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });  
41830             if (this.addicon) {
41831                 this.editicon.setStyle('margin-left', '40px');
41832             }
41833             this.editicon.on('click', function(e) {
41834                 
41835                 // we fire even  if inothing is selected..
41836                 this.fireEvent('edit', this, this.lastData );
41837                 
41838             }, this);
41839         }
41840         
41841         
41842         
41843     },
41844
41845     // private
41846     initEvents : function(){
41847         Roo.form.ComboBox.superclass.initEvents.call(this);
41848
41849         this.keyNav = new Roo.KeyNav(this.el, {
41850             "up" : function(e){
41851                 this.inKeyMode = true;
41852                 this.selectPrev();
41853             },
41854
41855             "down" : function(e){
41856                 if(!this.isExpanded()){
41857                     this.onTriggerClick();
41858                 }else{
41859                     this.inKeyMode = true;
41860                     this.selectNext();
41861                 }
41862             },
41863
41864             "enter" : function(e){
41865                 this.onViewClick();
41866                 //return true;
41867             },
41868
41869             "esc" : function(e){
41870                 this.collapse();
41871             },
41872
41873             "tab" : function(e){
41874                 this.onViewClick(false);
41875                 this.fireEvent("specialkey", this, e);
41876                 return true;
41877             },
41878
41879             scope : this,
41880
41881             doRelay : function(foo, bar, hname){
41882                 if(hname == 'down' || this.scope.isExpanded()){
41883                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
41884                 }
41885                 return true;
41886             },
41887
41888             forceKeyDown: true
41889         });
41890         this.queryDelay = Math.max(this.queryDelay || 10,
41891                 this.mode == 'local' ? 10 : 250);
41892         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
41893         if(this.typeAhead){
41894             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
41895         }
41896         if(this.editable !== false){
41897             this.el.on("keyup", this.onKeyUp, this);
41898         }
41899         if(this.forceSelection){
41900             this.on('blur', this.doForce, this);
41901         }
41902     },
41903
41904     onDestroy : function(){
41905         if(this.view){
41906             this.view.setStore(null);
41907             this.view.el.removeAllListeners();
41908             this.view.el.remove();
41909             this.view.purgeListeners();
41910         }
41911         if(this.list){
41912             this.list.destroy();
41913         }
41914         if(this.store){
41915             this.store.un('beforeload', this.onBeforeLoad, this);
41916             this.store.un('load', this.onLoad, this);
41917             this.store.un('loadexception', this.onLoadException, this);
41918         }
41919         Roo.form.ComboBox.superclass.onDestroy.call(this);
41920     },
41921
41922     // private
41923     fireKey : function(e){
41924         if(e.isNavKeyPress() && !this.list.isVisible()){
41925             this.fireEvent("specialkey", this, e);
41926         }
41927     },
41928
41929     // private
41930     onResize: function(w, h){
41931         Roo.form.ComboBox.superclass.onResize.apply(this, arguments);
41932         
41933         if(typeof w != 'number'){
41934             // we do not handle it!?!?
41935             return;
41936         }
41937         var tw = this.trigger.getWidth();
41938         tw += this.addicon ? this.addicon.getWidth() : 0;
41939         tw += this.editicon ? this.editicon.getWidth() : 0;
41940         var x = w - tw;
41941         this.el.setWidth( this.adjustWidth('input', x));
41942             
41943         this.trigger.setStyle('left', x+'px');
41944         
41945         if(this.list && this.listWidth === undefined){
41946             var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
41947             this.list.setWidth(lw);
41948             this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
41949         }
41950         
41951     
41952         
41953     },
41954
41955     /**
41956      * Allow or prevent the user from directly editing the field text.  If false is passed,
41957      * the user will only be able to select from the items defined in the dropdown list.  This method
41958      * is the runtime equivalent of setting the 'editable' config option at config time.
41959      * @param {Boolean} value True to allow the user to directly edit the field text
41960      */
41961     setEditable : function(value){
41962         if(value == this.editable){
41963             return;
41964         }
41965         this.editable = value;
41966         if(!value){
41967             this.el.dom.setAttribute('readOnly', true);
41968             this.el.on('mousedown', this.onTriggerClick,  this);
41969             this.el.addClass('x-combo-noedit');
41970         }else{
41971             this.el.dom.setAttribute('readOnly', false);
41972             this.el.un('mousedown', this.onTriggerClick,  this);
41973             this.el.removeClass('x-combo-noedit');
41974         }
41975     },
41976
41977     // private
41978     onBeforeLoad : function(){
41979         if(!this.hasFocus){
41980             return;
41981         }
41982         this.innerList.update(this.loadingText ?
41983                '<div class="loading-indicator">'+this.loadingText+'</div>' : '');
41984         this.restrictHeight();
41985         this.selectedIndex = -1;
41986     },
41987
41988     // private
41989     onLoad : function(){
41990         if(!this.hasFocus){
41991             return;
41992         }
41993         if(this.store.getCount() > 0){
41994             this.expand();
41995             this.restrictHeight();
41996             if(this.lastQuery == this.allQuery){
41997                 if(this.editable){
41998                     this.el.dom.select();
41999                 }
42000                 if(!this.selectByValue(this.value, true)){
42001                     this.select(0, true);
42002                 }
42003             }else{
42004                 this.selectNext();
42005                 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
42006                     this.taTask.delay(this.typeAheadDelay);
42007                 }
42008             }
42009         }else{
42010             this.onEmptyResults();
42011         }
42012         //this.el.focus();
42013     },
42014     // private
42015     onLoadException : function()
42016     {
42017         this.collapse();
42018         Roo.log(this.store.reader.jsonData);
42019         if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
42020             Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
42021         }
42022         
42023         
42024     },
42025     // private
42026     onTypeAhead : function(){
42027         if(this.store.getCount() > 0){
42028             var r = this.store.getAt(0);
42029             var newValue = r.data[this.displayField];
42030             var len = newValue.length;
42031             var selStart = this.getRawValue().length;
42032             if(selStart != len){
42033                 this.setRawValue(newValue);
42034                 this.selectText(selStart, newValue.length);
42035             }
42036         }
42037     },
42038
42039     // private
42040     onSelect : function(record, index){
42041         if(this.fireEvent('beforeselect', this, record, index) !== false){
42042             this.setFromData(index > -1 ? record.data : false);
42043             this.collapse();
42044             this.fireEvent('select', this, record, index);
42045         }
42046     },
42047
42048     /**
42049      * Returns the currently selected field value or empty string if no value is set.
42050      * @return {String} value The selected value
42051      */
42052     getValue : function(){
42053         if(this.valueField){
42054             return typeof this.value != 'undefined' ? this.value : '';
42055         }
42056         return Roo.form.ComboBox.superclass.getValue.call(this);
42057     },
42058
42059     /**
42060      * Clears any text/value currently set in the field
42061      */
42062     clearValue : function(){
42063         if(this.hiddenField){
42064             this.hiddenField.value = '';
42065         }
42066         this.value = '';
42067         this.setRawValue('');
42068         this.lastSelectionText = '';
42069         
42070     },
42071
42072     /**
42073      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
42074      * will be displayed in the field.  If the value does not match the data value of an existing item,
42075      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
42076      * Otherwise the field will be blank (although the value will still be set).
42077      * @param {String} value The value to match
42078      */
42079     setValue : function(v){
42080         var text = v;
42081         if(this.valueField){
42082             var r = this.findRecord(this.valueField, v);
42083             if(r){
42084                 text = r.data[this.displayField];
42085             }else if(this.valueNotFoundText !== undefined){
42086                 text = this.valueNotFoundText;
42087             }
42088         }
42089         this.lastSelectionText = text;
42090         if(this.hiddenField){
42091             this.hiddenField.value = v;
42092         }
42093         Roo.form.ComboBox.superclass.setValue.call(this, text);
42094         this.value = v;
42095     },
42096     /**
42097      * @property {Object} the last set data for the element
42098      */
42099     
42100     lastData : false,
42101     /**
42102      * Sets the value of the field based on a object which is related to the record format for the store.
42103      * @param {Object} value the value to set as. or false on reset?
42104      */
42105     setFromData : function(o){
42106         var dv = ''; // display value
42107         var vv = ''; // value value..
42108         this.lastData = o;
42109         if (this.displayField) {
42110             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
42111         } else {
42112             // this is an error condition!!!
42113             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
42114         }
42115         
42116         if(this.valueField){
42117             vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
42118         }
42119         if(this.hiddenField){
42120             this.hiddenField.value = vv;
42121             
42122             this.lastSelectionText = dv;
42123             Roo.form.ComboBox.superclass.setValue.call(this, dv);
42124             this.value = vv;
42125             return;
42126         }
42127         // no hidden field.. - we store the value in 'value', but still display
42128         // display field!!!!
42129         this.lastSelectionText = dv;
42130         Roo.form.ComboBox.superclass.setValue.call(this, dv);
42131         this.value = vv;
42132         
42133         
42134     },
42135     // private
42136     reset : function(){
42137         // overridden so that last data is reset..
42138         this.setValue(this.resetValue);
42139         this.originalValue = this.getValue();
42140         this.clearInvalid();
42141         this.lastData = false;
42142         if (this.view) {
42143             this.view.clearSelections();
42144         }
42145     },
42146     // private
42147     findRecord : function(prop, value){
42148         var record;
42149         if(this.store.getCount() > 0){
42150             this.store.each(function(r){
42151                 if(r.data[prop] == value){
42152                     record = r;
42153                     return false;
42154                 }
42155                 return true;
42156             });
42157         }
42158         return record;
42159     },
42160     
42161     getName: function()
42162     {
42163         // returns hidden if it's set..
42164         if (!this.rendered) {return ''};
42165         return !this.hiddenName && this.el.dom.name  ? this.el.dom.name : (this.hiddenName || '');
42166         
42167     },
42168     // private
42169     onViewMove : function(e, t){
42170         this.inKeyMode = false;
42171     },
42172
42173     // private
42174     onViewOver : function(e, t){
42175         if(this.inKeyMode){ // prevent key nav and mouse over conflicts
42176             return;
42177         }
42178         var item = this.view.findItemFromChild(t);
42179         if(item){
42180             var index = this.view.indexOf(item);
42181             this.select(index, false);
42182         }
42183     },
42184
42185     // private
42186     onViewClick : function(doFocus)
42187     {
42188         var index = this.view.getSelectedIndexes()[0];
42189         var r = this.store.getAt(index);
42190         if(r){
42191             this.onSelect(r, index);
42192         }
42193         if(doFocus !== false && !this.blockFocus){
42194             this.el.focus();
42195         }
42196     },
42197
42198     // private
42199     restrictHeight : function(){
42200         this.innerList.dom.style.height = '';
42201         var inner = this.innerList.dom;
42202         var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
42203         this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
42204         this.list.beginUpdate();
42205         this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
42206         this.list.alignTo(this.el, this.listAlign);
42207         this.list.endUpdate();
42208     },
42209
42210     // private
42211     onEmptyResults : function(){
42212         this.collapse();
42213     },
42214
42215     /**
42216      * Returns true if the dropdown list is expanded, else false.
42217      */
42218     isExpanded : function(){
42219         return this.list.isVisible();
42220     },
42221
42222     /**
42223      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
42224      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
42225      * @param {String} value The data value of the item to select
42226      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
42227      * selected item if it is not currently in view (defaults to true)
42228      * @return {Boolean} True if the value matched an item in the list, else false
42229      */
42230     selectByValue : function(v, scrollIntoView){
42231         if(v !== undefined && v !== null){
42232             var r = this.findRecord(this.valueField || this.displayField, v);
42233             if(r){
42234                 this.select(this.store.indexOf(r), scrollIntoView);
42235                 return true;
42236             }
42237         }
42238         return false;
42239     },
42240
42241     /**
42242      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
42243      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
42244      * @param {Number} index The zero-based index of the list item to select
42245      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
42246      * selected item if it is not currently in view (defaults to true)
42247      */
42248     select : function(index, scrollIntoView){
42249         this.selectedIndex = index;
42250         this.view.select(index);
42251         if(scrollIntoView !== false){
42252             var el = this.view.getNode(index);
42253             if(el){
42254                 this.innerList.scrollChildIntoView(el, false);
42255             }
42256         }
42257     },
42258
42259     // private
42260     selectNext : function(){
42261         var ct = this.store.getCount();
42262         if(ct > 0){
42263             if(this.selectedIndex == -1){
42264                 this.select(0);
42265             }else if(this.selectedIndex < ct-1){
42266                 this.select(this.selectedIndex+1);
42267             }
42268         }
42269     },
42270
42271     // private
42272     selectPrev : function(){
42273         var ct = this.store.getCount();
42274         if(ct > 0){
42275             if(this.selectedIndex == -1){
42276                 this.select(0);
42277             }else if(this.selectedIndex != 0){
42278                 this.select(this.selectedIndex-1);
42279             }
42280         }
42281     },
42282
42283     // private
42284     onKeyUp : function(e){
42285         if(this.editable !== false && !e.isSpecialKey()){
42286             this.lastKey = e.getKey();
42287             this.dqTask.delay(this.queryDelay);
42288         }
42289     },
42290
42291     // private
42292     validateBlur : function(){
42293         return !this.list || !this.list.isVisible();   
42294     },
42295
42296     // private
42297     initQuery : function(){
42298         this.doQuery(this.getRawValue());
42299     },
42300
42301     // private
42302     doForce : function(){
42303         if(this.el.dom.value.length > 0){
42304             this.el.dom.value =
42305                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
42306              
42307         }
42308     },
42309
42310     /**
42311      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
42312      * query allowing the query action to be canceled if needed.
42313      * @param {String} query The SQL query to execute
42314      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
42315      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
42316      * saved in the current store (defaults to false)
42317      */
42318     doQuery : function(q, forceAll){
42319         if(q === undefined || q === null){
42320             q = '';
42321         }
42322         var qe = {
42323             query: q,
42324             forceAll: forceAll,
42325             combo: this,
42326             cancel:false
42327         };
42328         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
42329             return false;
42330         }
42331         q = qe.query;
42332         forceAll = qe.forceAll;
42333         if(forceAll === true || (q.length >= this.minChars)){
42334             if(this.lastQuery != q || this.alwaysQuery){
42335                 this.lastQuery = q;
42336                 if(this.mode == 'local'){
42337                     this.selectedIndex = -1;
42338                     if(forceAll){
42339                         this.store.clearFilter();
42340                     }else{
42341                         this.store.filter(this.displayField, q);
42342                     }
42343                     this.onLoad();
42344                 }else{
42345                     this.store.baseParams[this.queryParam] = q;
42346                     this.store.load({
42347                         params: this.getParams(q)
42348                     });
42349                     this.expand();
42350                 }
42351             }else{
42352                 this.selectedIndex = -1;
42353                 this.onLoad();   
42354             }
42355         }
42356     },
42357
42358     // private
42359     getParams : function(q){
42360         var p = {};
42361         //p[this.queryParam] = q;
42362         if(this.pageSize){
42363             p.start = 0;
42364             p.limit = this.pageSize;
42365         }
42366         return p;
42367     },
42368
42369     /**
42370      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
42371      */
42372     collapse : function(){
42373         if(!this.isExpanded()){
42374             return;
42375         }
42376         this.list.hide();
42377         Roo.get(document).un('mousedown', this.collapseIf, this);
42378         Roo.get(document).un('mousewheel', this.collapseIf, this);
42379         if (!this.editable) {
42380             Roo.get(document).un('keydown', this.listKeyPress, this);
42381         }
42382         this.fireEvent('collapse', this);
42383     },
42384
42385     // private
42386     collapseIf : function(e){
42387         if(!e.within(this.wrap) && !e.within(this.list)){
42388             this.collapse();
42389         }
42390     },
42391
42392     /**
42393      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
42394      */
42395     expand : function(){
42396         if(this.isExpanded() || !this.hasFocus){
42397             return;
42398         }
42399         this.list.alignTo(this.el, this.listAlign);
42400         this.list.show();
42401         Roo.get(document).on('mousedown', this.collapseIf, this);
42402         Roo.get(document).on('mousewheel', this.collapseIf, this);
42403         if (!this.editable) {
42404             Roo.get(document).on('keydown', this.listKeyPress, this);
42405         }
42406         
42407         this.fireEvent('expand', this);
42408     },
42409
42410     // private
42411     // Implements the default empty TriggerField.onTriggerClick function
42412     onTriggerClick : function(){
42413         if(this.disabled){
42414             return;
42415         }
42416         if(this.isExpanded()){
42417             this.collapse();
42418             if (!this.blockFocus) {
42419                 this.el.focus();
42420             }
42421             
42422         }else {
42423             this.hasFocus = true;
42424             if(this.triggerAction == 'all') {
42425                 this.doQuery(this.allQuery, true);
42426             } else {
42427                 this.doQuery(this.getRawValue());
42428             }
42429             if (!this.blockFocus) {
42430                 this.el.focus();
42431             }
42432         }
42433     },
42434     listKeyPress : function(e)
42435     {
42436         //Roo.log('listkeypress');
42437         // scroll to first matching element based on key pres..
42438         if (e.isSpecialKey()) {
42439             return false;
42440         }
42441         var k = String.fromCharCode(e.getKey()).toUpperCase();
42442         //Roo.log(k);
42443         var match  = false;
42444         var csel = this.view.getSelectedNodes();
42445         var cselitem = false;
42446         if (csel.length) {
42447             var ix = this.view.indexOf(csel[0]);
42448             cselitem  = this.store.getAt(ix);
42449             if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
42450                 cselitem = false;
42451             }
42452             
42453         }
42454         
42455         this.store.each(function(v) { 
42456             if (cselitem) {
42457                 // start at existing selection.
42458                 if (cselitem.id == v.id) {
42459                     cselitem = false;
42460                 }
42461                 return;
42462             }
42463                 
42464             if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
42465                 match = this.store.indexOf(v);
42466                 return false;
42467             }
42468         }, this);
42469         
42470         if (match === false) {
42471             return true; // no more action?
42472         }
42473         // scroll to?
42474         this.view.select(match);
42475         var sn = Roo.get(this.view.getSelectedNodes()[0]);
42476         sn.scrollIntoView(sn.dom.parentNode, false);
42477     } 
42478
42479     /** 
42480     * @cfg {Boolean} grow 
42481     * @hide 
42482     */
42483     /** 
42484     * @cfg {Number} growMin 
42485     * @hide 
42486     */
42487     /** 
42488     * @cfg {Number} growMax 
42489     * @hide 
42490     */
42491     /**
42492      * @hide
42493      * @method autoSize
42494      */
42495 });/*
42496  * Copyright(c) 2010-2012, Roo J Solutions Limited
42497  *
42498  * Licence LGPL
42499  *
42500  */
42501
42502 /**
42503  * @class Roo.form.ComboBoxArray
42504  * @extends Roo.form.TextField
42505  * A facebook style adder... for lists of email / people / countries  etc...
42506  * pick multiple items from a combo box, and shows each one.
42507  *
42508  *  Fred [x]  Brian [x]  [Pick another |v]
42509  *
42510  *
42511  *  For this to work: it needs various extra information
42512  *    - normal combo problay has
42513  *      name, hiddenName
42514  *    + displayField, valueField
42515  *
42516  *    For our purpose...
42517  *
42518  *
42519  *   If we change from 'extends' to wrapping...
42520  *   
42521  *  
42522  *
42523  
42524  
42525  * @constructor
42526  * Create a new ComboBoxArray.
42527  * @param {Object} config Configuration options
42528  */
42529  
42530
42531 Roo.form.ComboBoxArray = function(config)
42532 {
42533     this.addEvents({
42534         /**
42535          * @event beforeremove
42536          * Fires before remove the value from the list
42537              * @param {Roo.form.ComboBoxArray} _self This combo box array
42538              * @param {Roo.form.ComboBoxArray.Item} item removed item
42539              */
42540         'beforeremove' : true,
42541         /**
42542          * @event remove
42543          * Fires when remove the value from the list
42544              * @param {Roo.form.ComboBoxArray} _self This combo box array
42545              * @param {Roo.form.ComboBoxArray.Item} item removed item
42546              */
42547         'remove' : true
42548         
42549         
42550     });
42551     
42552     Roo.form.ComboBoxArray.superclass.constructor.call(this, config);
42553     
42554     this.items = new Roo.util.MixedCollection(false);
42555     
42556     // construct the child combo...
42557     
42558     
42559     
42560     
42561    
42562     
42563 }
42564
42565  
42566 Roo.extend(Roo.form.ComboBoxArray, Roo.form.TextField,
42567
42568     /**
42569      * @cfg {Roo.form.ComboBox} combo [required] The combo box that is wrapped
42570      */
42571     
42572     lastData : false,
42573     
42574     // behavies liek a hiddne field
42575     inputType:      'hidden',
42576     /**
42577      * @cfg {Number} width The width of the box that displays the selected element
42578      */ 
42579     width:          300,
42580
42581     
42582     
42583     /**
42584      * @cfg {String} name    The name of the visable items on this form (eg. titles not ids)
42585      */
42586     name : false,
42587     /**
42588      * @cfg {String} hiddenName    The hidden name of the field, often contains an comma seperated list of names
42589      */
42590     hiddenName : false,
42591       /**
42592      * @cfg {String} seperator    The value seperator normally ',' 
42593      */
42594     seperator : ',',
42595     
42596     // private the array of items that are displayed..
42597     items  : false,
42598     // private - the hidden field el.
42599     hiddenEl : false,
42600     // private - the filed el..
42601     el : false,
42602     
42603     //validateValue : function() { return true; }, // all values are ok!
42604     //onAddClick: function() { },
42605     
42606     onRender : function(ct, position) 
42607     {
42608         
42609         // create the standard hidden element
42610         //Roo.form.ComboBoxArray.superclass.onRender.call(this, ct, position);
42611         
42612         
42613         // give fake names to child combo;
42614         this.combo.hiddenName = this.hiddenName ? (this.hiddenName+'-subcombo') : this.hiddenName;
42615         this.combo.name = this.name ? (this.name+'-subcombo') : this.name;
42616         
42617         this.combo = Roo.factory(this.combo, Roo.form);
42618         this.combo.onRender(ct, position);
42619         if (typeof(this.combo.width) != 'undefined') {
42620             this.combo.onResize(this.combo.width,0);
42621         }
42622         
42623         this.combo.initEvents();
42624         
42625         // assigned so form know we need to do this..
42626         this.store          = this.combo.store;
42627         this.valueField     = this.combo.valueField;
42628         this.displayField   = this.combo.displayField ;
42629         
42630         
42631         this.combo.wrap.addClass('x-cbarray-grp');
42632         
42633         var cbwrap = this.combo.wrap.createChild(
42634             {tag: 'div', cls: 'x-cbarray-cb'},
42635             this.combo.el.dom
42636         );
42637         
42638              
42639         this.hiddenEl = this.combo.wrap.createChild({
42640             tag: 'input',  type:'hidden' , name: this.hiddenName, value : ''
42641         });
42642         this.el = this.combo.wrap.createChild({
42643             tag: 'input',  type:'hidden' , name: this.name, value : ''
42644         });
42645          //   this.el.dom.removeAttribute("name");
42646         
42647         
42648         this.outerWrap = this.combo.wrap;
42649         this.wrap = cbwrap;
42650         
42651         this.outerWrap.setWidth(this.width);
42652         this.outerWrap.dom.removeChild(this.el.dom);
42653         
42654         this.wrap.dom.appendChild(this.el.dom);
42655         this.outerWrap.dom.removeChild(this.combo.trigger.dom);
42656         this.combo.wrap.dom.appendChild(this.combo.trigger.dom);
42657         
42658         this.combo.trigger.setStyle('position','relative');
42659         this.combo.trigger.setStyle('left', '0px');
42660         this.combo.trigger.setStyle('top', '2px');
42661         
42662         this.combo.el.setStyle('vertical-align', 'text-bottom');
42663         
42664         //this.trigger.setStyle('vertical-align', 'top');
42665         
42666         // this should use the code from combo really... on('add' ....)
42667         if (this.adder) {
42668             
42669         
42670             this.adder = this.outerWrap.createChild(
42671                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-adder', style: 'margin-left:2px'});  
42672             var _t = this;
42673             this.adder.on('click', function(e) {
42674                 _t.fireEvent('adderclick', this, e);
42675             }, _t);
42676         }
42677         //var _t = this;
42678         //this.adder.on('click', this.onAddClick, _t);
42679         
42680         
42681         this.combo.on('select', function(cb, rec, ix) {
42682             this.addItem(rec.data);
42683             
42684             cb.setValue('');
42685             cb.el.dom.value = '';
42686             //cb.lastData = rec.data;
42687             // add to list
42688             
42689         }, this);
42690         
42691         
42692     },
42693     
42694     
42695     getName: function()
42696     {
42697         // returns hidden if it's set..
42698         if (!this.rendered) {return ''};
42699         return  this.hiddenName ? this.hiddenName : this.name;
42700         
42701     },
42702     
42703     
42704     onResize: function(w, h){
42705         
42706         return;
42707         // not sure if this is needed..
42708         //this.combo.onResize(w,h);
42709         
42710         if(typeof w != 'number'){
42711             // we do not handle it!?!?
42712             return;
42713         }
42714         var tw = this.combo.trigger.getWidth();
42715         tw += this.addicon ? this.addicon.getWidth() : 0;
42716         tw += this.editicon ? this.editicon.getWidth() : 0;
42717         var x = w - tw;
42718         this.combo.el.setWidth( this.combo.adjustWidth('input', x));
42719             
42720         this.combo.trigger.setStyle('left', '0px');
42721         
42722         if(this.list && this.listWidth === undefined){
42723             var lw = Math.max(x + this.combo.trigger.getWidth(), this.combo.minListWidth);
42724             this.list.setWidth(lw);
42725             this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
42726         }
42727         
42728     
42729         
42730     },
42731     
42732     addItem: function(rec)
42733     {
42734         var valueField = this.combo.valueField;
42735         var displayField = this.combo.displayField;
42736         
42737         if (this.items.indexOfKey(rec[valueField]) > -1) {
42738             //console.log("GOT " + rec.data.id);
42739             return;
42740         }
42741         
42742         var x = new Roo.form.ComboBoxArray.Item({
42743             //id : rec[this.idField],
42744             data : rec,
42745             displayField : displayField ,
42746             tipField : displayField ,
42747             cb : this
42748         });
42749         // use the 
42750         this.items.add(rec[valueField],x);
42751         // add it before the element..
42752         this.updateHiddenEl();
42753         x.render(this.outerWrap, this.wrap.dom);
42754         // add the image handler..
42755     },
42756     
42757     updateHiddenEl : function()
42758     {
42759         this.validate();
42760         if (!this.hiddenEl) {
42761             return;
42762         }
42763         var ar = [];
42764         var idField = this.combo.valueField;
42765         
42766         this.items.each(function(f) {
42767             ar.push(f.data[idField]);
42768         });
42769         this.hiddenEl.dom.value = ar.join(this.seperator);
42770         this.validate();
42771     },
42772     
42773     reset : function()
42774     {
42775         this.items.clear();
42776         
42777         Roo.each(this.outerWrap.select('.x-cbarray-item', true).elements, function(el){
42778            el.remove();
42779         });
42780         
42781         this.el.dom.value = '';
42782         if (this.hiddenEl) {
42783             this.hiddenEl.dom.value = '';
42784         }
42785         
42786     },
42787     getValue: function()
42788     {
42789         return this.hiddenEl ? this.hiddenEl.dom.value : '';
42790     },
42791     setValue: function(v) // not a valid action - must use addItems..
42792     {
42793         
42794         this.reset();
42795          
42796         if (this.store.isLocal && (typeof(v) == 'string')) {
42797             // then we can use the store to find the values..
42798             // comma seperated at present.. this needs to allow JSON based encoding..
42799             this.hiddenEl.value  = v;
42800             var v_ar = [];
42801             Roo.each(v.split(this.seperator), function(k) {
42802                 Roo.log("CHECK " + this.valueField + ',' + k);
42803                 var li = this.store.query(this.valueField, k);
42804                 if (!li.length) {
42805                     return;
42806                 }
42807                 var add = {};
42808                 add[this.valueField] = k;
42809                 add[this.displayField] = li.item(0).data[this.displayField];
42810                 
42811                 this.addItem(add);
42812             }, this) 
42813              
42814         }
42815         if (typeof(v) == 'object' ) {
42816             // then let's assume it's an array of objects..
42817             Roo.each(v, function(l) {
42818                 var add = l;
42819                 if (typeof(l) == 'string') {
42820                     add = {};
42821                     add[this.valueField] = l;
42822                     add[this.displayField] = l
42823                 }
42824                 this.addItem(add);
42825             }, this);
42826              
42827         }
42828         
42829         
42830     },
42831     setFromData: function(v)
42832     {
42833         // this recieves an object, if setValues is called.
42834         this.reset();
42835         this.el.dom.value = v[this.displayField];
42836         this.hiddenEl.dom.value = v[this.valueField];
42837         if (typeof(v[this.valueField]) != 'string' || !v[this.valueField].length) {
42838             return;
42839         }
42840         var kv = v[this.valueField];
42841         var dv = v[this.displayField];
42842         kv = typeof(kv) != 'string' ? '' : kv;
42843         dv = typeof(dv) != 'string' ? '' : dv;
42844         
42845         
42846         var keys = kv.split(this.seperator);
42847         var display = dv.split(this.seperator);
42848         for (var i = 0 ; i < keys.length; i++) {
42849             add = {};
42850             add[this.valueField] = keys[i];
42851             add[this.displayField] = display[i];
42852             this.addItem(add);
42853         }
42854       
42855         
42856     },
42857     
42858     /**
42859      * Validates the combox array value
42860      * @return {Boolean} True if the value is valid, else false
42861      */
42862     validate : function(){
42863         if(this.disabled || this.validateValue(this.processValue(this.getValue()))){
42864             this.clearInvalid();
42865             return true;
42866         }
42867         return false;
42868     },
42869     
42870     validateValue : function(value){
42871         return Roo.form.ComboBoxArray.superclass.validateValue.call(this, this.getValue());
42872         
42873     },
42874     
42875     /*@
42876      * overide
42877      * 
42878      */
42879     isDirty : function() {
42880         if(this.disabled) {
42881             return false;
42882         }
42883         
42884         try {
42885             var d = Roo.decode(String(this.originalValue));
42886         } catch (e) {
42887             return String(this.getValue()) !== String(this.originalValue);
42888         }
42889         
42890         var originalValue = [];
42891         
42892         for (var i = 0; i < d.length; i++){
42893             originalValue.push(d[i][this.valueField]);
42894         }
42895         
42896         return String(this.getValue()) !== String(originalValue.join(this.seperator));
42897         
42898     }
42899     
42900 });
42901
42902
42903
42904 /**
42905  * @class Roo.form.ComboBoxArray.Item
42906  * @extends Roo.BoxComponent
42907  * A selected item in the list
42908  *  Fred [x]  Brian [x]  [Pick another |v]
42909  * 
42910  * @constructor
42911  * Create a new item.
42912  * @param {Object} config Configuration options
42913  */
42914  
42915 Roo.form.ComboBoxArray.Item = function(config) {
42916     config.id = Roo.id();
42917     Roo.form.ComboBoxArray.Item.superclass.constructor.call(this, config);
42918 }
42919
42920 Roo.extend(Roo.form.ComboBoxArray.Item, Roo.BoxComponent, {
42921     data : {},
42922     cb: false,
42923     displayField : false,
42924     tipField : false,
42925     
42926     
42927     defaultAutoCreate : {
42928         tag: 'div',
42929         cls: 'x-cbarray-item',
42930         cn : [ 
42931             { tag: 'div' },
42932             {
42933                 tag: 'img',
42934                 width:16,
42935                 height : 16,
42936                 src : Roo.BLANK_IMAGE_URL ,
42937                 align: 'center'
42938             }
42939         ]
42940         
42941     },
42942     
42943  
42944     onRender : function(ct, position)
42945     {
42946         Roo.form.Field.superclass.onRender.call(this, ct, position);
42947         
42948         if(!this.el){
42949             var cfg = this.getAutoCreate();
42950             this.el = ct.createChild(cfg, position);
42951         }
42952         
42953         this.el.child('img').dom.setAttribute('src', Roo.BLANK_IMAGE_URL);
42954         
42955         this.el.child('div').dom.innerHTML = this.cb.renderer ? 
42956             this.cb.renderer(this.data) :
42957             String.format('{0}',this.data[this.displayField]);
42958         
42959             
42960         this.el.child('div').dom.setAttribute('qtip',
42961                         String.format('{0}',this.data[this.tipField])
42962         );
42963         
42964         this.el.child('img').on('click', this.remove, this);
42965         
42966     },
42967    
42968     remove : function()
42969     {
42970         if(this.cb.disabled){
42971             return;
42972         }
42973         
42974         if(false !== this.cb.fireEvent('beforeremove', this.cb, this)){
42975             this.cb.items.remove(this);
42976             this.el.child('img').un('click', this.remove, this);
42977             this.el.remove();
42978             this.cb.updateHiddenEl();
42979
42980             this.cb.fireEvent('remove', this.cb, this);
42981         }
42982         
42983     }
42984 });/*
42985  * RooJS Library 1.1.1
42986  * Copyright(c) 2008-2011  Alan Knowles
42987  *
42988  * License - LGPL
42989  */
42990  
42991
42992 /**
42993  * @class Roo.form.ComboNested
42994  * @extends Roo.form.ComboBox
42995  * A combobox for that allows selection of nested items in a list,
42996  * eg.
42997  *
42998  *  Book
42999  *    -> red
43000  *    -> green
43001  *  Table
43002  *    -> square
43003  *      ->red
43004  *      ->green
43005  *    -> rectangle
43006  *      ->green
43007  *      
43008  * 
43009  * @constructor
43010  * Create a new ComboNested
43011  * @param {Object} config Configuration options
43012  */
43013 Roo.form.ComboNested = function(config){
43014     Roo.form.ComboCheck.superclass.constructor.call(this, config);
43015     // should verify some data...
43016     // like
43017     // hiddenName = required..
43018     // displayField = required
43019     // valudField == required
43020     var req= [ 'hiddenName', 'displayField', 'valueField' ];
43021     var _t = this;
43022     Roo.each(req, function(e) {
43023         if ((typeof(_t[e]) == 'undefined' ) || !_t[e].length) {
43024             throw "Roo.form.ComboNested : missing value for: " + e;
43025         }
43026     });
43027      
43028     
43029 };
43030
43031 Roo.extend(Roo.form.ComboNested, Roo.form.ComboBox, {
43032    
43033     /*
43034      * @config {Number} max Number of columns to show
43035      */
43036     
43037     maxColumns : 3,
43038    
43039     list : null, // the outermost div..
43040     innerLists : null, // the
43041     views : null,
43042     stores : null,
43043     // private
43044     loadingChildren : false,
43045     
43046     onRender : function(ct, position)
43047     {
43048         Roo.form.ComboBox.superclass.onRender.call(this, ct, position); // skip parent call - got to above..
43049         
43050         if(this.hiddenName){
43051             this.hiddenField = this.el.insertSibling({tag:'input', type:'hidden', name: this.hiddenName, id:  (this.hiddenId||this.hiddenName)},
43052                     'before', true);
43053             this.hiddenField.value =
43054                 this.hiddenValue !== undefined ? this.hiddenValue :
43055                 this.value !== undefined ? this.value : '';
43056
43057             // prevent input submission
43058             this.el.dom.removeAttribute('name');
43059              
43060              
43061         }
43062         
43063         if(Roo.isGecko){
43064             this.el.dom.setAttribute('autocomplete', 'off');
43065         }
43066
43067         var cls = 'x-combo-list';
43068
43069         this.list = new Roo.Layer({
43070             shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
43071         });
43072
43073         var lw = this.listWidth || Math.max(this.wrap.getWidth(), this.minListWidth);
43074         this.list.setWidth(lw);
43075         this.list.swallowEvent('mousewheel');
43076         this.assetHeight = 0;
43077
43078         if(this.title){
43079             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
43080             this.assetHeight += this.header.getHeight();
43081         }
43082         this.innerLists = [];
43083         this.views = [];
43084         this.stores = [];
43085         for (var i =0 ; i < this.maxColumns; i++) {
43086             this.onRenderList( cls, i);
43087         }
43088         
43089         // always needs footer, as we are going to have an 'OK' button.
43090         this.footer = this.list.createChild({cls:cls+'-ft'});
43091         this.pageTb = new Roo.Toolbar(this.footer);  
43092         var _this = this;
43093         this.pageTb.add(  {
43094             
43095             text: 'Done',
43096             handler: function()
43097             {
43098                 _this.collapse();
43099             }
43100         });
43101         
43102         if ( this.allowBlank && !this.disableClear) {
43103             
43104             this.pageTb.add(new Roo.Toolbar.Fill(), {
43105                 cls: 'x-btn-icon x-btn-clear',
43106                 text: '&#160;',
43107                 handler: function()
43108                 {
43109                     _this.collapse();
43110                     _this.clearValue();
43111                     _this.onSelect(false, -1);
43112                 }
43113             });
43114         }
43115         if (this.footer) {
43116             this.assetHeight += this.footer.getHeight();
43117         }
43118         
43119     },
43120     onRenderList : function (  cls, i)
43121     {
43122         
43123         var lw = Math.floor(
43124                 ((this.listWidth * this.maxColumns || Math.max(this.wrap.getWidth(), this.minListWidth)) - this.list.getFrameWidth('lr')) / this.maxColumns
43125         );
43126         
43127         this.list.setWidth(lw); // default to '1'
43128
43129         var il = this.innerLists[i] = this.list.createChild({cls:cls+'-inner'});
43130         //il.on('mouseover', this.onViewOver, this, { list:  i });
43131         //il.on('mousemove', this.onViewMove, this, { list:  i });
43132         il.setWidth(lw);
43133         il.setStyle({ 'overflow-x' : 'hidden'});
43134
43135         if(!this.tpl){
43136             this.tpl = new Roo.Template({
43137                 html :  '<div class="'+cls+'-item '+cls+'-item-{cn:this.isEmpty}">{' + this.displayField + '}</div>',
43138                 isEmpty: function (value, allValues) {
43139                     //Roo.log(value);
43140                     var dl = typeof(value.data) != 'undefined' ? value.data.length : value.length; ///json is a nested response..
43141                     return dl ? 'has-children' : 'no-children'
43142                 }
43143             });
43144         }
43145         
43146         var store  = this.store;
43147         if (i > 0) {
43148             store  = new Roo.data.SimpleStore({
43149                 //fields : this.store.reader.meta.fields,
43150                 reader : this.store.reader,
43151                 data : [ ]
43152             });
43153         }
43154         this.stores[i]  = store;
43155                   
43156         var view = this.views[i] = new Roo.View(
43157             il,
43158             this.tpl,
43159             {
43160                 singleSelect:true,
43161                 store: store,
43162                 selectedClass: this.selectedClass
43163             }
43164         );
43165         view.getEl().setWidth(lw);
43166         view.getEl().setStyle({
43167             position: i < 1 ? 'relative' : 'absolute',
43168             top: 0,
43169             left: (i * lw ) + 'px',
43170             display : i > 0 ? 'none' : 'block'
43171         });
43172         view.on('selectionchange', this.onSelectChange.createDelegate(this, {list : i }, true));
43173         view.on('dblclick', this.onDoubleClick.createDelegate(this, {list : i }, true));
43174         //view.on('click', this.onViewClick, this, { list : i });
43175
43176         store.on('beforeload', this.onBeforeLoad, this);
43177         store.on('load',  this.onLoad, this, { list  : i});
43178         store.on('loadexception', this.onLoadException, this);
43179
43180         // hide the other vies..
43181         
43182         
43183         
43184     },
43185       
43186     restrictHeight : function()
43187     {
43188         var mh = 0;
43189         Roo.each(this.innerLists, function(il,i) {
43190             var el = this.views[i].getEl();
43191             el.dom.style.height = '';
43192             var inner = el.dom;
43193             var h = Math.max(il.clientHeight, il.offsetHeight, il.scrollHeight);
43194             // only adjust heights on other ones..
43195             mh = Math.max(h, mh);
43196             if (i < 1) {
43197                 
43198                 el.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
43199                 il.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
43200                
43201             }
43202             
43203             
43204         }, this);
43205         
43206         this.list.beginUpdate();
43207         this.list.setHeight(mh+this.list.getFrameWidth('tb')+this.assetHeight);
43208         this.list.alignTo(this.el, this.listAlign);
43209         this.list.endUpdate();
43210         
43211     },
43212      
43213     
43214     // -- store handlers..
43215     // private
43216     onBeforeLoad : function()
43217     {
43218         if(!this.hasFocus){
43219             return;
43220         }
43221         this.innerLists[0].update(this.loadingText ?
43222                '<div class="loading-indicator">'+this.loadingText+'</div>' : '');
43223         this.restrictHeight();
43224         this.selectedIndex = -1;
43225     },
43226     // private
43227     onLoad : function(a,b,c,d)
43228     {
43229         if (!this.loadingChildren) {
43230             // then we are loading the top level. - hide the children
43231             for (var i = 1;i < this.views.length; i++) {
43232                 this.views[i].getEl().setStyle({ display : 'none' });
43233             }
43234             var lw = Math.floor(
43235                 ((this.listWidth * this.maxColumns || Math.max(this.wrap.getWidth(), this.minListWidth)) - this.list.getFrameWidth('lr')) / this.maxColumns
43236             );
43237         
43238              this.list.setWidth(lw); // default to '1'
43239
43240             
43241         }
43242         if(!this.hasFocus){
43243             return;
43244         }
43245         
43246         if(this.store.getCount() > 0) {
43247             this.expand();
43248             this.restrictHeight();   
43249         } else {
43250             this.onEmptyResults();
43251         }
43252         
43253         if (!this.loadingChildren) {
43254             this.selectActive();
43255         }
43256         /*
43257         this.stores[1].loadData([]);
43258         this.stores[2].loadData([]);
43259         this.views
43260         */    
43261     
43262         //this.el.focus();
43263     },
43264     
43265     
43266     // private
43267     onLoadException : function()
43268     {
43269         this.collapse();
43270         Roo.log(this.store.reader.jsonData);
43271         if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
43272             Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
43273         }
43274         
43275         
43276     },
43277     // no cleaning of leading spaces on blur here.
43278     cleanLeadingSpace : function(e) { },
43279     
43280
43281     onSelectChange : function (view, sels, opts )
43282     {
43283         var ix = view.getSelectedIndexes();
43284          
43285         if (opts.list > this.maxColumns - 2) {
43286             if (view.store.getCount()<  1) {
43287                 this.views[opts.list ].getEl().setStyle({ display :   'none' });
43288
43289             } else  {
43290                 if (ix.length) {
43291                     // used to clear ?? but if we are loading unselected 
43292                     this.setFromData(view.store.getAt(ix[0]).data);
43293                 }
43294                 
43295             }
43296             
43297             return;
43298         }
43299         
43300         if (!ix.length) {
43301             // this get's fired when trigger opens..
43302            // this.setFromData({});
43303             var str = this.stores[opts.list+1];
43304             str.data.clear(); // removeall wihtout the fire events..
43305             return;
43306         }
43307         
43308         var rec = view.store.getAt(ix[0]);
43309          
43310         this.setFromData(rec.data);
43311         this.fireEvent('select', this, rec, ix[0]);
43312         
43313         var lw = Math.floor(
43314              (
43315                 (this.listWidth * this.maxColumns || Math.max(this.wrap.getWidth(), this.minListWidth)) - this.list.getFrameWidth('lr')
43316              ) / this.maxColumns
43317         );
43318         this.loadingChildren = true;
43319         this.stores[opts.list+1].loadDataFromChildren( rec );
43320         this.loadingChildren = false;
43321         var dl = this.stores[opts.list+1]. getTotalCount();
43322         
43323         this.views[opts.list+1].getEl().setHeight( this.innerLists[0].getHeight());
43324         
43325         this.views[opts.list+1].getEl().setStyle({ display : dl ? 'block' : 'none' });
43326         for (var i = opts.list+2; i < this.views.length;i++) {
43327             this.views[i].getEl().setStyle({ display : 'none' });
43328         }
43329         
43330         this.innerLists[opts.list+1].setHeight( this.innerLists[0].getHeight());
43331         this.list.setWidth(lw * (opts.list + (dl ? 2 : 1)));
43332         
43333         if (this.isLoading) {
43334            // this.selectActive(opts.list);
43335         }
43336          
43337     },
43338     
43339     
43340     
43341     
43342     onDoubleClick : function()
43343     {
43344         this.collapse(); //??
43345     },
43346     
43347      
43348     
43349     
43350     
43351     // private
43352     recordToStack : function(store, prop, value, stack)
43353     {
43354         var cstore = new Roo.data.SimpleStore({
43355             //fields : this.store.reader.meta.fields, // we need array reader.. for
43356             reader : this.store.reader,
43357             data : [ ]
43358         });
43359         var _this = this;
43360         var record  = false;
43361         var srec = false;
43362         if(store.getCount() < 1){
43363             return false;
43364         }
43365         store.each(function(r){
43366             if(r.data[prop] == value){
43367                 record = r;
43368             srec = r;
43369                 return false;
43370             }
43371             if (r.data.cn && r.data.cn.length) {
43372                 cstore.loadDataFromChildren( r);
43373                 var cret = _this.recordToStack(cstore, prop, value, stack);
43374                 if (cret !== false) {
43375                     record = cret;
43376                     srec = r;
43377                     return false;
43378                 }
43379             }
43380              
43381             return true;
43382         });
43383         if (record == false) {
43384             return false
43385         }
43386         stack.unshift(srec);
43387         return record;
43388     },
43389     
43390     /*
43391      * find the stack of stores that match our value.
43392      *
43393      * 
43394      */
43395     
43396     selectActive : function ()
43397     {
43398         // if store is not loaded, then we will need to wait for that to happen first.
43399         var stack = [];
43400         this.recordToStack(this.store, this.valueField, this.getValue(), stack);
43401         for (var i = 0; i < stack.length; i++ ) {
43402             this.views[i].select(stack[i].store.indexOf(stack[i]), false, false );
43403         }
43404         
43405     }
43406         
43407          
43408     
43409     
43410     
43411     
43412 });/*
43413  * Based on:
43414  * Ext JS Library 1.1.1
43415  * Copyright(c) 2006-2007, Ext JS, LLC.
43416  *
43417  * Originally Released Under LGPL - original licence link has changed is not relivant.
43418  *
43419  * Fork - LGPL
43420  * <script type="text/javascript">
43421  */
43422 /**
43423  * @class Roo.form.Checkbox
43424  * @extends Roo.form.Field
43425  * Single checkbox field.  Can be used as a direct replacement for traditional checkbox fields.
43426  * @constructor
43427  * Creates a new Checkbox
43428  * @param {Object} config Configuration options
43429  */
43430 Roo.form.Checkbox = function(config){
43431     Roo.form.Checkbox.superclass.constructor.call(this, config);
43432     this.addEvents({
43433         /**
43434          * @event check
43435          * Fires when the checkbox is checked or unchecked.
43436              * @param {Roo.form.Checkbox} this This checkbox
43437              * @param {Boolean} checked The new checked value
43438              */
43439         check : true
43440     });
43441 };
43442
43443 Roo.extend(Roo.form.Checkbox, Roo.form.Field,  {
43444     /**
43445      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
43446      */
43447     focusClass : undefined,
43448     /**
43449      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
43450      */
43451     fieldClass: "x-form-field",
43452     /**
43453      * @cfg {Boolean} checked True if the the checkbox should render already checked (defaults to false)
43454      */
43455     checked: false,
43456     /**
43457      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
43458      * {tag: "input", type: "checkbox", autocomplete: "off"})
43459      */
43460     defaultAutoCreate : { tag: "input", type: 'hidden', autocomplete: "off"},
43461     /**
43462      * @cfg {String} boxLabel The text that appears beside the checkbox
43463      */
43464     boxLabel : "",
43465     /**
43466      * @cfg {String} inputValue The value that should go into the generated input element's value attribute
43467      */  
43468     inputValue : '1',
43469     /**
43470      * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
43471      */
43472      valueOff: '0', // value when not checked..
43473
43474     actionMode : 'viewEl', 
43475     //
43476     // private
43477     itemCls : 'x-menu-check-item x-form-item',
43478     groupClass : 'x-menu-group-item',
43479     inputType : 'hidden',
43480     
43481     
43482     inSetChecked: false, // check that we are not calling self...
43483     
43484     inputElement: false, // real input element?
43485     basedOn: false, // ????
43486     
43487     isFormField: true, // not sure where this is needed!!!!
43488
43489     onResize : function(){
43490         Roo.form.Checkbox.superclass.onResize.apply(this, arguments);
43491         if(!this.boxLabel){
43492             this.el.alignTo(this.wrap, 'c-c');
43493         }
43494     },
43495
43496     initEvents : function(){
43497         Roo.form.Checkbox.superclass.initEvents.call(this);
43498         this.el.on("click", this.onClick,  this);
43499         this.el.on("change", this.onClick,  this);
43500     },
43501
43502
43503     getResizeEl : function(){
43504         return this.wrap;
43505     },
43506
43507     getPositionEl : function(){
43508         return this.wrap;
43509     },
43510
43511     // private
43512     onRender : function(ct, position){
43513         Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
43514         /*
43515         if(this.inputValue !== undefined){
43516             this.el.dom.value = this.inputValue;
43517         }
43518         */
43519         //this.wrap = this.el.wrap({cls: "x-form-check-wrap"});
43520         this.wrap = this.el.wrap({cls: 'x-menu-check-item '});
43521         var viewEl = this.wrap.createChild({ 
43522             tag: 'img', cls: 'x-menu-item-icon', style: 'margin: 0px;' ,src : Roo.BLANK_IMAGE_URL });
43523         this.viewEl = viewEl;   
43524         this.wrap.on('click', this.onClick,  this); 
43525         
43526         this.el.on('DOMAttrModified', this.setFromHidden,  this); //ff
43527         this.el.on('propertychange', this.setFromHidden,  this);  //ie
43528         
43529         
43530         
43531         if(this.boxLabel){
43532             this.wrap.createChild({tag: 'label', htmlFor: this.el.id, cls: 'x-form-cb-label', html: this.boxLabel});
43533         //    viewEl.on('click', this.onClick,  this); 
43534         }
43535         //if(this.checked){
43536             this.setChecked(this.checked);
43537         //}else{
43538             //this.checked = this.el.dom;
43539         //}
43540
43541     },
43542
43543     // private
43544     initValue : Roo.emptyFn,
43545
43546     /**
43547      * Returns the checked state of the checkbox.
43548      * @return {Boolean} True if checked, else false
43549      */
43550     getValue : function(){
43551         if(this.el){
43552             return String(this.el.dom.value) == String(this.inputValue ) ? this.inputValue : this.valueOff;
43553         }
43554         return this.valueOff;
43555         
43556     },
43557
43558         // private
43559     onClick : function(){ 
43560         if (this.disabled) {
43561             return;
43562         }
43563         this.setChecked(!this.checked);
43564
43565         //if(this.el.dom.checked != this.checked){
43566         //    this.setValue(this.el.dom.checked);
43567        // }
43568     },
43569
43570     /**
43571      * Sets the checked state of the checkbox.
43572      * On is always based on a string comparison between inputValue and the param.
43573      * @param {Boolean/String} value - the value to set 
43574      * @param {Boolean/String} suppressEvent - whether to suppress the checkchange event.
43575      */
43576     setValue : function(v,suppressEvent){
43577         
43578         
43579         //this.checked = (v === true || v === 'true' || v == '1' || String(v).toLowerCase() == 'on');
43580         //if(this.el && this.el.dom){
43581         //    this.el.dom.checked = this.checked;
43582         //    this.el.dom.defaultChecked = this.checked;
43583         //}
43584         this.setChecked(String(v) === String(this.inputValue), suppressEvent);
43585         //this.fireEvent("check", this, this.checked);
43586     },
43587     // private..
43588     setChecked : function(state,suppressEvent)
43589     {
43590         if (this.inSetChecked) {
43591             this.checked = state;
43592             return;
43593         }
43594         
43595     
43596         if(this.wrap){
43597             this.wrap[state ? 'addClass' : 'removeClass']('x-menu-item-checked');
43598         }
43599         this.checked = state;
43600         if(suppressEvent !== true){
43601             this.fireEvent('check', this, state);
43602         }
43603         this.inSetChecked = true;
43604         this.el.dom.value = state ? this.inputValue : this.valueOff;
43605         this.inSetChecked = false;
43606         
43607     },
43608     // handle setting of hidden value by some other method!!?!?
43609     setFromHidden: function()
43610     {
43611         if(!this.el){
43612             return;
43613         }
43614         //console.log("SET FROM HIDDEN");
43615         //alert('setFrom hidden');
43616         this.setValue(this.el.dom.value);
43617     },
43618     
43619     onDestroy : function()
43620     {
43621         if(this.viewEl){
43622             Roo.get(this.viewEl).remove();
43623         }
43624          
43625         Roo.form.Checkbox.superclass.onDestroy.call(this);
43626     },
43627     
43628     setBoxLabel : function(str)
43629     {
43630         this.wrap.select('.x-form-cb-label', true).first().dom.innerHTML = str;
43631     }
43632
43633 });/*
43634  * Based on:
43635  * Ext JS Library 1.1.1
43636  * Copyright(c) 2006-2007, Ext JS, LLC.
43637  *
43638  * Originally Released Under LGPL - original licence link has changed is not relivant.
43639  *
43640  * Fork - LGPL
43641  * <script type="text/javascript">
43642  */
43643  
43644 /**
43645  * @class Roo.form.Radio
43646  * @extends Roo.form.Checkbox
43647  * Single radio field.  Same as Checkbox, but provided as a convenience for automatically setting the input type.
43648  * Radio grouping is handled automatically by the browser if you give each radio in a group the same name.
43649  * @constructor
43650  * Creates a new Radio
43651  * @param {Object} config Configuration options
43652  */
43653 Roo.form.Radio = function(){
43654     Roo.form.Radio.superclass.constructor.apply(this, arguments);
43655 };
43656 Roo.extend(Roo.form.Radio, Roo.form.Checkbox, {
43657     inputType: 'radio',
43658
43659     /**
43660      * If this radio is part of a group, it will return the selected value
43661      * @return {String}
43662      */
43663     getGroupValue : function(){
43664         return this.el.up('form').child('input[name='+this.el.dom.name+']:checked', true).value;
43665     },
43666     
43667     
43668     onRender : function(ct, position){
43669         Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
43670         
43671         if(this.inputValue !== undefined){
43672             this.el.dom.value = this.inputValue;
43673         }
43674          
43675         this.wrap = this.el.wrap({cls: "x-form-check-wrap"});
43676         //this.wrap = this.el.wrap({cls: 'x-menu-check-item '});
43677         //var viewEl = this.wrap.createChild({ 
43678         //    tag: 'img', cls: 'x-menu-item-icon', style: 'margin: 0px;' ,src : Roo.BLANK_IMAGE_URL });
43679         //this.viewEl = viewEl;   
43680         //this.wrap.on('click', this.onClick,  this); 
43681         
43682         //this.el.on('DOMAttrModified', this.setFromHidden,  this); //ff
43683         //this.el.on('propertychange', this.setFromHidden,  this);  //ie
43684         
43685         
43686         
43687         if(this.boxLabel){
43688             this.wrap.createChild({tag: 'label', htmlFor: this.el.id, cls: 'x-form-cb-label', html: this.boxLabel});
43689         //    viewEl.on('click', this.onClick,  this); 
43690         }
43691          if(this.checked){
43692             this.el.dom.checked =   'checked' ;
43693         }
43694          
43695     } 
43696     
43697     
43698 });//<script type="text/javascript">
43699
43700 /*
43701  * Based  Ext JS Library 1.1.1
43702  * Copyright(c) 2006-2007, Ext JS, LLC.
43703  * LGPL
43704  *
43705  */
43706  
43707 /**
43708  * @class Roo.HtmlEditorCore
43709  * @extends Roo.Component
43710  * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
43711  *
43712  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
43713  */
43714
43715 Roo.HtmlEditorCore = function(config){
43716     
43717     
43718     Roo.HtmlEditorCore.superclass.constructor.call(this, config);
43719     
43720     
43721     this.addEvents({
43722         /**
43723          * @event initialize
43724          * Fires when the editor is fully initialized (including the iframe)
43725          * @param {Roo.HtmlEditorCore} this
43726          */
43727         initialize: true,
43728         /**
43729          * @event activate
43730          * Fires when the editor is first receives the focus. Any insertion must wait
43731          * until after this event.
43732          * @param {Roo.HtmlEditorCore} this
43733          */
43734         activate: true,
43735          /**
43736          * @event beforesync
43737          * Fires before the textarea is updated with content from the editor iframe. Return false
43738          * to cancel the sync.
43739          * @param {Roo.HtmlEditorCore} this
43740          * @param {String} html
43741          */
43742         beforesync: true,
43743          /**
43744          * @event beforepush
43745          * Fires before the iframe editor is updated with content from the textarea. Return false
43746          * to cancel the push.
43747          * @param {Roo.HtmlEditorCore} this
43748          * @param {String} html
43749          */
43750         beforepush: true,
43751          /**
43752          * @event sync
43753          * Fires when the textarea is updated with content from the editor iframe.
43754          * @param {Roo.HtmlEditorCore} this
43755          * @param {String} html
43756          */
43757         sync: true,
43758          /**
43759          * @event push
43760          * Fires when the iframe editor is updated with content from the textarea.
43761          * @param {Roo.HtmlEditorCore} this
43762          * @param {String} html
43763          */
43764         push: true,
43765         
43766         /**
43767          * @event editorevent
43768          * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
43769          * @param {Roo.HtmlEditorCore} this
43770          */
43771         editorevent: true
43772         
43773     });
43774     
43775     // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
43776     
43777     // defaults : white / black...
43778     this.applyBlacklists();
43779     
43780     
43781     
43782 };
43783
43784
43785 Roo.extend(Roo.HtmlEditorCore, Roo.Component,  {
43786
43787
43788      /**
43789      * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field 
43790      */
43791     
43792     owner : false,
43793     
43794      /**
43795      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
43796      *                        Roo.resizable.
43797      */
43798     resizable : false,
43799      /**
43800      * @cfg {Number} height (in pixels)
43801      */   
43802     height: 300,
43803    /**
43804      * @cfg {Number} width (in pixels)
43805      */   
43806     width: 500,
43807     
43808     /**
43809      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
43810      * 
43811      */
43812     stylesheets: false,
43813     
43814     /**
43815      * @cfg {boolean} allowComments - default false - allow comments in HTML source - by default they are stripped - if you are editing email you may need this.
43816      */
43817     allowComments: false,
43818     // id of frame..
43819     frameId: false,
43820     
43821     // private properties
43822     validationEvent : false,
43823     deferHeight: true,
43824     initialized : false,
43825     activated : false,
43826     sourceEditMode : false,
43827     onFocus : Roo.emptyFn,
43828     iframePad:3,
43829     hideMode:'offsets',
43830     
43831     clearUp: true,
43832     
43833     // blacklist + whitelisted elements..
43834     black: false,
43835     white: false,
43836      
43837     bodyCls : '',
43838
43839     /**
43840      * Protected method that will not generally be called directly. It
43841      * is called when the editor initializes the iframe with HTML contents. Override this method if you
43842      * want to change the initialization markup of the iframe (e.g. to add stylesheets).
43843      */
43844     getDocMarkup : function(){
43845         // body styles..
43846         var st = '';
43847         
43848         // inherit styels from page...?? 
43849         if (this.stylesheets === false) {
43850             
43851             Roo.get(document.head).select('style').each(function(node) {
43852                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
43853             });
43854             
43855             Roo.get(document.head).select('link').each(function(node) { 
43856                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
43857             });
43858             
43859         } else if (!this.stylesheets.length) {
43860                 // simple..
43861                 st = '<style type="text/css">' +
43862                     'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
43863                    '</style>';
43864         } else {
43865             for (var i in this.stylesheets) { 
43866                 st += '<link rel="stylesheet" href="' + this.stylesheets[i] +'" type="text/css">';
43867             }
43868             
43869         }
43870         
43871         st +=  '<style type="text/css">' +
43872             'IMG { cursor: pointer } ' +
43873         '</style>';
43874
43875         var cls = 'roo-htmleditor-body';
43876         
43877         if(this.bodyCls.length){
43878             cls += ' ' + this.bodyCls;
43879         }
43880         
43881         return '<html><head>' + st  +
43882             //<style type="text/css">' +
43883             //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
43884             //'</style>' +
43885             ' </head><body contenteditable="true" data-enable-grammerly="true" class="' +  cls + '"></body></html>';
43886     },
43887
43888     // private
43889     onRender : function(ct, position)
43890     {
43891         var _t = this;
43892         //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
43893         this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
43894         
43895         
43896         this.el.dom.style.border = '0 none';
43897         this.el.dom.setAttribute('tabIndex', -1);
43898         this.el.addClass('x-hidden hide');
43899         
43900         
43901         
43902         if(Roo.isIE){ // fix IE 1px bogus margin
43903             this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
43904         }
43905        
43906         
43907         this.frameId = Roo.id();
43908         
43909          
43910         
43911         var iframe = this.owner.wrap.createChild({
43912             tag: 'iframe',
43913             cls: 'form-control', // bootstrap..
43914             id: this.frameId,
43915             name: this.frameId,
43916             frameBorder : 'no',
43917             'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL  :  "javascript:false"
43918         }, this.el
43919         );
43920         
43921         
43922         this.iframe = iframe.dom;
43923
43924          this.assignDocWin();
43925         
43926         this.doc.designMode = 'on';
43927        
43928         this.doc.open();
43929         this.doc.write(this.getDocMarkup());
43930         this.doc.close();
43931
43932         
43933         var task = { // must defer to wait for browser to be ready
43934             run : function(){
43935                 //console.log("run task?" + this.doc.readyState);
43936                 this.assignDocWin();
43937                 if(this.doc.body || this.doc.readyState == 'complete'){
43938                     try {
43939                         this.doc.designMode="on";
43940                     } catch (e) {
43941                         return;
43942                     }
43943                     Roo.TaskMgr.stop(task);
43944                     this.initEditor.defer(10, this);
43945                 }
43946             },
43947             interval : 10,
43948             duration: 10000,
43949             scope: this
43950         };
43951         Roo.TaskMgr.start(task);
43952
43953     },
43954
43955     // private
43956     onResize : function(w, h)
43957     {
43958          Roo.log('resize: ' +w + ',' + h );
43959         //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
43960         if(!this.iframe){
43961             return;
43962         }
43963         if(typeof w == 'number'){
43964             
43965             this.iframe.style.width = w + 'px';
43966         }
43967         if(typeof h == 'number'){
43968             
43969             this.iframe.style.height = h + 'px';
43970             if(this.doc){
43971                 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
43972             }
43973         }
43974         
43975     },
43976
43977     /**
43978      * Toggles the editor between standard and source edit mode.
43979      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
43980      */
43981     toggleSourceEdit : function(sourceEditMode){
43982         
43983         this.sourceEditMode = sourceEditMode === true;
43984         
43985         if(this.sourceEditMode){
43986  
43987             Roo.get(this.iframe).addClass(['x-hidden','hide']);     //FIXME - what's the BS styles for these
43988             
43989         }else{
43990             Roo.get(this.iframe).removeClass(['x-hidden','hide']);
43991             //this.iframe.className = '';
43992             this.deferFocus();
43993         }
43994         //this.setSize(this.owner.wrap.getSize());
43995         //this.fireEvent('editmodechange', this, this.sourceEditMode);
43996     },
43997
43998     
43999   
44000
44001     /**
44002      * Protected method that will not generally be called directly. If you need/want
44003      * custom HTML cleanup, this is the method you should override.
44004      * @param {String} html The HTML to be cleaned
44005      * return {String} The cleaned HTML
44006      */
44007     cleanHtml : function(html){
44008         html = String(html);
44009         if(html.length > 5){
44010             if(Roo.isSafari){ // strip safari nonsense
44011                 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
44012             }
44013         }
44014         if(html == '&nbsp;'){
44015             html = '';
44016         }
44017         return html;
44018     },
44019
44020     /**
44021      * HTML Editor -> Textarea
44022      * Protected method that will not generally be called directly. Syncs the contents
44023      * of the editor iframe with the textarea.
44024      */
44025     syncValue : function(){
44026         if(this.initialized){
44027             var bd = (this.doc.body || this.doc.documentElement);
44028             //this.cleanUpPaste(); -- this is done else where and causes havoc..
44029             var html = bd.innerHTML;
44030             if(Roo.isSafari){
44031                 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
44032                 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
44033                 if(m && m[1]){
44034                     html = '<div style="'+m[0]+'">' + html + '</div>';
44035                 }
44036             }
44037             html = this.cleanHtml(html);
44038             // fix up the special chars.. normaly like back quotes in word...
44039             // however we do not want to do this with chinese..
44040             html = html.replace(/[\uD800-\uDBFF][\uDC00-\uDFFF]|[\u0080-\uFFFF]/g, function(match) {
44041                 
44042                 var cc = match.charCodeAt();
44043
44044                 // Get the character value, handling surrogate pairs
44045                 if (match.length == 2) {
44046                     // It's a surrogate pair, calculate the Unicode code point
44047                     var high = match.charCodeAt(0) - 0xD800;
44048                     var low  = match.charCodeAt(1) - 0xDC00;
44049                     cc = (high * 0x400) + low + 0x10000;
44050                 }  else if (
44051                     (cc >= 0x4E00 && cc < 0xA000 ) ||
44052                     (cc >= 0x3400 && cc < 0x4E00 ) ||
44053                     (cc >= 0xf900 && cc < 0xfb00 )
44054                 ) {
44055                         return match;
44056                 }  
44057          
44058                 // No, use a numeric entity. Here we brazenly (and possibly mistakenly)
44059                 return "&#" + cc + ";";
44060                 
44061                 
44062             });
44063             
44064             
44065              
44066             if(this.owner.fireEvent('beforesync', this, html) !== false){
44067                 this.el.dom.value = html;
44068                 this.owner.fireEvent('sync', this, html);
44069             }
44070         }
44071     },
44072
44073     /**
44074      * Protected method that will not generally be called directly. Pushes the value of the textarea
44075      * into the iframe editor.
44076      */
44077     pushValue : function(){
44078         if(this.initialized){
44079             var v = this.el.dom.value.trim();
44080             
44081 //            if(v.length < 1){
44082 //                v = '&#160;';
44083 //            }
44084             
44085             if(this.owner.fireEvent('beforepush', this, v) !== false){
44086                 var d = (this.doc.body || this.doc.documentElement);
44087                 d.innerHTML = v;
44088                 this.cleanUpPaste();
44089                 this.el.dom.value = d.innerHTML;
44090                 this.owner.fireEvent('push', this, v);
44091             }
44092         }
44093     },
44094
44095     // private
44096     deferFocus : function(){
44097         this.focus.defer(10, this);
44098     },
44099
44100     // doc'ed in Field
44101     focus : function(){
44102         if(this.win && !this.sourceEditMode){
44103             this.win.focus();
44104         }else{
44105             this.el.focus();
44106         }
44107     },
44108     
44109     assignDocWin: function()
44110     {
44111         var iframe = this.iframe;
44112         
44113          if(Roo.isIE){
44114             this.doc = iframe.contentWindow.document;
44115             this.win = iframe.contentWindow;
44116         } else {
44117 //            if (!Roo.get(this.frameId)) {
44118 //                return;
44119 //            }
44120 //            this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
44121 //            this.win = Roo.get(this.frameId).dom.contentWindow;
44122             
44123             if (!Roo.get(this.frameId) && !iframe.contentDocument) {
44124                 return;
44125             }
44126             
44127             this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
44128             this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
44129         }
44130     },
44131     
44132     // private
44133     initEditor : function(){
44134         //console.log("INIT EDITOR");
44135         this.assignDocWin();
44136         
44137         
44138         
44139         this.doc.designMode="on";
44140         this.doc.open();
44141         this.doc.write(this.getDocMarkup());
44142         this.doc.close();
44143         
44144         var dbody = (this.doc.body || this.doc.documentElement);
44145         //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
44146         // this copies styles from the containing element into thsi one..
44147         // not sure why we need all of this..
44148         //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
44149         
44150         //var ss = this.el.getStyles( 'background-image', 'background-repeat');
44151         //ss['background-attachment'] = 'fixed'; // w3c
44152         dbody.bgProperties = 'fixed'; // ie
44153         //Roo.DomHelper.applyStyles(dbody, ss);
44154         Roo.EventManager.on(this.doc, {
44155             //'mousedown': this.onEditorEvent,
44156             'mouseup': this.onEditorEvent,
44157             'dblclick': this.onEditorEvent,
44158             'click': this.onEditorEvent,
44159             'keyup': this.onEditorEvent,
44160             buffer:100,
44161             scope: this
44162         });
44163         if(Roo.isGecko){
44164             Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
44165         }
44166         if(Roo.isIE || Roo.isSafari || Roo.isOpera){
44167             Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
44168         }
44169         this.initialized = true;
44170
44171         this.owner.fireEvent('initialize', this);
44172         this.pushValue();
44173     },
44174
44175     // private
44176     onDestroy : function(){
44177         
44178         
44179         
44180         if(this.rendered){
44181             
44182             //for (var i =0; i < this.toolbars.length;i++) {
44183             //    // fixme - ask toolbars for heights?
44184             //    this.toolbars[i].onDestroy();
44185            // }
44186             
44187             //this.wrap.dom.innerHTML = '';
44188             //this.wrap.remove();
44189         }
44190     },
44191
44192     // private
44193     onFirstFocus : function(){
44194         
44195         this.assignDocWin();
44196         
44197         
44198         this.activated = true;
44199          
44200     
44201         if(Roo.isGecko){ // prevent silly gecko errors
44202             this.win.focus();
44203             var s = this.win.getSelection();
44204             if(!s.focusNode || s.focusNode.nodeType != 3){
44205                 var r = s.getRangeAt(0);
44206                 r.selectNodeContents((this.doc.body || this.doc.documentElement));
44207                 r.collapse(true);
44208                 this.deferFocus();
44209             }
44210             try{
44211                 this.execCmd('useCSS', true);
44212                 this.execCmd('styleWithCSS', false);
44213             }catch(e){}
44214         }
44215         this.owner.fireEvent('activate', this);
44216     },
44217
44218     // private
44219     adjustFont: function(btn){
44220         var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
44221         //if(Roo.isSafari){ // safari
44222         //    adjust *= 2;
44223        // }
44224         var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
44225         if(Roo.isSafari){ // safari
44226             var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
44227             v =  (v < 10) ? 10 : v;
44228             v =  (v > 48) ? 48 : v;
44229             v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
44230             
44231         }
44232         
44233         
44234         v = Math.max(1, v+adjust);
44235         
44236         this.execCmd('FontSize', v  );
44237     },
44238
44239     onEditorEvent : function(e)
44240     {
44241         this.owner.fireEvent('editorevent', this, e);
44242       //  this.updateToolbar();
44243         this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
44244     },
44245
44246     insertTag : function(tg)
44247     {
44248         // could be a bit smarter... -> wrap the current selected tRoo..
44249         if (tg.toLowerCase() == 'span' ||
44250             tg.toLowerCase() == 'code' ||
44251             tg.toLowerCase() == 'sup' ||
44252             tg.toLowerCase() == 'sub' 
44253             ) {
44254             
44255             range = this.createRange(this.getSelection());
44256             var wrappingNode = this.doc.createElement(tg.toLowerCase());
44257             wrappingNode.appendChild(range.extractContents());
44258             range.insertNode(wrappingNode);
44259
44260             return;
44261             
44262             
44263             
44264         }
44265         this.execCmd("formatblock",   tg);
44266         
44267     },
44268     
44269     insertText : function(txt)
44270     {
44271         
44272         
44273         var range = this.createRange();
44274         range.deleteContents();
44275                //alert(Sender.getAttribute('label'));
44276                
44277         range.insertNode(this.doc.createTextNode(txt));
44278     } ,
44279     
44280      
44281
44282     /**
44283      * Executes a Midas editor command on the editor document and performs necessary focus and
44284      * toolbar updates. <b>This should only be called after the editor is initialized.</b>
44285      * @param {String} cmd The Midas command
44286      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
44287      */
44288     relayCmd : function(cmd, value){
44289         this.win.focus();
44290         this.execCmd(cmd, value);
44291         this.owner.fireEvent('editorevent', this);
44292         //this.updateToolbar();
44293         this.owner.deferFocus();
44294     },
44295
44296     /**
44297      * Executes a Midas editor command directly on the editor document.
44298      * For visual commands, you should use {@link #relayCmd} instead.
44299      * <b>This should only be called after the editor is initialized.</b>
44300      * @param {String} cmd The Midas command
44301      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
44302      */
44303     execCmd : function(cmd, value){
44304         this.doc.execCommand(cmd, false, value === undefined ? null : value);
44305         this.syncValue();
44306     },
44307  
44308  
44309    
44310     /**
44311      * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
44312      * to insert tRoo.
44313      * @param {String} text | dom node.. 
44314      */
44315     insertAtCursor : function(text)
44316     {
44317         
44318         if(!this.activated){
44319             return;
44320         }
44321         /*
44322         if(Roo.isIE){
44323             this.win.focus();
44324             var r = this.doc.selection.createRange();
44325             if(r){
44326                 r.collapse(true);
44327                 r.pasteHTML(text);
44328                 this.syncValue();
44329                 this.deferFocus();
44330             
44331             }
44332             return;
44333         }
44334         */
44335         if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
44336             this.win.focus();
44337             
44338             
44339             // from jquery ui (MIT licenced)
44340             var range, node;
44341             var win = this.win;
44342             
44343             if (win.getSelection && win.getSelection().getRangeAt) {
44344                 range = win.getSelection().getRangeAt(0);
44345                 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
44346                 range.insertNode(node);
44347             } else if (win.document.selection && win.document.selection.createRange) {
44348                 // no firefox support
44349                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
44350                 win.document.selection.createRange().pasteHTML(txt);
44351             } else {
44352                 // no firefox support
44353                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
44354                 this.execCmd('InsertHTML', txt);
44355             } 
44356             
44357             this.syncValue();
44358             
44359             this.deferFocus();
44360         }
44361     },
44362  // private
44363     mozKeyPress : function(e){
44364         if(e.ctrlKey){
44365             var c = e.getCharCode(), cmd;
44366           
44367             if(c > 0){
44368                 c = String.fromCharCode(c).toLowerCase();
44369                 switch(c){
44370                     case 'b':
44371                         cmd = 'bold';
44372                         break;
44373                     case 'i':
44374                         cmd = 'italic';
44375                         break;
44376                     
44377                     case 'u':
44378                         cmd = 'underline';
44379                         break;
44380                     
44381                     case 'v':
44382                         this.cleanUpPaste.defer(100, this);
44383                         return;
44384                         
44385                 }
44386                 if(cmd){
44387                     this.win.focus();
44388                     this.execCmd(cmd);
44389                     this.deferFocus();
44390                     e.preventDefault();
44391                 }
44392                 
44393             }
44394         }
44395     },
44396
44397     // private
44398     fixKeys : function(){ // load time branching for fastest keydown performance
44399         if(Roo.isIE){
44400             return function(e){
44401                 var k = e.getKey(), r;
44402                 if(k == e.TAB){
44403                     e.stopEvent();
44404                     r = this.doc.selection.createRange();
44405                     if(r){
44406                         r.collapse(true);
44407                         r.pasteHTML('&#160;&#160;&#160;&#160;');
44408                         this.deferFocus();
44409                     }
44410                     return;
44411                 }
44412                 
44413                 if(k == e.ENTER){
44414                     r = this.doc.selection.createRange();
44415                     if(r){
44416                         var target = r.parentElement();
44417                         if(!target || target.tagName.toLowerCase() != 'li'){
44418                             e.stopEvent();
44419                             r.pasteHTML('<br />');
44420                             r.collapse(false);
44421                             r.select();
44422                         }
44423                     }
44424                 }
44425                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
44426                     this.cleanUpPaste.defer(100, this);
44427                     return;
44428                 }
44429                 
44430                 
44431             };
44432         }else if(Roo.isOpera){
44433             return function(e){
44434                 var k = e.getKey();
44435                 if(k == e.TAB){
44436                     e.stopEvent();
44437                     this.win.focus();
44438                     this.execCmd('InsertHTML','&#160;&#160;&#160;&#160;');
44439                     this.deferFocus();
44440                 }
44441                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
44442                     this.cleanUpPaste.defer(100, this);
44443                     return;
44444                 }
44445                 
44446             };
44447         }else if(Roo.isSafari){
44448             return function(e){
44449                 var k = e.getKey();
44450                 
44451                 if(k == e.TAB){
44452                     e.stopEvent();
44453                     this.execCmd('InsertText','\t');
44454                     this.deferFocus();
44455                     return;
44456                 }
44457                if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
44458                     this.cleanUpPaste.defer(100, this);
44459                     return;
44460                 }
44461                 
44462              };
44463         }
44464     }(),
44465     
44466     getAllAncestors: function()
44467     {
44468         var p = this.getSelectedNode();
44469         var a = [];
44470         if (!p) {
44471             a.push(p); // push blank onto stack..
44472             p = this.getParentElement();
44473         }
44474         
44475         
44476         while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
44477             a.push(p);
44478             p = p.parentNode;
44479         }
44480         a.push(this.doc.body);
44481         return a;
44482     },
44483     lastSel : false,
44484     lastSelNode : false,
44485     
44486     
44487     getSelection : function() 
44488     {
44489         this.assignDocWin();
44490         return Roo.isIE ? this.doc.selection : this.win.getSelection();
44491     },
44492     
44493     getSelectedNode: function() 
44494     {
44495         // this may only work on Gecko!!!
44496         
44497         // should we cache this!!!!
44498         
44499         
44500         
44501          
44502         var range = this.createRange(this.getSelection()).cloneRange();
44503         
44504         if (Roo.isIE) {
44505             var parent = range.parentElement();
44506             while (true) {
44507                 var testRange = range.duplicate();
44508                 testRange.moveToElementText(parent);
44509                 if (testRange.inRange(range)) {
44510                     break;
44511                 }
44512                 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
44513                     break;
44514                 }
44515                 parent = parent.parentElement;
44516             }
44517             return parent;
44518         }
44519         
44520         // is ancestor a text element.
44521         var ac =  range.commonAncestorContainer;
44522         if (ac.nodeType == 3) {
44523             ac = ac.parentNode;
44524         }
44525         
44526         var ar = ac.childNodes;
44527          
44528         var nodes = [];
44529         var other_nodes = [];
44530         var has_other_nodes = false;
44531         for (var i=0;i<ar.length;i++) {
44532             if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ? 
44533                 continue;
44534             }
44535             // fullly contained node.
44536             
44537             if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
44538                 nodes.push(ar[i]);
44539                 continue;
44540             }
44541             
44542             // probably selected..
44543             if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
44544                 other_nodes.push(ar[i]);
44545                 continue;
44546             }
44547             // outer..
44548             if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0))  {
44549                 continue;
44550             }
44551             
44552             
44553             has_other_nodes = true;
44554         }
44555         if (!nodes.length && other_nodes.length) {
44556             nodes= other_nodes;
44557         }
44558         if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
44559             return false;
44560         }
44561         
44562         return nodes[0];
44563     },
44564     createRange: function(sel)
44565     {
44566         // this has strange effects when using with 
44567         // top toolbar - not sure if it's a great idea.
44568         //this.editor.contentWindow.focus();
44569         if (typeof sel != "undefined") {
44570             try {
44571                 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
44572             } catch(e) {
44573                 return this.doc.createRange();
44574             }
44575         } else {
44576             return this.doc.createRange();
44577         }
44578     },
44579     getParentElement: function()
44580     {
44581         
44582         this.assignDocWin();
44583         var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
44584         
44585         var range = this.createRange(sel);
44586          
44587         try {
44588             var p = range.commonAncestorContainer;
44589             while (p.nodeType == 3) { // text node
44590                 p = p.parentNode;
44591             }
44592             return p;
44593         } catch (e) {
44594             return null;
44595         }
44596     
44597     },
44598     /***
44599      *
44600      * Range intersection.. the hard stuff...
44601      *  '-1' = before
44602      *  '0' = hits..
44603      *  '1' = after.
44604      *         [ -- selected range --- ]
44605      *   [fail]                        [fail]
44606      *
44607      *    basically..
44608      *      if end is before start or  hits it. fail.
44609      *      if start is after end or hits it fail.
44610      *
44611      *   if either hits (but other is outside. - then it's not 
44612      *   
44613      *    
44614      **/
44615     
44616     
44617     // @see http://www.thismuchiknow.co.uk/?p=64.
44618     rangeIntersectsNode : function(range, node)
44619     {
44620         var nodeRange = node.ownerDocument.createRange();
44621         try {
44622             nodeRange.selectNode(node);
44623         } catch (e) {
44624             nodeRange.selectNodeContents(node);
44625         }
44626     
44627         var rangeStartRange = range.cloneRange();
44628         rangeStartRange.collapse(true);
44629     
44630         var rangeEndRange = range.cloneRange();
44631         rangeEndRange.collapse(false);
44632     
44633         var nodeStartRange = nodeRange.cloneRange();
44634         nodeStartRange.collapse(true);
44635     
44636         var nodeEndRange = nodeRange.cloneRange();
44637         nodeEndRange.collapse(false);
44638     
44639         return rangeStartRange.compareBoundaryPoints(
44640                  Range.START_TO_START, nodeEndRange) == -1 &&
44641                rangeEndRange.compareBoundaryPoints(
44642                  Range.START_TO_START, nodeStartRange) == 1;
44643         
44644          
44645     },
44646     rangeCompareNode : function(range, node)
44647     {
44648         var nodeRange = node.ownerDocument.createRange();
44649         try {
44650             nodeRange.selectNode(node);
44651         } catch (e) {
44652             nodeRange.selectNodeContents(node);
44653         }
44654         
44655         
44656         range.collapse(true);
44657     
44658         nodeRange.collapse(true);
44659      
44660         var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
44661         var ee = range.compareBoundaryPoints(  Range.END_TO_END, nodeRange);
44662          
44663         //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
44664         
44665         var nodeIsBefore   =  ss == 1;
44666         var nodeIsAfter    = ee == -1;
44667         
44668         if (nodeIsBefore && nodeIsAfter) {
44669             return 0; // outer
44670         }
44671         if (!nodeIsBefore && nodeIsAfter) {
44672             return 1; //right trailed.
44673         }
44674         
44675         if (nodeIsBefore && !nodeIsAfter) {
44676             return 2;  // left trailed.
44677         }
44678         // fully contined.
44679         return 3;
44680     },
44681
44682     // private? - in a new class?
44683     cleanUpPaste :  function()
44684     {
44685         // cleans up the whole document..
44686         Roo.log('cleanuppaste');
44687         
44688         this.cleanUpChildren(this.doc.body);
44689         var clean = this.cleanWordChars(this.doc.body.innerHTML);
44690         if (clean != this.doc.body.innerHTML) {
44691             this.doc.body.innerHTML = clean;
44692         }
44693         
44694     },
44695     
44696     cleanWordChars : function(input) {// change the chars to hex code
44697         var he = Roo.HtmlEditorCore;
44698         
44699         var output = input;
44700         Roo.each(he.swapCodes, function(sw) { 
44701             var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
44702             
44703             output = output.replace(swapper, sw[1]);
44704         });
44705         
44706         return output;
44707     },
44708     
44709     
44710     cleanUpChildren : function (n)
44711     {
44712         if (!n.childNodes.length) {
44713             return;
44714         }
44715         for (var i = n.childNodes.length-1; i > -1 ; i--) {
44716            this.cleanUpChild(n.childNodes[i]);
44717         }
44718     },
44719     
44720     
44721         
44722     
44723     cleanUpChild : function (node)
44724     {
44725         var ed = this;
44726         //console.log(node);
44727         if (node.nodeName == "#text") {
44728             // clean up silly Windows -- stuff?
44729             return; 
44730         }
44731         if (node.nodeName == "#comment") {
44732             if (!this.allowComments) {
44733                 node.parentNode.removeChild(node);
44734             }
44735             // clean up silly Windows -- stuff?
44736             return; 
44737         }
44738         var lcname = node.tagName.toLowerCase();
44739         // we ignore whitelists... ?? = not really the way to go, but we probably have not got a full
44740         // whitelist of tags..
44741         
44742         if (this.black.indexOf(lcname) > -1 && this.clearUp ) {
44743             // remove node.
44744             node.parentNode.removeChild(node);
44745             return;
44746             
44747         }
44748         
44749         var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
44750         
44751         // spans with no attributes - just remove them..
44752         if ((!node.attributes || !node.attributes.length) && lcname == 'span') { 
44753             remove_keep_children = true;
44754         }
44755         
44756         // remove <a name=....> as rendering on yahoo mailer is borked with this.
44757         // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
44758         
44759         //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
44760         //    remove_keep_children = true;
44761         //}
44762         
44763         if (remove_keep_children) {
44764             this.cleanUpChildren(node);
44765             // inserts everything just before this node...
44766             while (node.childNodes.length) {
44767                 var cn = node.childNodes[0];
44768                 node.removeChild(cn);
44769                 node.parentNode.insertBefore(cn, node);
44770             }
44771             node.parentNode.removeChild(node);
44772             return;
44773         }
44774         
44775         if (!node.attributes || !node.attributes.length) {
44776             
44777           
44778             
44779             
44780             this.cleanUpChildren(node);
44781             return;
44782         }
44783         
44784         function cleanAttr(n,v)
44785         {
44786             
44787             if (v.match(/^\./) || v.match(/^\//)) {
44788                 return;
44789             }
44790             if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/) || v.match(/^ftp:/)) {
44791                 return;
44792             }
44793             if (v.match(/^#/)) {
44794                 return;
44795             }
44796             if (v.match(/^\{/)) { // allow template editing.
44797                 return;
44798             }
44799 //            Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
44800             node.removeAttribute(n);
44801             
44802         }
44803         
44804         var cwhite = this.cwhite;
44805         var cblack = this.cblack;
44806             
44807         function cleanStyle(n,v)
44808         {
44809             if (v.match(/expression/)) { //XSS?? should we even bother..
44810                 node.removeAttribute(n);
44811                 return;
44812             }
44813             
44814             var parts = v.split(/;/);
44815             var clean = [];
44816             
44817             Roo.each(parts, function(p) {
44818                 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
44819                 if (!p.length) {
44820                     return true;
44821                 }
44822                 var l = p.split(':').shift().replace(/\s+/g,'');
44823                 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
44824                 
44825                 if ( cwhite.length && cblack.indexOf(l) > -1) {
44826 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
44827                     //node.removeAttribute(n);
44828                     return true;
44829                 }
44830                 //Roo.log()
44831                 // only allow 'c whitelisted system attributes'
44832                 if ( cwhite.length &&  cwhite.indexOf(l) < 0) {
44833 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
44834                     //node.removeAttribute(n);
44835                     return true;
44836                 }
44837                 
44838                 
44839                  
44840                 
44841                 clean.push(p);
44842                 return true;
44843             });
44844             if (clean.length) { 
44845                 node.setAttribute(n, clean.join(';'));
44846             } else {
44847                 node.removeAttribute(n);
44848             }
44849             
44850         }
44851         
44852         
44853         for (var i = node.attributes.length-1; i > -1 ; i--) {
44854             var a = node.attributes[i];
44855             //console.log(a);
44856             
44857             if (a.name.toLowerCase().substr(0,2)=='on')  {
44858                 node.removeAttribute(a.name);
44859                 continue;
44860             }
44861             if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
44862                 node.removeAttribute(a.name);
44863                 continue;
44864             }
44865             if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
44866                 cleanAttr(a.name,a.value); // fixme..
44867                 continue;
44868             }
44869             if (a.name == 'style') {
44870                 cleanStyle(a.name,a.value);
44871                 continue;
44872             }
44873             /// clean up MS crap..
44874             // tecnically this should be a list of valid class'es..
44875             
44876             
44877             if (a.name == 'class') {
44878                 if (a.value.match(/^Mso/)) {
44879                     node.removeAttribute('class');
44880                 }
44881                 
44882                 if (a.value.match(/^body$/)) {
44883                     node.removeAttribute('class');
44884                 }
44885                 continue;
44886             }
44887             
44888             // style cleanup!?
44889             // class cleanup?
44890             
44891         }
44892         
44893         
44894         this.cleanUpChildren(node);
44895         
44896         
44897     },
44898     
44899     /**
44900      * Clean up MS wordisms...
44901      */
44902     cleanWord : function(node)
44903     {
44904         if (!node) {
44905             this.cleanWord(this.doc.body);
44906             return;
44907         }
44908         
44909         if(
44910                 node.nodeName == 'SPAN' &&
44911                 !node.hasAttributes() &&
44912                 node.childNodes.length == 1 &&
44913                 node.firstChild.nodeName == "#text"  
44914         ) {
44915             var textNode = node.firstChild;
44916             node.removeChild(textNode);
44917             if (node.getAttribute('lang') != 'zh-CN') {   // do not space pad on chinese characters..
44918                 node.parentNode.insertBefore(node.ownerDocument.createTextNode(" "), node);
44919             }
44920             node.parentNode.insertBefore(textNode, node);
44921             if (node.getAttribute('lang') != 'zh-CN') {   // do not space pad on chinese characters..
44922                 node.parentNode.insertBefore(node.ownerDocument.createTextNode(" ") , node);
44923             }
44924             node.parentNode.removeChild(node);
44925         }
44926         
44927         if (node.nodeName == "#text") {
44928             // clean up silly Windows -- stuff?
44929             return; 
44930         }
44931         if (node.nodeName == "#comment") {
44932             node.parentNode.removeChild(node);
44933             // clean up silly Windows -- stuff?
44934             return; 
44935         }
44936         
44937         if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
44938             node.parentNode.removeChild(node);
44939             return;
44940         }
44941         //Roo.log(node.tagName);
44942         // remove - but keep children..
44943         if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|v:|font)/)) {
44944             //Roo.log('-- removed');
44945             while (node.childNodes.length) {
44946                 var cn = node.childNodes[0];
44947                 node.removeChild(cn);
44948                 node.parentNode.insertBefore(cn, node);
44949                 // move node to parent - and clean it..
44950                 this.cleanWord(cn);
44951             }
44952             node.parentNode.removeChild(node);
44953             /// no need to iterate chidlren = it's got none..
44954             //this.iterateChildren(node, this.cleanWord);
44955             return;
44956         }
44957         // clean styles
44958         if (node.className.length) {
44959             
44960             var cn = node.className.split(/\W+/);
44961             var cna = [];
44962             Roo.each(cn, function(cls) {
44963                 if (cls.match(/Mso[a-zA-Z]+/)) {
44964                     return;
44965                 }
44966                 cna.push(cls);
44967             });
44968             node.className = cna.length ? cna.join(' ') : '';
44969             if (!cna.length) {
44970                 node.removeAttribute("class");
44971             }
44972         }
44973         
44974         if (node.hasAttribute("lang")) {
44975             node.removeAttribute("lang");
44976         }
44977         
44978         if (node.hasAttribute("style")) {
44979             
44980             var styles = node.getAttribute("style").split(";");
44981             var nstyle = [];
44982             Roo.each(styles, function(s) {
44983                 if (!s.match(/:/)) {
44984                     return;
44985                 }
44986                 var kv = s.split(":");
44987                 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
44988                     return;
44989                 }
44990                 // what ever is left... we allow.
44991                 nstyle.push(s);
44992             });
44993             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
44994             if (!nstyle.length) {
44995                 node.removeAttribute('style');
44996             }
44997         }
44998         this.iterateChildren(node, this.cleanWord);
44999         
45000         
45001         
45002     },
45003     /**
45004      * iterateChildren of a Node, calling fn each time, using this as the scole..
45005      * @param {DomNode} node node to iterate children of.
45006      * @param {Function} fn method of this class to call on each item.
45007      */
45008     iterateChildren : function(node, fn)
45009     {
45010         if (!node.childNodes.length) {
45011                 return;
45012         }
45013         for (var i = node.childNodes.length-1; i > -1 ; i--) {
45014            fn.call(this, node.childNodes[i])
45015         }
45016     },
45017     
45018     
45019     /**
45020      * cleanTableWidths.
45021      *
45022      * Quite often pasting from word etc.. results in tables with column and widths.
45023      * This does not work well on fluid HTML layouts - like emails. - so this code should hunt an destroy them..
45024      *
45025      */
45026     cleanTableWidths : function(node)
45027     {
45028          
45029          
45030         if (!node) {
45031             this.cleanTableWidths(this.doc.body);
45032             return;
45033         }
45034         
45035         // ignore list...
45036         if (node.nodeName == "#text" || node.nodeName == "#comment") {
45037             return; 
45038         }
45039         Roo.log(node.tagName);
45040         if (!node.tagName.toLowerCase().match(/^(table|td|tr)$/)) {
45041             this.iterateChildren(node, this.cleanTableWidths);
45042             return;
45043         }
45044         if (node.hasAttribute('width')) {
45045             node.removeAttribute('width');
45046         }
45047         
45048          
45049         if (node.hasAttribute("style")) {
45050             // pretty basic...
45051             
45052             var styles = node.getAttribute("style").split(";");
45053             var nstyle = [];
45054             Roo.each(styles, function(s) {
45055                 if (!s.match(/:/)) {
45056                     return;
45057                 }
45058                 var kv = s.split(":");
45059                 if (kv[0].match(/^\s*(width|min-width)\s*$/)) {
45060                     return;
45061                 }
45062                 // what ever is left... we allow.
45063                 nstyle.push(s);
45064             });
45065             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
45066             if (!nstyle.length) {
45067                 node.removeAttribute('style');
45068             }
45069         }
45070         
45071         this.iterateChildren(node, this.cleanTableWidths);
45072         
45073         
45074     },
45075     
45076     
45077     
45078     
45079     domToHTML : function(currentElement, depth, nopadtext) {
45080         
45081         depth = depth || 0;
45082         nopadtext = nopadtext || false;
45083     
45084         if (!currentElement) {
45085             return this.domToHTML(this.doc.body);
45086         }
45087         
45088         //Roo.log(currentElement);
45089         var j;
45090         var allText = false;
45091         var nodeName = currentElement.nodeName;
45092         var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
45093         
45094         if  (nodeName == '#text') {
45095             
45096             return nopadtext ? currentElement.nodeValue : currentElement.nodeValue.trim();
45097         }
45098         
45099         
45100         var ret = '';
45101         if (nodeName != 'BODY') {
45102              
45103             var i = 0;
45104             // Prints the node tagName, such as <A>, <IMG>, etc
45105             if (tagName) {
45106                 var attr = [];
45107                 for(i = 0; i < currentElement.attributes.length;i++) {
45108                     // quoting?
45109                     var aname = currentElement.attributes.item(i).name;
45110                     if (!currentElement.attributes.item(i).value.length) {
45111                         continue;
45112                     }
45113                     attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
45114                 }
45115                 
45116                 ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
45117             } 
45118             else {
45119                 
45120                 // eack
45121             }
45122         } else {
45123             tagName = false;
45124         }
45125         if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
45126             return ret;
45127         }
45128         if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
45129             nopadtext = true;
45130         }
45131         
45132         
45133         // Traverse the tree
45134         i = 0;
45135         var currentElementChild = currentElement.childNodes.item(i);
45136         var allText = true;
45137         var innerHTML  = '';
45138         lastnode = '';
45139         while (currentElementChild) {
45140             // Formatting code (indent the tree so it looks nice on the screen)
45141             var nopad = nopadtext;
45142             if (lastnode == 'SPAN') {
45143                 nopad  = true;
45144             }
45145             // text
45146             if  (currentElementChild.nodeName == '#text') {
45147                 var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
45148                 toadd = nopadtext ? toadd : toadd.trim();
45149                 if (!nopad && toadd.length > 80) {
45150                     innerHTML  += "\n" + (new Array( depth + 1 )).join( "  "  );
45151                 }
45152                 innerHTML  += toadd;
45153                 
45154                 i++;
45155                 currentElementChild = currentElement.childNodes.item(i);
45156                 lastNode = '';
45157                 continue;
45158             }
45159             allText = false;
45160             
45161             innerHTML  += nopad ? '' : "\n" + (new Array( depth + 1 )).join( "  "  );
45162                 
45163             // Recursively traverse the tree structure of the child node
45164             innerHTML   += this.domToHTML(currentElementChild, depth+1, nopadtext);
45165             lastnode = currentElementChild.nodeName;
45166             i++;
45167             currentElementChild=currentElement.childNodes.item(i);
45168         }
45169         
45170         ret += innerHTML;
45171         
45172         if (!allText) {
45173                 // The remaining code is mostly for formatting the tree
45174             ret+= nopadtext ? '' : "\n" + (new Array( depth  )).join( "  "  );
45175         }
45176         
45177         
45178         if (tagName) {
45179             ret+= "</"+tagName+">";
45180         }
45181         return ret;
45182         
45183     },
45184         
45185     applyBlacklists : function()
45186     {
45187         var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white  : [];
45188         var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black :  [];
45189         
45190         this.white = [];
45191         this.black = [];
45192         Roo.each(Roo.HtmlEditorCore.white, function(tag) {
45193             if (b.indexOf(tag) > -1) {
45194                 return;
45195             }
45196             this.white.push(tag);
45197             
45198         }, this);
45199         
45200         Roo.each(w, function(tag) {
45201             if (b.indexOf(tag) > -1) {
45202                 return;
45203             }
45204             if (this.white.indexOf(tag) > -1) {
45205                 return;
45206             }
45207             this.white.push(tag);
45208             
45209         }, this);
45210         
45211         
45212         Roo.each(Roo.HtmlEditorCore.black, function(tag) {
45213             if (w.indexOf(tag) > -1) {
45214                 return;
45215             }
45216             this.black.push(tag);
45217             
45218         }, this);
45219         
45220         Roo.each(b, function(tag) {
45221             if (w.indexOf(tag) > -1) {
45222                 return;
45223             }
45224             if (this.black.indexOf(tag) > -1) {
45225                 return;
45226             }
45227             this.black.push(tag);
45228             
45229         }, this);
45230         
45231         
45232         w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite  : [];
45233         b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack :  [];
45234         
45235         this.cwhite = [];
45236         this.cblack = [];
45237         Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
45238             if (b.indexOf(tag) > -1) {
45239                 return;
45240             }
45241             this.cwhite.push(tag);
45242             
45243         }, this);
45244         
45245         Roo.each(w, function(tag) {
45246             if (b.indexOf(tag) > -1) {
45247                 return;
45248             }
45249             if (this.cwhite.indexOf(tag) > -1) {
45250                 return;
45251             }
45252             this.cwhite.push(tag);
45253             
45254         }, this);
45255         
45256         
45257         Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
45258             if (w.indexOf(tag) > -1) {
45259                 return;
45260             }
45261             this.cblack.push(tag);
45262             
45263         }, this);
45264         
45265         Roo.each(b, function(tag) {
45266             if (w.indexOf(tag) > -1) {
45267                 return;
45268             }
45269             if (this.cblack.indexOf(tag) > -1) {
45270                 return;
45271             }
45272             this.cblack.push(tag);
45273             
45274         }, this);
45275     },
45276     
45277     setStylesheets : function(stylesheets)
45278     {
45279         if(typeof(stylesheets) == 'string'){
45280             Roo.get(this.iframe.contentDocument.head).createChild({
45281                 tag : 'link',
45282                 rel : 'stylesheet',
45283                 type : 'text/css',
45284                 href : stylesheets
45285             });
45286             
45287             return;
45288         }
45289         var _this = this;
45290      
45291         Roo.each(stylesheets, function(s) {
45292             if(!s.length){
45293                 return;
45294             }
45295             
45296             Roo.get(_this.iframe.contentDocument.head).createChild({
45297                 tag : 'link',
45298                 rel : 'stylesheet',
45299                 type : 'text/css',
45300                 href : s
45301             });
45302         });
45303
45304         
45305     },
45306     
45307     removeStylesheets : function()
45308     {
45309         var _this = this;
45310         
45311         Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
45312             s.remove();
45313         });
45314     },
45315     
45316     setStyle : function(style)
45317     {
45318         Roo.get(this.iframe.contentDocument.head).createChild({
45319             tag : 'style',
45320             type : 'text/css',
45321             html : style
45322         });
45323
45324         return;
45325     }
45326     
45327     // hide stuff that is not compatible
45328     /**
45329      * @event blur
45330      * @hide
45331      */
45332     /**
45333      * @event change
45334      * @hide
45335      */
45336     /**
45337      * @event focus
45338      * @hide
45339      */
45340     /**
45341      * @event specialkey
45342      * @hide
45343      */
45344     /**
45345      * @cfg {String} fieldClass @hide
45346      */
45347     /**
45348      * @cfg {String} focusClass @hide
45349      */
45350     /**
45351      * @cfg {String} autoCreate @hide
45352      */
45353     /**
45354      * @cfg {String} inputType @hide
45355      */
45356     /**
45357      * @cfg {String} invalidClass @hide
45358      */
45359     /**
45360      * @cfg {String} invalidText @hide
45361      */
45362     /**
45363      * @cfg {String} msgFx @hide
45364      */
45365     /**
45366      * @cfg {String} validateOnBlur @hide
45367      */
45368 });
45369
45370 Roo.HtmlEditorCore.white = [
45371         'area', 'br', 'img', 'input', 'hr', 'wbr',
45372         
45373        'address', 'blockquote', 'center', 'dd',      'dir',       'div', 
45374        'dl',      'dt',         'h1',     'h2',      'h3',        'h4', 
45375        'h5',      'h6',         'hr',     'isindex', 'listing',   'marquee', 
45376        'menu',    'multicol',   'ol',     'p',       'plaintext', 'pre', 
45377        'table',   'ul',         'xmp', 
45378        
45379        'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th', 
45380       'thead',   'tr', 
45381      
45382       'dir', 'menu', 'ol', 'ul', 'dl',
45383        
45384       'embed',  'object'
45385 ];
45386
45387
45388 Roo.HtmlEditorCore.black = [
45389     //    'embed',  'object', // enable - backend responsiblity to clean thiese
45390         'applet', // 
45391         'base',   'basefont', 'bgsound', 'blink',  'body', 
45392         'frame',  'frameset', 'head',    'html',   'ilayer', 
45393         'iframe', 'layer',  'link',     'meta',    'object',   
45394         'script', 'style' ,'title',  'xml' // clean later..
45395 ];
45396 Roo.HtmlEditorCore.clean = [
45397     'script', 'style', 'title', 'xml'
45398 ];
45399 Roo.HtmlEditorCore.remove = [
45400     'font'
45401 ];
45402 // attributes..
45403
45404 Roo.HtmlEditorCore.ablack = [
45405     'on'
45406 ];
45407     
45408 Roo.HtmlEditorCore.aclean = [ 
45409     'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc' 
45410 ];
45411
45412 // protocols..
45413 Roo.HtmlEditorCore.pwhite= [
45414         'http',  'https',  'mailto'
45415 ];
45416
45417 // white listed style attributes.
45418 Roo.HtmlEditorCore.cwhite= [
45419       //  'text-align', /// default is to allow most things..
45420       
45421          
45422 //        'font-size'//??
45423 ];
45424
45425 // black listed style attributes.
45426 Roo.HtmlEditorCore.cblack= [
45427       //  'font-size' -- this can be set by the project 
45428 ];
45429
45430
45431 Roo.HtmlEditorCore.swapCodes   =[ 
45432     [    8211, "&#8211;" ], 
45433     [    8212, "&#8212;" ], 
45434     [    8216,  "'" ],  
45435     [    8217, "'" ],  
45436     [    8220, '"' ],  
45437     [    8221, '"' ],  
45438     [    8226, "*" ],  
45439     [    8230, "..." ]
45440 ]; 
45441
45442     //<script type="text/javascript">
45443
45444 /*
45445  * Ext JS Library 1.1.1
45446  * Copyright(c) 2006-2007, Ext JS, LLC.
45447  * Licence LGPL
45448  * 
45449  */
45450  
45451  
45452 Roo.form.HtmlEditor = function(config){
45453     
45454     
45455     
45456     Roo.form.HtmlEditor.superclass.constructor.call(this, config);
45457     
45458     if (!this.toolbars) {
45459         this.toolbars = [];
45460     }
45461     this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
45462     
45463     
45464 };
45465
45466 /**
45467  * @class Roo.form.HtmlEditor
45468  * @extends Roo.form.Field
45469  * Provides a lightweight HTML Editor component.
45470  *
45471  * This has been tested on Fireforx / Chrome.. IE may not be so great..
45472  * 
45473  * <br><br><b>Note: The focus/blur and validation marking functionality inherited from Ext.form.Field is NOT
45474  * supported by this editor.</b><br/><br/>
45475  * An Editor is a sensitive component that can't be used in all spots standard fields can be used. Putting an Editor within
45476  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
45477  */
45478 Roo.extend(Roo.form.HtmlEditor, Roo.form.Field, {
45479     /**
45480      * @cfg {Boolean} clearUp
45481      */
45482     clearUp : true,
45483       /**
45484      * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
45485      */
45486     toolbars : false,
45487    
45488      /**
45489      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
45490      *                        Roo.resizable.
45491      */
45492     resizable : false,
45493      /**
45494      * @cfg {Number} height (in pixels)
45495      */   
45496     height: 300,
45497    /**
45498      * @cfg {Number} width (in pixels)
45499      */   
45500     width: 500,
45501     
45502     /**
45503      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
45504      * 
45505      */
45506     stylesheets: false,
45507     
45508     
45509      /**
45510      * @cfg {Array} blacklist of css styles style attributes (blacklist overrides whitelist)
45511      * 
45512      */
45513     cblack: false,
45514     /**
45515      * @cfg {Array} whitelist of css styles style attributes (blacklist overrides whitelist)
45516      * 
45517      */
45518     cwhite: false,
45519     
45520      /**
45521      * @cfg {Array} blacklist of html tags - in addition to standard blacklist.
45522      * 
45523      */
45524     black: false,
45525     /**
45526      * @cfg {Array} whitelist of html tags - in addition to statndard whitelist
45527      * 
45528      */
45529     white: false,
45530     /**
45531      * @cfg {boolean} allowComments - default false - allow comments in HTML source - by default they are stripped - if you are editing email you may need this.
45532      */
45533     allowComments: false,
45534     
45535     // id of frame..
45536     frameId: false,
45537     
45538     // private properties
45539     validationEvent : false,
45540     deferHeight: true,
45541     initialized : false,
45542     activated : false,
45543     
45544     onFocus : Roo.emptyFn,
45545     iframePad:3,
45546     hideMode:'offsets',
45547     
45548     actionMode : 'container', // defaults to hiding it...
45549     
45550     defaultAutoCreate : { // modified by initCompnoent..
45551         tag: "textarea",
45552         style:"width:500px;height:300px;",
45553         autocomplete: "new-password"
45554     },
45555
45556     // private
45557     initComponent : function(){
45558         this.addEvents({
45559             /**
45560              * @event initialize
45561              * Fires when the editor is fully initialized (including the iframe)
45562              * @param {HtmlEditor} this
45563              */
45564             initialize: true,
45565             /**
45566              * @event activate
45567              * Fires when the editor is first receives the focus. Any insertion must wait
45568              * until after this event.
45569              * @param {HtmlEditor} this
45570              */
45571             activate: true,
45572              /**
45573              * @event beforesync
45574              * Fires before the textarea is updated with content from the editor iframe. Return false
45575              * to cancel the sync.
45576              * @param {HtmlEditor} this
45577              * @param {String} html
45578              */
45579             beforesync: true,
45580              /**
45581              * @event beforepush
45582              * Fires before the iframe editor is updated with content from the textarea. Return false
45583              * to cancel the push.
45584              * @param {HtmlEditor} this
45585              * @param {String} html
45586              */
45587             beforepush: true,
45588              /**
45589              * @event sync
45590              * Fires when the textarea is updated with content from the editor iframe.
45591              * @param {HtmlEditor} this
45592              * @param {String} html
45593              */
45594             sync: true,
45595              /**
45596              * @event push
45597              * Fires when the iframe editor is updated with content from the textarea.
45598              * @param {HtmlEditor} this
45599              * @param {String} html
45600              */
45601             push: true,
45602              /**
45603              * @event editmodechange
45604              * Fires when the editor switches edit modes
45605              * @param {HtmlEditor} this
45606              * @param {Boolean} sourceEdit True if source edit, false if standard editing.
45607              */
45608             editmodechange: true,
45609             /**
45610              * @event editorevent
45611              * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
45612              * @param {HtmlEditor} this
45613              */
45614             editorevent: true,
45615             /**
45616              * @event firstfocus
45617              * Fires when on first focus - needed by toolbars..
45618              * @param {HtmlEditor} this
45619              */
45620             firstfocus: true,
45621             /**
45622              * @event autosave
45623              * Auto save the htmlEditor value as a file into Events
45624              * @param {HtmlEditor} this
45625              */
45626             autosave: true,
45627             /**
45628              * @event savedpreview
45629              * preview the saved version of htmlEditor
45630              * @param {HtmlEditor} this
45631              */
45632             savedpreview: true,
45633             
45634             /**
45635             * @event stylesheetsclick
45636             * Fires when press the Sytlesheets button
45637             * @param {Roo.HtmlEditorCore} this
45638             */
45639             stylesheetsclick: true
45640         });
45641         this.defaultAutoCreate =  {
45642             tag: "textarea",
45643             style:'width: ' + this.width + 'px;height: ' + this.height + 'px;',
45644             autocomplete: "new-password"
45645         };
45646     },
45647
45648     /**
45649      * Protected method that will not generally be called directly. It
45650      * is called when the editor creates its toolbar. Override this method if you need to
45651      * add custom toolbar buttons.
45652      * @param {HtmlEditor} editor
45653      */
45654     createToolbar : function(editor){
45655         Roo.log("create toolbars");
45656         if (!editor.toolbars || !editor.toolbars.length) {
45657             editor.toolbars = [ new Roo.form.HtmlEditor.ToolbarStandard() ]; // can be empty?
45658         }
45659         
45660         for (var i =0 ; i < editor.toolbars.length;i++) {
45661             editor.toolbars[i] = Roo.factory(
45662                     typeof(editor.toolbars[i]) == 'string' ?
45663                         { xtype: editor.toolbars[i]} : editor.toolbars[i],
45664                 Roo.form.HtmlEditor);
45665             editor.toolbars[i].init(editor);
45666         }
45667          
45668         
45669     },
45670
45671      
45672     // private
45673     onRender : function(ct, position)
45674     {
45675         var _t = this;
45676         Roo.form.HtmlEditor.superclass.onRender.call(this, ct, position);
45677         
45678         this.wrap = this.el.wrap({
45679             cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
45680         });
45681         
45682         this.editorcore.onRender(ct, position);
45683          
45684         if (this.resizable) {
45685             this.resizeEl = new Roo.Resizable(this.wrap, {
45686                 pinned : true,
45687                 wrap: true,
45688                 dynamic : true,
45689                 minHeight : this.height,
45690                 height: this.height,
45691                 handles : this.resizable,
45692                 width: this.width,
45693                 listeners : {
45694                     resize : function(r, w, h) {
45695                         _t.onResize(w,h); // -something
45696                     }
45697                 }
45698             });
45699             
45700         }
45701         this.createToolbar(this);
45702        
45703         
45704         if(!this.width){
45705             this.setSize(this.wrap.getSize());
45706         }
45707         if (this.resizeEl) {
45708             this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
45709             // should trigger onReize..
45710         }
45711         
45712         this.keyNav = new Roo.KeyNav(this.el, {
45713             
45714             "tab" : function(e){
45715                 e.preventDefault();
45716                 
45717                 var value = this.getValue();
45718                 
45719                 var start = this.el.dom.selectionStart;
45720                 var end = this.el.dom.selectionEnd;
45721                 
45722                 if(!e.shiftKey){
45723                     
45724                     this.setValue(value.substring(0, start) + "\t" + value.substring(end));
45725                     this.el.dom.setSelectionRange(end + 1, end + 1);
45726                     return;
45727                 }
45728                 
45729                 var f = value.substring(0, start).split("\t");
45730                 
45731                 if(f.pop().length != 0){
45732                     return;
45733                 }
45734                 
45735                 this.setValue(f.join("\t") + value.substring(end));
45736                 this.el.dom.setSelectionRange(start - 1, start - 1);
45737                 
45738             },
45739             
45740             "home" : function(e){
45741                 e.preventDefault();
45742                 
45743                 var curr = this.el.dom.selectionStart;
45744                 var lines = this.getValue().split("\n");
45745                 
45746                 if(!lines.length){
45747                     return;
45748                 }
45749                 
45750                 if(e.ctrlKey){
45751                     this.el.dom.setSelectionRange(0, 0);
45752                     return;
45753                 }
45754                 
45755                 var pos = 0;
45756                 
45757                 for (var i = 0; i < lines.length;i++) {
45758                     pos += lines[i].length;
45759                     
45760                     if(i != 0){
45761                         pos += 1;
45762                     }
45763                     
45764                     if(pos < curr){
45765                         continue;
45766                     }
45767                     
45768                     pos -= lines[i].length;
45769                     
45770                     break;
45771                 }
45772                 
45773                 if(!e.shiftKey){
45774                     this.el.dom.setSelectionRange(pos, pos);
45775                     return;
45776                 }
45777                 
45778                 this.el.dom.selectionStart = pos;
45779                 this.el.dom.selectionEnd = curr;
45780             },
45781             
45782             "end" : function(e){
45783                 e.preventDefault();
45784                 
45785                 var curr = this.el.dom.selectionStart;
45786                 var lines = this.getValue().split("\n");
45787                 
45788                 if(!lines.length){
45789                     return;
45790                 }
45791                 
45792                 if(e.ctrlKey){
45793                     this.el.dom.setSelectionRange(this.getValue().length, this.getValue().length);
45794                     return;
45795                 }
45796                 
45797                 var pos = 0;
45798                 
45799                 for (var i = 0; i < lines.length;i++) {
45800                     
45801                     pos += lines[i].length;
45802                     
45803                     if(i != 0){
45804                         pos += 1;
45805                     }
45806                     
45807                     if(pos < curr){
45808                         continue;
45809                     }
45810                     
45811                     break;
45812                 }
45813                 
45814                 if(!e.shiftKey){
45815                     this.el.dom.setSelectionRange(pos, pos);
45816                     return;
45817                 }
45818                 
45819                 this.el.dom.selectionStart = curr;
45820                 this.el.dom.selectionEnd = pos;
45821             },
45822
45823             scope : this,
45824
45825             doRelay : function(foo, bar, hname){
45826                 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
45827             },
45828
45829             forceKeyDown: true
45830         });
45831         
45832 //        if(this.autosave && this.w){
45833 //            this.autoSaveFn = setInterval(this.autosave, 1000);
45834 //        }
45835     },
45836
45837     // private
45838     onResize : function(w, h)
45839     {
45840         Roo.form.HtmlEditor.superclass.onResize.apply(this, arguments);
45841         var ew = false;
45842         var eh = false;
45843         
45844         if(this.el ){
45845             if(typeof w == 'number'){
45846                 var aw = w - this.wrap.getFrameWidth('lr');
45847                 this.el.setWidth(this.adjustWidth('textarea', aw));
45848                 ew = aw;
45849             }
45850             if(typeof h == 'number'){
45851                 var tbh = 0;
45852                 for (var i =0; i < this.toolbars.length;i++) {
45853                     // fixme - ask toolbars for heights?
45854                     tbh += this.toolbars[i].tb.el.getHeight();
45855                     if (this.toolbars[i].footer) {
45856                         tbh += this.toolbars[i].footer.el.getHeight();
45857                     }
45858                 }
45859                 
45860                 
45861                 
45862                 
45863                 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
45864                 ah -= 5; // knock a few pixes off for look..
45865 //                Roo.log(ah);
45866                 this.el.setHeight(this.adjustWidth('textarea', ah));
45867                 var eh = ah;
45868             }
45869         }
45870         Roo.log('onResize:' + [w,h,ew,eh].join(',') );
45871         this.editorcore.onResize(ew,eh);
45872         
45873     },
45874
45875     /**
45876      * Toggles the editor between standard and source edit mode.
45877      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
45878      */
45879     toggleSourceEdit : function(sourceEditMode)
45880     {
45881         this.editorcore.toggleSourceEdit(sourceEditMode);
45882         
45883         if(this.editorcore.sourceEditMode){
45884             Roo.log('editor - showing textarea');
45885             
45886 //            Roo.log('in');
45887 //            Roo.log(this.syncValue());
45888             this.editorcore.syncValue();
45889             this.el.removeClass('x-hidden');
45890             this.el.dom.removeAttribute('tabIndex');
45891             this.el.focus();
45892             
45893             for (var i = 0; i < this.toolbars.length; i++) {
45894                 if(this.toolbars[i] instanceof Roo.form.HtmlEditor.ToolbarContext){
45895                     this.toolbars[i].tb.hide();
45896                     this.toolbars[i].footer.hide();
45897                 }
45898             }
45899             
45900         }else{
45901             Roo.log('editor - hiding textarea');
45902 //            Roo.log('out')
45903 //            Roo.log(this.pushValue()); 
45904             this.editorcore.pushValue();
45905             
45906             this.el.addClass('x-hidden');
45907             this.el.dom.setAttribute('tabIndex', -1);
45908             
45909             for (var i = 0; i < this.toolbars.length; i++) {
45910                 if(this.toolbars[i] instanceof Roo.form.HtmlEditor.ToolbarContext){
45911                     this.toolbars[i].tb.show();
45912                     this.toolbars[i].footer.show();
45913                 }
45914             }
45915             
45916             //this.deferFocus();
45917         }
45918         
45919         this.setSize(this.wrap.getSize());
45920         this.onResize(this.wrap.getSize().width, this.wrap.getSize().height);
45921         
45922         this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
45923     },
45924  
45925     // private (for BoxComponent)
45926     adjustSize : Roo.BoxComponent.prototype.adjustSize,
45927
45928     // private (for BoxComponent)
45929     getResizeEl : function(){
45930         return this.wrap;
45931     },
45932
45933     // private (for BoxComponent)
45934     getPositionEl : function(){
45935         return this.wrap;
45936     },
45937
45938     // private
45939     initEvents : function(){
45940         this.originalValue = this.getValue();
45941     },
45942
45943     /**
45944      * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
45945      * @method
45946      */
45947     markInvalid : Roo.emptyFn,
45948     /**
45949      * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
45950      * @method
45951      */
45952     clearInvalid : Roo.emptyFn,
45953
45954     setValue : function(v){
45955         Roo.form.HtmlEditor.superclass.setValue.call(this, v);
45956         this.editorcore.pushValue();
45957     },
45958
45959      
45960     // private
45961     deferFocus : function(){
45962         this.focus.defer(10, this);
45963     },
45964
45965     // doc'ed in Field
45966     focus : function(){
45967         this.editorcore.focus();
45968         
45969     },
45970       
45971
45972     // private
45973     onDestroy : function(){
45974         
45975         
45976         
45977         if(this.rendered){
45978             
45979             for (var i =0; i < this.toolbars.length;i++) {
45980                 // fixme - ask toolbars for heights?
45981                 this.toolbars[i].onDestroy();
45982             }
45983             
45984             this.wrap.dom.innerHTML = '';
45985             this.wrap.remove();
45986         }
45987     },
45988
45989     // private
45990     onFirstFocus : function(){
45991         //Roo.log("onFirstFocus");
45992         this.editorcore.onFirstFocus();
45993          for (var i =0; i < this.toolbars.length;i++) {
45994             this.toolbars[i].onFirstFocus();
45995         }
45996         
45997     },
45998     
45999     // private
46000     syncValue : function()
46001     {
46002         this.editorcore.syncValue();
46003     },
46004     
46005     pushValue : function()
46006     {
46007         this.editorcore.pushValue();
46008     },
46009     
46010     setStylesheets : function(stylesheets)
46011     {
46012         this.editorcore.setStylesheets(stylesheets);
46013     },
46014     
46015     removeStylesheets : function()
46016     {
46017         this.editorcore.removeStylesheets();
46018     }
46019      
46020     
46021     // hide stuff that is not compatible
46022     /**
46023      * @event blur
46024      * @hide
46025      */
46026     /**
46027      * @event change
46028      * @hide
46029      */
46030     /**
46031      * @event focus
46032      * @hide
46033      */
46034     /**
46035      * @event specialkey
46036      * @hide
46037      */
46038     /**
46039      * @cfg {String} fieldClass @hide
46040      */
46041     /**
46042      * @cfg {String} focusClass @hide
46043      */
46044     /**
46045      * @cfg {String} autoCreate @hide
46046      */
46047     /**
46048      * @cfg {String} inputType @hide
46049      */
46050     /**
46051      * @cfg {String} invalidClass @hide
46052      */
46053     /**
46054      * @cfg {String} invalidText @hide
46055      */
46056     /**
46057      * @cfg {String} msgFx @hide
46058      */
46059     /**
46060      * @cfg {String} validateOnBlur @hide
46061      */
46062 });
46063  
46064     // <script type="text/javascript">
46065 /*
46066  * Based on
46067  * Ext JS Library 1.1.1
46068  * Copyright(c) 2006-2007, Ext JS, LLC.
46069  *  
46070  
46071  */
46072
46073 /**
46074  * @class Roo.form.HtmlEditorToolbar1
46075  * Basic Toolbar
46076  * 
46077  * Usage:
46078  *
46079  new Roo.form.HtmlEditor({
46080     ....
46081     toolbars : [
46082         new Roo.form.HtmlEditorToolbar1({
46083             disable : { fonts: 1 , format: 1, ..., ... , ...],
46084             btns : [ .... ]
46085         })
46086     }
46087      
46088  * 
46089  * @cfg {Object} disable List of elements to disable..
46090  * @cfg {Array} btns List of additional buttons.
46091  * 
46092  * 
46093  * NEEDS Extra CSS? 
46094  * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
46095  */
46096  
46097 Roo.form.HtmlEditor.ToolbarStandard = function(config)
46098 {
46099     
46100     Roo.apply(this, config);
46101     
46102     // default disabled, based on 'good practice'..
46103     this.disable = this.disable || {};
46104     Roo.applyIf(this.disable, {
46105         fontSize : true,
46106         colors : true,
46107         specialElements : true
46108     });
46109     
46110     
46111     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
46112     // dont call parent... till later.
46113 }
46114
46115 Roo.apply(Roo.form.HtmlEditor.ToolbarStandard.prototype,  {
46116     
46117     tb: false,
46118     
46119     rendered: false,
46120     
46121     editor : false,
46122     editorcore : false,
46123     /**
46124      * @cfg {Object} disable  List of toolbar elements to disable
46125          
46126      */
46127     disable : false,
46128     
46129     
46130      /**
46131      * @cfg {String} createLinkText The default text for the create link prompt
46132      */
46133     createLinkText : 'Please enter the URL for the link:',
46134     /**
46135      * @cfg {String} defaultLinkValue The default value for the create link prompt (defaults to http:/ /)
46136      */
46137     defaultLinkValue : 'http:/'+'/',
46138    
46139     
46140       /**
46141      * @cfg {Array} fontFamilies An array of available font families
46142      */
46143     fontFamilies : [
46144         'Arial',
46145         'Courier New',
46146         'Tahoma',
46147         'Times New Roman',
46148         'Verdana'
46149     ],
46150     
46151     specialChars : [
46152            "&#169;",
46153           "&#174;",     
46154           "&#8482;",    
46155           "&#163;" ,    
46156          // "&#8212;",    
46157           "&#8230;",    
46158           "&#247;" ,    
46159         //  "&#225;" ,     ?? a acute?
46160            "&#8364;"    , //Euro
46161        //   "&#8220;"    ,
46162         //  "&#8221;"    ,
46163         //  "&#8226;"    ,
46164           "&#176;"  //   , // degrees
46165
46166          // "&#233;"     , // e ecute
46167          // "&#250;"     , // u ecute?
46168     ],
46169     
46170     specialElements : [
46171         {
46172             text: "Insert Table",
46173             xtype: 'MenuItem',
46174             xns : Roo.Menu,
46175             ihtml :  '<table><tr><td>Cell</td></tr></table>' 
46176                 
46177         },
46178         {    
46179             text: "Insert Image",
46180             xtype: 'MenuItem',
46181             xns : Roo.Menu,
46182             ihtml : '<img src="about:blank"/>'
46183             
46184         }
46185         
46186          
46187     ],
46188     
46189     
46190     inputElements : [ 
46191             "form", "input:text", "input:hidden", "input:checkbox", "input:radio", "input:password", 
46192             "input:submit", "input:button", "select", "textarea", "label" ],
46193     formats : [
46194         ["p"] ,  
46195         ["h1"],["h2"],["h3"],["h4"],["h5"],["h6"], 
46196         ["pre"],[ "code"], 
46197         ["abbr"],[ "acronym"],[ "address"],[ "cite"],[ "samp"],[ "var"],
46198         ['div'],['span'],
46199         ['sup'],['sub']
46200     ],
46201     
46202     cleanStyles : [
46203         "font-size"
46204     ],
46205      /**
46206      * @cfg {String} defaultFont default font to use.
46207      */
46208     defaultFont: 'tahoma',
46209    
46210     fontSelect : false,
46211     
46212     
46213     formatCombo : false,
46214     
46215     init : function(editor)
46216     {
46217         this.editor = editor;
46218         this.editorcore = editor.editorcore ? editor.editorcore : editor;
46219         var editorcore = this.editorcore;
46220         
46221         var _t = this;
46222         
46223         var fid = editorcore.frameId;
46224         var etb = this;
46225         function btn(id, toggle, handler){
46226             var xid = fid + '-'+ id ;
46227             return {
46228                 id : xid,
46229                 cmd : id,
46230                 cls : 'x-btn-icon x-edit-'+id,
46231                 enableToggle:toggle !== false,
46232                 scope: _t, // was editor...
46233                 handler:handler||_t.relayBtnCmd,
46234                 clickEvent:'mousedown',
46235                 tooltip: etb.buttonTips[id] || undefined, ///tips ???
46236                 tabIndex:-1
46237             };
46238         }
46239         
46240         
46241         
46242         var tb = new Roo.Toolbar(editor.wrap.dom.firstChild);
46243         this.tb = tb;
46244          // stop form submits
46245         tb.el.on('click', function(e){
46246             e.preventDefault(); // what does this do?
46247         });
46248
46249         if(!this.disable.font) { // && !Roo.isSafari){
46250             /* why no safari for fonts 
46251             editor.fontSelect = tb.el.createChild({
46252                 tag:'select',
46253                 tabIndex: -1,
46254                 cls:'x-font-select',
46255                 html: this.createFontOptions()
46256             });
46257             
46258             editor.fontSelect.on('change', function(){
46259                 var font = editor.fontSelect.dom.value;
46260                 editor.relayCmd('fontname', font);
46261                 editor.deferFocus();
46262             }, editor);
46263             
46264             tb.add(
46265                 editor.fontSelect.dom,
46266                 '-'
46267             );
46268             */
46269             
46270         };
46271         if(!this.disable.formats){
46272             this.formatCombo = new Roo.form.ComboBox({
46273                 store: new Roo.data.SimpleStore({
46274                     id : 'tag',
46275                     fields: ['tag'],
46276                     data : this.formats // from states.js
46277                 }),
46278                 blockFocus : true,
46279                 name : '',
46280                 //autoCreate : {tag: "div",  size: "20"},
46281                 displayField:'tag',
46282                 typeAhead: false,
46283                 mode: 'local',
46284                 editable : false,
46285                 triggerAction: 'all',
46286                 emptyText:'Add tag',
46287                 selectOnFocus:true,
46288                 width:135,
46289                 listeners : {
46290                     'select': function(c, r, i) {
46291                         editorcore.insertTag(r.get('tag'));
46292                         editor.focus();
46293                     }
46294                 }
46295
46296             });
46297             tb.addField(this.formatCombo);
46298             
46299         }
46300         
46301         if(!this.disable.format){
46302             tb.add(
46303                 btn('bold'),
46304                 btn('italic'),
46305                 btn('underline'),
46306                 btn('strikethrough')
46307             );
46308         };
46309         if(!this.disable.fontSize){
46310             tb.add(
46311                 '-',
46312                 
46313                 
46314                 btn('increasefontsize', false, editorcore.adjustFont),
46315                 btn('decreasefontsize', false, editorcore.adjustFont)
46316             );
46317         };
46318         
46319         
46320         if(!this.disable.colors){
46321             tb.add(
46322                 '-', {
46323                     id:editorcore.frameId +'-forecolor',
46324                     cls:'x-btn-icon x-edit-forecolor',
46325                     clickEvent:'mousedown',
46326                     tooltip: this.buttonTips['forecolor'] || undefined,
46327                     tabIndex:-1,
46328                     menu : new Roo.menu.ColorMenu({
46329                         allowReselect: true,
46330                         focus: Roo.emptyFn,
46331                         value:'000000',
46332                         plain:true,
46333                         selectHandler: function(cp, color){
46334                             editorcore.execCmd('forecolor', Roo.isSafari || Roo.isIE ? '#'+color : color);
46335                             editor.deferFocus();
46336                         },
46337                         scope: editorcore,
46338                         clickEvent:'mousedown'
46339                     })
46340                 }, {
46341                     id:editorcore.frameId +'backcolor',
46342                     cls:'x-btn-icon x-edit-backcolor',
46343                     clickEvent:'mousedown',
46344                     tooltip: this.buttonTips['backcolor'] || undefined,
46345                     tabIndex:-1,
46346                     menu : new Roo.menu.ColorMenu({
46347                         focus: Roo.emptyFn,
46348                         value:'FFFFFF',
46349                         plain:true,
46350                         allowReselect: true,
46351                         selectHandler: function(cp, color){
46352                             if(Roo.isGecko){
46353                                 editorcore.execCmd('useCSS', false);
46354                                 editorcore.execCmd('hilitecolor', color);
46355                                 editorcore.execCmd('useCSS', true);
46356                                 editor.deferFocus();
46357                             }else{
46358                                 editorcore.execCmd(Roo.isOpera ? 'hilitecolor' : 'backcolor', 
46359                                     Roo.isSafari || Roo.isIE ? '#'+color : color);
46360                                 editor.deferFocus();
46361                             }
46362                         },
46363                         scope:editorcore,
46364                         clickEvent:'mousedown'
46365                     })
46366                 }
46367             );
46368         };
46369         // now add all the items...
46370         
46371
46372         if(!this.disable.alignments){
46373             tb.add(
46374                 '-',
46375                 btn('justifyleft'),
46376                 btn('justifycenter'),
46377                 btn('justifyright')
46378             );
46379         };
46380
46381         //if(!Roo.isSafari){
46382             if(!this.disable.links){
46383                 tb.add(
46384                     '-',
46385                     btn('createlink', false, this.createLink)    /// MOVE TO HERE?!!?!?!?!
46386                 );
46387             };
46388
46389             if(!this.disable.lists){
46390                 tb.add(
46391                     '-',
46392                     btn('insertorderedlist'),
46393                     btn('insertunorderedlist')
46394                 );
46395             }
46396             if(!this.disable.sourceEdit){
46397                 tb.add(
46398                     '-',
46399                     btn('sourceedit', true, function(btn){
46400                         this.toggleSourceEdit(btn.pressed);
46401                     })
46402                 );
46403             }
46404         //}
46405         
46406         var smenu = { };
46407         // special menu.. - needs to be tidied up..
46408         if (!this.disable.special) {
46409             smenu = {
46410                 text: "&#169;",
46411                 cls: 'x-edit-none',
46412                 
46413                 menu : {
46414                     items : []
46415                 }
46416             };
46417             for (var i =0; i < this.specialChars.length; i++) {
46418                 smenu.menu.items.push({
46419                     
46420                     html: this.specialChars[i],
46421                     handler: function(a,b) {
46422                         editorcore.insertAtCursor(String.fromCharCode(a.html.replace('&#','').replace(';', '')));
46423                         //editor.insertAtCursor(a.html);
46424                         
46425                     },
46426                     tabIndex:-1
46427                 });
46428             }
46429             
46430             
46431             tb.add(smenu);
46432             
46433             
46434         }
46435         
46436         var cmenu = { };
46437         if (!this.disable.cleanStyles) {
46438             cmenu = {
46439                 cls: 'x-btn-icon x-btn-clear',
46440                 
46441                 menu : {
46442                     items : []
46443                 }
46444             };
46445             for (var i =0; i < this.cleanStyles.length; i++) {
46446                 cmenu.menu.items.push({
46447                     actiontype : this.cleanStyles[i],
46448                     html: 'Remove ' + this.cleanStyles[i],
46449                     handler: function(a,b) {
46450 //                        Roo.log(a);
46451 //                        Roo.log(b);
46452                         var c = Roo.get(editorcore.doc.body);
46453                         c.select('[style]').each(function(s) {
46454                             s.dom.style.removeProperty(a.actiontype);
46455                         });
46456                         editorcore.syncValue();
46457                     },
46458                     tabIndex:-1
46459                 });
46460             }
46461              cmenu.menu.items.push({
46462                 actiontype : 'tablewidths',
46463                 html: 'Remove Table Widths',
46464                 handler: function(a,b) {
46465                     editorcore.cleanTableWidths();
46466                     editorcore.syncValue();
46467                 },
46468                 tabIndex:-1
46469             });
46470             cmenu.menu.items.push({
46471                 actiontype : 'word',
46472                 html: 'Remove MS Word Formating',
46473                 handler: function(a,b) {
46474                     editorcore.cleanWord();
46475                     editorcore.syncValue();
46476                 },
46477                 tabIndex:-1
46478             });
46479             
46480             cmenu.menu.items.push({
46481                 actiontype : 'all',
46482                 html: 'Remove All Styles',
46483                 handler: function(a,b) {
46484                     
46485                     var c = Roo.get(editorcore.doc.body);
46486                     c.select('[style]').each(function(s) {
46487                         s.dom.removeAttribute('style');
46488                     });
46489                     editorcore.syncValue();
46490                 },
46491                 tabIndex:-1
46492             });
46493             
46494             cmenu.menu.items.push({
46495                 actiontype : 'all',
46496                 html: 'Remove All CSS Classes',
46497                 handler: function(a,b) {
46498                     
46499                     var c = Roo.get(editorcore.doc.body);
46500                     c.select('[class]').each(function(s) {
46501                         s.dom.removeAttribute('class');
46502                     });
46503                     editorcore.cleanWord();
46504                     editorcore.syncValue();
46505                 },
46506                 tabIndex:-1
46507             });
46508             
46509              cmenu.menu.items.push({
46510                 actiontype : 'tidy',
46511                 html: 'Tidy HTML Source',
46512                 handler: function(a,b) {
46513                     editorcore.doc.body.innerHTML = editorcore.domToHTML();
46514                     editorcore.syncValue();
46515                 },
46516                 tabIndex:-1
46517             });
46518             
46519             
46520             tb.add(cmenu);
46521         }
46522          
46523         if (!this.disable.specialElements) {
46524             var semenu = {
46525                 text: "Other;",
46526                 cls: 'x-edit-none',
46527                 menu : {
46528                     items : []
46529                 }
46530             };
46531             for (var i =0; i < this.specialElements.length; i++) {
46532                 semenu.menu.items.push(
46533                     Roo.apply({ 
46534                         handler: function(a,b) {
46535                             editor.insertAtCursor(this.ihtml);
46536                         }
46537                     }, this.specialElements[i])
46538                 );
46539                     
46540             }
46541             
46542             tb.add(semenu);
46543             
46544             
46545         }
46546          
46547         
46548         if (this.btns) {
46549             for(var i =0; i< this.btns.length;i++) {
46550                 var b = Roo.factory(this.btns[i],Roo.form);
46551                 b.cls =  'x-edit-none';
46552                 
46553                 if(typeof(this.btns[i].cls) != 'undefined' && this.btns[i].cls.indexOf('x-init-enable') !== -1){
46554                     b.cls += ' x-init-enable';
46555                 }
46556                 
46557                 b.scope = editorcore;
46558                 tb.add(b);
46559             }
46560         
46561         }
46562         
46563         
46564         
46565         // disable everything...
46566         
46567         this.tb.items.each(function(item){
46568             
46569            if(
46570                 item.id != editorcore.frameId+ '-sourceedit' && 
46571                 (typeof(item.cls) != 'undefined' && item.cls.indexOf('x-init-enable') === -1)
46572             ){
46573                 
46574                 item.disable();
46575             }
46576         });
46577         this.rendered = true;
46578         
46579         // the all the btns;
46580         editor.on('editorevent', this.updateToolbar, this);
46581         // other toolbars need to implement this..
46582         //editor.on('editmodechange', this.updateToolbar, this);
46583     },
46584     
46585     
46586     relayBtnCmd : function(btn) {
46587         this.editorcore.relayCmd(btn.cmd);
46588     },
46589     // private used internally
46590     createLink : function(){
46591         Roo.log("create link?");
46592         var url = prompt(this.createLinkText, this.defaultLinkValue);
46593         if(url && url != 'http:/'+'/'){
46594             this.editorcore.relayCmd('createlink', url);
46595         }
46596     },
46597
46598     
46599     /**
46600      * Protected method that will not generally be called directly. It triggers
46601      * a toolbar update by reading the markup state of the current selection in the editor.
46602      */
46603     updateToolbar: function(){
46604
46605         if(!this.editorcore.activated){
46606             this.editor.onFirstFocus();
46607             return;
46608         }
46609
46610         var btns = this.tb.items.map, 
46611             doc = this.editorcore.doc,
46612             frameId = this.editorcore.frameId;
46613
46614         if(!this.disable.font && !Roo.isSafari){
46615             /*
46616             var name = (doc.queryCommandValue('FontName')||this.editor.defaultFont).toLowerCase();
46617             if(name != this.fontSelect.dom.value){
46618                 this.fontSelect.dom.value = name;
46619             }
46620             */
46621         }
46622         if(!this.disable.format){
46623             btns[frameId + '-bold'].toggle(doc.queryCommandState('bold'));
46624             btns[frameId + '-italic'].toggle(doc.queryCommandState('italic'));
46625             btns[frameId + '-underline'].toggle(doc.queryCommandState('underline'));
46626             btns[frameId + '-strikethrough'].toggle(doc.queryCommandState('strikethrough'));
46627         }
46628         if(!this.disable.alignments){
46629             btns[frameId + '-justifyleft'].toggle(doc.queryCommandState('justifyleft'));
46630             btns[frameId + '-justifycenter'].toggle(doc.queryCommandState('justifycenter'));
46631             btns[frameId + '-justifyright'].toggle(doc.queryCommandState('justifyright'));
46632         }
46633         if(!Roo.isSafari && !this.disable.lists){
46634             btns[frameId + '-insertorderedlist'].toggle(doc.queryCommandState('insertorderedlist'));
46635             btns[frameId + '-insertunorderedlist'].toggle(doc.queryCommandState('insertunorderedlist'));
46636         }
46637         
46638         var ans = this.editorcore.getAllAncestors();
46639         if (this.formatCombo) {
46640             
46641             
46642             var store = this.formatCombo.store;
46643             this.formatCombo.setValue("");
46644             for (var i =0; i < ans.length;i++) {
46645                 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
46646                     // select it..
46647                     this.formatCombo.setValue(ans[i].tagName.toLowerCase());
46648                     break;
46649                 }
46650             }
46651         }
46652         
46653         
46654         
46655         // hides menus... - so this cant be on a menu...
46656         Roo.menu.MenuMgr.hideAll();
46657
46658         //this.editorsyncValue();
46659     },
46660    
46661     
46662     createFontOptions : function(){
46663         var buf = [], fs = this.fontFamilies, ff, lc;
46664         
46665         
46666         
46667         for(var i = 0, len = fs.length; i< len; i++){
46668             ff = fs[i];
46669             lc = ff.toLowerCase();
46670             buf.push(
46671                 '<option value="',lc,'" style="font-family:',ff,';"',
46672                     (this.defaultFont == lc ? ' selected="true">' : '>'),
46673                     ff,
46674                 '</option>'
46675             );
46676         }
46677         return buf.join('');
46678     },
46679     
46680     toggleSourceEdit : function(sourceEditMode){
46681         
46682         Roo.log("toolbar toogle");
46683         if(sourceEditMode === undefined){
46684             sourceEditMode = !this.sourceEditMode;
46685         }
46686         this.sourceEditMode = sourceEditMode === true;
46687         var btn = this.tb.items.get(this.editorcore.frameId +'-sourceedit');
46688         // just toggle the button?
46689         if(btn.pressed !== this.sourceEditMode){
46690             btn.toggle(this.sourceEditMode);
46691             return;
46692         }
46693         
46694         if(sourceEditMode){
46695             Roo.log("disabling buttons");
46696             this.tb.items.each(function(item){
46697                 if(item.cmd != 'sourceedit' && (typeof(item.cls) != 'undefined' && item.cls.indexOf('x-init-enable') === -1)){
46698                     item.disable();
46699                 }
46700             });
46701           
46702         }else{
46703             Roo.log("enabling buttons");
46704             if(this.editorcore.initialized){
46705                 this.tb.items.each(function(item){
46706                     item.enable();
46707                 });
46708             }
46709             
46710         }
46711         Roo.log("calling toggole on editor");
46712         // tell the editor that it's been pressed..
46713         this.editor.toggleSourceEdit(sourceEditMode);
46714        
46715     },
46716      /**
46717      * Object collection of toolbar tooltips for the buttons in the editor. The key
46718      * is the command id associated with that button and the value is a valid QuickTips object.
46719      * For example:
46720 <pre><code>
46721 {
46722     bold : {
46723         title: 'Bold (Ctrl+B)',
46724         text: 'Make the selected text bold.',
46725         cls: 'x-html-editor-tip'
46726     },
46727     italic : {
46728         title: 'Italic (Ctrl+I)',
46729         text: 'Make the selected text italic.',
46730         cls: 'x-html-editor-tip'
46731     },
46732     ...
46733 </code></pre>
46734     * @type Object
46735      */
46736     buttonTips : {
46737         bold : {
46738             title: 'Bold (Ctrl+B)',
46739             text: 'Make the selected text bold.',
46740             cls: 'x-html-editor-tip'
46741         },
46742         italic : {
46743             title: 'Italic (Ctrl+I)',
46744             text: 'Make the selected text italic.',
46745             cls: 'x-html-editor-tip'
46746         },
46747         underline : {
46748             title: 'Underline (Ctrl+U)',
46749             text: 'Underline the selected text.',
46750             cls: 'x-html-editor-tip'
46751         },
46752         strikethrough : {
46753             title: 'Strikethrough',
46754             text: 'Strikethrough the selected text.',
46755             cls: 'x-html-editor-tip'
46756         },
46757         increasefontsize : {
46758             title: 'Grow Text',
46759             text: 'Increase the font size.',
46760             cls: 'x-html-editor-tip'
46761         },
46762         decreasefontsize : {
46763             title: 'Shrink Text',
46764             text: 'Decrease the font size.',
46765             cls: 'x-html-editor-tip'
46766         },
46767         backcolor : {
46768             title: 'Text Highlight Color',
46769             text: 'Change the background color of the selected text.',
46770             cls: 'x-html-editor-tip'
46771         },
46772         forecolor : {
46773             title: 'Font Color',
46774             text: 'Change the color of the selected text.',
46775             cls: 'x-html-editor-tip'
46776         },
46777         justifyleft : {
46778             title: 'Align Text Left',
46779             text: 'Align text to the left.',
46780             cls: 'x-html-editor-tip'
46781         },
46782         justifycenter : {
46783             title: 'Center Text',
46784             text: 'Center text in the editor.',
46785             cls: 'x-html-editor-tip'
46786         },
46787         justifyright : {
46788             title: 'Align Text Right',
46789             text: 'Align text to the right.',
46790             cls: 'x-html-editor-tip'
46791         },
46792         insertunorderedlist : {
46793             title: 'Bullet List',
46794             text: 'Start a bulleted list.',
46795             cls: 'x-html-editor-tip'
46796         },
46797         insertorderedlist : {
46798             title: 'Numbered List',
46799             text: 'Start a numbered list.',
46800             cls: 'x-html-editor-tip'
46801         },
46802         createlink : {
46803             title: 'Hyperlink',
46804             text: 'Make the selected text a hyperlink.',
46805             cls: 'x-html-editor-tip'
46806         },
46807         sourceedit : {
46808             title: 'Source Edit',
46809             text: 'Switch to source editing mode.',
46810             cls: 'x-html-editor-tip'
46811         }
46812     },
46813     // private
46814     onDestroy : function(){
46815         if(this.rendered){
46816             
46817             this.tb.items.each(function(item){
46818                 if(item.menu){
46819                     item.menu.removeAll();
46820                     if(item.menu.el){
46821                         item.menu.el.destroy();
46822                     }
46823                 }
46824                 item.destroy();
46825             });
46826              
46827         }
46828     },
46829     onFirstFocus: function() {
46830         this.tb.items.each(function(item){
46831            item.enable();
46832         });
46833     }
46834 });
46835
46836
46837
46838
46839 // <script type="text/javascript">
46840 /*
46841  * Based on
46842  * Ext JS Library 1.1.1
46843  * Copyright(c) 2006-2007, Ext JS, LLC.
46844  *  
46845  
46846  */
46847
46848  
46849 /**
46850  * @class Roo.form.HtmlEditor.ToolbarContext
46851  * Context Toolbar
46852  * 
46853  * Usage:
46854  *
46855  new Roo.form.HtmlEditor({
46856     ....
46857     toolbars : [
46858         { xtype: 'ToolbarStandard', styles : {} }
46859         { xtype: 'ToolbarContext', disable : {} }
46860     ]
46861 })
46862
46863      
46864  * 
46865  * @config : {Object} disable List of elements to disable.. (not done yet.)
46866  * @config : {Object} styles  Map of styles available.
46867  * 
46868  */
46869
46870 Roo.form.HtmlEditor.ToolbarContext = function(config)
46871 {
46872     
46873     Roo.apply(this, config);
46874     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
46875     // dont call parent... till later.
46876     this.styles = this.styles || {};
46877 }
46878
46879  
46880
46881 Roo.form.HtmlEditor.ToolbarContext.types = {
46882     'IMG' : {
46883         width : {
46884             title: "Width",
46885             width: 40
46886         },
46887         height:  {
46888             title: "Height",
46889             width: 40
46890         },
46891         align: {
46892             title: "Align",
46893             opts : [ [""],[ "left"],[ "right"],[ "center"],[ "top"]],
46894             width : 80
46895             
46896         },
46897         border: {
46898             title: "Border",
46899             width: 40
46900         },
46901         alt: {
46902             title: "Alt",
46903             width: 120
46904         },
46905         src : {
46906             title: "Src",
46907             width: 220
46908         }
46909         
46910     },
46911     'A' : {
46912         name : {
46913             title: "Name",
46914             width: 50
46915         },
46916         target:  {
46917             title: "Target",
46918             width: 120
46919         },
46920         href:  {
46921             title: "Href",
46922             width: 220
46923         } // border?
46924         
46925     },
46926     'TABLE' : {
46927         rows : {
46928             title: "Rows",
46929             width: 20
46930         },
46931         cols : {
46932             title: "Cols",
46933             width: 20
46934         },
46935         width : {
46936             title: "Width",
46937             width: 40
46938         },
46939         height : {
46940             title: "Height",
46941             width: 40
46942         },
46943         border : {
46944             title: "Border",
46945             width: 20
46946         }
46947     },
46948     'TD' : {
46949         width : {
46950             title: "Width",
46951             width: 40
46952         },
46953         height : {
46954             title: "Height",
46955             width: 40
46956         },   
46957         align: {
46958             title: "Align",
46959             opts : [[""],[ "left"],[ "center"],[ "right"],[ "justify"],[ "char"]],
46960             width: 80
46961         },
46962         valign: {
46963             title: "Valign",
46964             opts : [[""],[ "top"],[ "middle"],[ "bottom"],[ "baseline"]],
46965             width: 80
46966         },
46967         colspan: {
46968             title: "Colspan",
46969             width: 20
46970             
46971         },
46972          'font-family'  : {
46973             title : "Font",
46974             style : 'fontFamily',
46975             displayField: 'display',
46976             optname : 'font-family',
46977             width: 140
46978         }
46979     },
46980     'INPUT' : {
46981         name : {
46982             title: "name",
46983             width: 120
46984         },
46985         value : {
46986             title: "Value",
46987             width: 120
46988         },
46989         width : {
46990             title: "Width",
46991             width: 40
46992         }
46993     },
46994     'LABEL' : {
46995         'for' : {
46996             title: "For",
46997             width: 120
46998         }
46999     },
47000     'TEXTAREA' : {
47001           name : {
47002             title: "name",
47003             width: 120
47004         },
47005         rows : {
47006             title: "Rows",
47007             width: 20
47008         },
47009         cols : {
47010             title: "Cols",
47011             width: 20
47012         }
47013     },
47014     'SELECT' : {
47015         name : {
47016             title: "name",
47017             width: 120
47018         },
47019         selectoptions : {
47020             title: "Options",
47021             width: 200
47022         }
47023     },
47024     
47025     // should we really allow this??
47026     // should this just be 
47027     'BODY' : {
47028         title : {
47029             title: "Title",
47030             width: 200,
47031             disabled : true
47032         }
47033     },
47034     'SPAN' : {
47035         'font-family'  : {
47036             title : "Font",
47037             style : 'fontFamily',
47038             displayField: 'display',
47039             optname : 'font-family',
47040             width: 140
47041         }
47042     },
47043     'DIV' : {
47044         'font-family'  : {
47045             title : "Font",
47046             style : 'fontFamily',
47047             displayField: 'display',
47048             optname : 'font-family',
47049             width: 140
47050         }
47051     },
47052      'P' : {
47053         'font-family'  : {
47054             title : "Font",
47055             style : 'fontFamily',
47056             displayField: 'display',
47057             optname : 'font-family',
47058             width: 140
47059         }
47060     },
47061     
47062     '*' : {
47063         // empty..
47064     }
47065
47066 };
47067
47068 // this should be configurable.. - you can either set it up using stores, or modify options somehwere..
47069 Roo.form.HtmlEditor.ToolbarContext.stores = false;
47070
47071 Roo.form.HtmlEditor.ToolbarContext.options = {
47072         'font-family'  : [ 
47073                 [ 'Helvetica,Arial,sans-serif', 'Helvetica'],
47074                 [ 'Courier New', 'Courier New'],
47075                 [ 'Tahoma', 'Tahoma'],
47076                 [ 'Times New Roman,serif', 'Times'],
47077                 [ 'Verdana','Verdana' ]
47078         ]
47079 };
47080
47081 // fixme - these need to be configurable..
47082  
47083
47084 //Roo.form.HtmlEditor.ToolbarContext.types
47085
47086
47087 Roo.apply(Roo.form.HtmlEditor.ToolbarContext.prototype,  {
47088     
47089     tb: false,
47090     
47091     rendered: false,
47092     
47093     editor : false,
47094     editorcore : false,
47095     /**
47096      * @cfg {Object} disable  List of toolbar elements to disable
47097          
47098      */
47099     disable : false,
47100     /**
47101      * @cfg {Object} styles List of styles 
47102      *    eg. { '*' : [ 'headline' ] , 'TD' : [ 'underline', 'double-underline' ] } 
47103      *
47104      * These must be defined in the page, so they get rendered correctly..
47105      * .headline { }
47106      * TD.underline { }
47107      * 
47108      */
47109     styles : false,
47110     
47111     options: false,
47112     
47113     toolbars : false,
47114     
47115     init : function(editor)
47116     {
47117         this.editor = editor;
47118         this.editorcore = editor.editorcore ? editor.editorcore : editor;
47119         var editorcore = this.editorcore;
47120         
47121         var fid = editorcore.frameId;
47122         var etb = this;
47123         function btn(id, toggle, handler){
47124             var xid = fid + '-'+ id ;
47125             return {
47126                 id : xid,
47127                 cmd : id,
47128                 cls : 'x-btn-icon x-edit-'+id,
47129                 enableToggle:toggle !== false,
47130                 scope: editorcore, // was editor...
47131                 handler:handler||editorcore.relayBtnCmd,
47132                 clickEvent:'mousedown',
47133                 tooltip: etb.buttonTips[id] || undefined, ///tips ???
47134                 tabIndex:-1
47135             };
47136         }
47137         // create a new element.
47138         var wdiv = editor.wrap.createChild({
47139                 tag: 'div'
47140             }, editor.wrap.dom.firstChild.nextSibling, true);
47141         
47142         // can we do this more than once??
47143         
47144          // stop form submits
47145       
47146  
47147         // disable everything...
47148         var ty= Roo.form.HtmlEditor.ToolbarContext.types;
47149         this.toolbars = {};
47150            
47151         for (var i in  ty) {
47152           
47153             this.toolbars[i] = this.buildToolbar(ty[i],i);
47154         }
47155         this.tb = this.toolbars.BODY;
47156         this.tb.el.show();
47157         this.buildFooter();
47158         this.footer.show();
47159         editor.on('hide', function( ) { this.footer.hide() }, this);
47160         editor.on('show', function( ) { this.footer.show() }, this);
47161         
47162          
47163         this.rendered = true;
47164         
47165         // the all the btns;
47166         editor.on('editorevent', this.updateToolbar, this);
47167         // other toolbars need to implement this..
47168         //editor.on('editmodechange', this.updateToolbar, this);
47169     },
47170     
47171     
47172     
47173     /**
47174      * Protected method that will not generally be called directly. It triggers
47175      * a toolbar update by reading the markup state of the current selection in the editor.
47176      *
47177      * Note you can force an update by calling on('editorevent', scope, false)
47178      */
47179     updateToolbar: function(editor,ev,sel){
47180
47181         //Roo.log(ev);
47182         // capture mouse up - this is handy for selecting images..
47183         // perhaps should go somewhere else...
47184         if(!this.editorcore.activated){
47185              this.editor.onFirstFocus();
47186             return;
47187         }
47188         
47189         
47190         
47191         // http://developer.yahoo.com/yui/docs/simple-editor.js.html
47192         // selectNode - might want to handle IE?
47193         if (ev &&
47194             (ev.type == 'mouseup' || ev.type == 'click' ) &&
47195             ev.target && ev.target.tagName == 'IMG') {
47196             // they have click on an image...
47197             // let's see if we can change the selection...
47198             sel = ev.target;
47199          
47200               var nodeRange = sel.ownerDocument.createRange();
47201             try {
47202                 nodeRange.selectNode(sel);
47203             } catch (e) {
47204                 nodeRange.selectNodeContents(sel);
47205             }
47206             //nodeRange.collapse(true);
47207             var s = this.editorcore.win.getSelection();
47208             s.removeAllRanges();
47209             s.addRange(nodeRange);
47210         }  
47211         
47212       
47213         var updateFooter = sel ? false : true;
47214         
47215         
47216         var ans = this.editorcore.getAllAncestors();
47217         
47218         // pick
47219         var ty= Roo.form.HtmlEditor.ToolbarContext.types;
47220         
47221         if (!sel) { 
47222             sel = ans.length ? (ans[0] ?  ans[0]  : ans[1]) : this.editorcore.doc.body;
47223             sel = sel ? sel : this.editorcore.doc.body;
47224             sel = sel.tagName.length ? sel : this.editorcore.doc.body;
47225             
47226         }
47227         // pick a menu that exists..
47228         var tn = sel.tagName.toUpperCase();
47229         //sel = typeof(ty[tn]) != 'undefined' ? sel : this.editor.doc.body;
47230         
47231         tn = sel.tagName.toUpperCase();
47232         
47233         var lastSel = this.tb.selectedNode;
47234         
47235         this.tb.selectedNode = sel;
47236         
47237         // if current menu does not match..
47238         
47239         if ((this.tb.name != tn) || (lastSel != this.tb.selectedNode) || ev === false) {
47240                 
47241             this.tb.el.hide();
47242             ///console.log("show: " + tn);
47243             this.tb =  typeof(ty[tn]) != 'undefined' ? this.toolbars[tn] : this.toolbars['*'];
47244             this.tb.el.show();
47245             // update name
47246             this.tb.items.first().el.innerHTML = tn + ':&nbsp;';
47247             
47248             
47249             // update attributes
47250             if (this.tb.fields) {
47251                 this.tb.fields.each(function(e) {
47252                     if (e.stylename) {
47253                         e.setValue(sel.style[e.stylename]);
47254                         return;
47255                     } 
47256                    e.setValue(sel.getAttribute(e.attrname));
47257                 });
47258             }
47259             
47260             var hasStyles = false;
47261             for(var i in this.styles) {
47262                 hasStyles = true;
47263                 break;
47264             }
47265             
47266             // update styles
47267             if (hasStyles) { 
47268                 var st = this.tb.fields.item(0);
47269                 
47270                 st.store.removeAll();
47271                
47272                 
47273                 var cn = sel.className.split(/\s+/);
47274                 
47275                 var avs = [];
47276                 if (this.styles['*']) {
47277                     
47278                     Roo.each(this.styles['*'], function(v) {
47279                         avs.push( [ v , cn.indexOf(v) > -1 ? 1 : 0 ] );         
47280                     });
47281                 }
47282                 if (this.styles[tn]) { 
47283                     Roo.each(this.styles[tn], function(v) {
47284                         avs.push( [ v , cn.indexOf(v) > -1 ? 1 : 0 ] );         
47285                     });
47286                 }
47287                 
47288                 st.store.loadData(avs);
47289                 st.collapse();
47290                 st.setValue(cn);
47291             }
47292             // flag our selected Node.
47293             this.tb.selectedNode = sel;
47294            
47295            
47296             Roo.menu.MenuMgr.hideAll();
47297
47298         }
47299         
47300         if (!updateFooter) {
47301             //this.footDisp.dom.innerHTML = ''; 
47302             return;
47303         }
47304         // update the footer
47305         //
47306         var html = '';
47307         
47308         this.footerEls = ans.reverse();
47309         Roo.each(this.footerEls, function(a,i) {
47310             if (!a) { return; }
47311             html += html.length ? ' &gt; '  :  '';
47312             
47313             html += '<span class="x-ed-loc-' + i + '">' + a.tagName + '</span>';
47314             
47315         });
47316        
47317         // 
47318         var sz = this.footDisp.up('td').getSize();
47319         this.footDisp.dom.style.width = (sz.width -10) + 'px';
47320         this.footDisp.dom.style.marginLeft = '5px';
47321         
47322         this.footDisp.dom.style.overflow = 'hidden';
47323         
47324         this.footDisp.dom.innerHTML = html;
47325             
47326         //this.editorsyncValue();
47327     },
47328      
47329     
47330    
47331        
47332     // private
47333     onDestroy : function(){
47334         if(this.rendered){
47335             
47336             this.tb.items.each(function(item){
47337                 if(item.menu){
47338                     item.menu.removeAll();
47339                     if(item.menu.el){
47340                         item.menu.el.destroy();
47341                     }
47342                 }
47343                 item.destroy();
47344             });
47345              
47346         }
47347     },
47348     onFirstFocus: function() {
47349         // need to do this for all the toolbars..
47350         this.tb.items.each(function(item){
47351            item.enable();
47352         });
47353     },
47354     buildToolbar: function(tlist, nm)
47355     {
47356         var editor = this.editor;
47357         var editorcore = this.editorcore;
47358          // create a new element.
47359         var wdiv = editor.wrap.createChild({
47360                 tag: 'div'
47361             }, editor.wrap.dom.firstChild.nextSibling, true);
47362         
47363        
47364         var tb = new Roo.Toolbar(wdiv);
47365         // add the name..
47366         
47367         tb.add(nm+ ":&nbsp;");
47368         
47369         var styles = [];
47370         for(var i in this.styles) {
47371             styles.push(i);
47372         }
47373         
47374         // styles...
47375         if (styles && styles.length) {
47376             
47377             // this needs a multi-select checkbox...
47378             tb.addField( new Roo.form.ComboBox({
47379                 store: new Roo.data.SimpleStore({
47380                     id : 'val',
47381                     fields: ['val', 'selected'],
47382                     data : [] 
47383                 }),
47384                 name : '-roo-edit-className',
47385                 attrname : 'className',
47386                 displayField: 'val',
47387                 typeAhead: false,
47388                 mode: 'local',
47389                 editable : false,
47390                 triggerAction: 'all',
47391                 emptyText:'Select Style',
47392                 selectOnFocus:true,
47393                 width: 130,
47394                 listeners : {
47395                     'select': function(c, r, i) {
47396                         // initial support only for on class per el..
47397                         tb.selectedNode.className =  r ? r.get('val') : '';
47398                         editorcore.syncValue();
47399                     }
47400                 }
47401     
47402             }));
47403         }
47404         
47405         var tbc = Roo.form.HtmlEditor.ToolbarContext;
47406         var tbops = tbc.options;
47407         
47408         for (var i in tlist) {
47409             
47410             var item = tlist[i];
47411             tb.add(item.title + ":&nbsp;");
47412             
47413             
47414             //optname == used so you can configure the options available..
47415             var opts = item.opts ? item.opts : false;
47416             if (item.optname) {
47417                 opts = tbops[item.optname];
47418            
47419             }
47420             
47421             if (opts) {
47422                 // opts == pulldown..
47423                 tb.addField( new Roo.form.ComboBox({
47424                     store:   typeof(tbc.stores[i]) != 'undefined' ?  Roo.factory(tbc.stores[i],Roo.data) : new Roo.data.SimpleStore({
47425                         id : 'val',
47426                         fields: ['val', 'display'],
47427                         data : opts  
47428                     }),
47429                     name : '-roo-edit-' + i,
47430                     attrname : i,
47431                     stylename : item.style ? item.style : false,
47432                     displayField: item.displayField ? item.displayField : 'val',
47433                     valueField :  'val',
47434                     typeAhead: false,
47435                     mode: typeof(tbc.stores[i]) != 'undefined'  ? 'remote' : 'local',
47436                     editable : false,
47437                     triggerAction: 'all',
47438                     emptyText:'Select',
47439                     selectOnFocus:true,
47440                     width: item.width ? item.width  : 130,
47441                     listeners : {
47442                         'select': function(c, r, i) {
47443                             if (c.stylename) {
47444                                 tb.selectedNode.style[c.stylename] =  r.get('val');
47445                                 return;
47446                             }
47447                             tb.selectedNode.setAttribute(c.attrname, r.get('val'));
47448                         }
47449                     }
47450
47451                 }));
47452                 continue;
47453                     
47454                  
47455                 
47456                 tb.addField( new Roo.form.TextField({
47457                     name: i,
47458                     width: 100,
47459                     //allowBlank:false,
47460                     value: ''
47461                 }));
47462                 continue;
47463             }
47464             tb.addField( new Roo.form.TextField({
47465                 name: '-roo-edit-' + i,
47466                 attrname : i,
47467                 
47468                 width: item.width,
47469                 //allowBlank:true,
47470                 value: '',
47471                 listeners: {
47472                     'change' : function(f, nv, ov) {
47473                         tb.selectedNode.setAttribute(f.attrname, nv);
47474                         editorcore.syncValue();
47475                     }
47476                 }
47477             }));
47478              
47479         }
47480         
47481         var _this = this;
47482         
47483         if(nm == 'BODY'){
47484             tb.addSeparator();
47485         
47486             tb.addButton( {
47487                 text: 'Stylesheets',
47488
47489                 listeners : {
47490                     click : function ()
47491                     {
47492                         _this.editor.fireEvent('stylesheetsclick', _this.editor);
47493                     }
47494                 }
47495             });
47496         }
47497         
47498         tb.addFill();
47499         tb.addButton( {
47500             text: 'Remove Tag',
47501     
47502             listeners : {
47503                 click : function ()
47504                 {
47505                     // remove
47506                     // undo does not work.
47507                      
47508                     var sn = tb.selectedNode;
47509                     
47510                     var pn = sn.parentNode;
47511                     
47512                     var stn =  sn.childNodes[0];
47513                     var en = sn.childNodes[sn.childNodes.length - 1 ];
47514                     while (sn.childNodes.length) {
47515                         var node = sn.childNodes[0];
47516                         sn.removeChild(node);
47517                         //Roo.log(node);
47518                         pn.insertBefore(node, sn);
47519                         
47520                     }
47521                     pn.removeChild(sn);
47522                     var range = editorcore.createRange();
47523         
47524                     range.setStart(stn,0);
47525                     range.setEnd(en,0); //????
47526                     //range.selectNode(sel);
47527                     
47528                     
47529                     var selection = editorcore.getSelection();
47530                     selection.removeAllRanges();
47531                     selection.addRange(range);
47532                     
47533                     
47534                     
47535                     //_this.updateToolbar(null, null, pn);
47536                     _this.updateToolbar(null, null, null);
47537                     _this.footDisp.dom.innerHTML = ''; 
47538                 }
47539             }
47540             
47541                     
47542                 
47543             
47544         });
47545         
47546         
47547         tb.el.on('click', function(e){
47548             e.preventDefault(); // what does this do?
47549         });
47550         tb.el.setVisibilityMode( Roo.Element.DISPLAY);
47551         tb.el.hide();
47552         tb.name = nm;
47553         // dont need to disable them... as they will get hidden
47554         return tb;
47555          
47556         
47557     },
47558     buildFooter : function()
47559     {
47560         
47561         var fel = this.editor.wrap.createChild();
47562         this.footer = new Roo.Toolbar(fel);
47563         // toolbar has scrolly on left / right?
47564         var footDisp= new Roo.Toolbar.Fill();
47565         var _t = this;
47566         this.footer.add(
47567             {
47568                 text : '&lt;',
47569                 xtype: 'Button',
47570                 handler : function() {
47571                     _t.footDisp.scrollTo('left',0,true)
47572                 }
47573             }
47574         );
47575         this.footer.add( footDisp );
47576         this.footer.add( 
47577             {
47578                 text : '&gt;',
47579                 xtype: 'Button',
47580                 handler : function() {
47581                     // no animation..
47582                     _t.footDisp.select('span').last().scrollIntoView(_t.footDisp,true);
47583                 }
47584             }
47585         );
47586         var fel = Roo.get(footDisp.el);
47587         fel.addClass('x-editor-context');
47588         this.footDispWrap = fel; 
47589         this.footDispWrap.overflow  = 'hidden';
47590         
47591         this.footDisp = fel.createChild();
47592         this.footDispWrap.on('click', this.onContextClick, this)
47593         
47594         
47595     },
47596     onContextClick : function (ev,dom)
47597     {
47598         ev.preventDefault();
47599         var  cn = dom.className;
47600         //Roo.log(cn);
47601         if (!cn.match(/x-ed-loc-/)) {
47602             return;
47603         }
47604         var n = cn.split('-').pop();
47605         var ans = this.footerEls;
47606         var sel = ans[n];
47607         
47608          // pick
47609         var range = this.editorcore.createRange();
47610         
47611         range.selectNodeContents(sel);
47612         //range.selectNode(sel);
47613         
47614         
47615         var selection = this.editorcore.getSelection();
47616         selection.removeAllRanges();
47617         selection.addRange(range);
47618         
47619         
47620         
47621         this.updateToolbar(null, null, sel);
47622         
47623         
47624     }
47625     
47626     
47627     
47628     
47629     
47630 });
47631
47632
47633
47634
47635
47636 /*
47637  * Based on:
47638  * Ext JS Library 1.1.1
47639  * Copyright(c) 2006-2007, Ext JS, LLC.
47640  *
47641  * Originally Released Under LGPL - original licence link has changed is not relivant.
47642  *
47643  * Fork - LGPL
47644  * <script type="text/javascript">
47645  */
47646  
47647 /**
47648  * @class Roo.form.BasicForm
47649  * @extends Roo.util.Observable
47650  * Supplies the functionality to do "actions" on forms and initialize Roo.form.Field types on existing markup.
47651  * @constructor
47652  * @param {String/HTMLElement/Roo.Element} el The form element or its id
47653  * @param {Object} config Configuration options
47654  */
47655 Roo.form.BasicForm = function(el, config){
47656     this.allItems = [];
47657     this.childForms = [];
47658     Roo.apply(this, config);
47659     /*
47660      * The Roo.form.Field items in this form.
47661      * @type MixedCollection
47662      */
47663      
47664      
47665     this.items = new Roo.util.MixedCollection(false, function(o){
47666         return o.id || (o.id = Roo.id());
47667     });
47668     this.addEvents({
47669         /**
47670          * @event beforeaction
47671          * Fires before any action is performed. Return false to cancel the action.
47672          * @param {Form} this
47673          * @param {Action} action The action to be performed
47674          */
47675         beforeaction: true,
47676         /**
47677          * @event actionfailed
47678          * Fires when an action fails.
47679          * @param {Form} this
47680          * @param {Action} action The action that failed
47681          */
47682         actionfailed : true,
47683         /**
47684          * @event actioncomplete
47685          * Fires when an action is completed.
47686          * @param {Form} this
47687          * @param {Action} action The action that completed
47688          */
47689         actioncomplete : true
47690     });
47691     if(el){
47692         this.initEl(el);
47693     }
47694     Roo.form.BasicForm.superclass.constructor.call(this);
47695     
47696     Roo.form.BasicForm.popover.apply();
47697 };
47698
47699 Roo.extend(Roo.form.BasicForm, Roo.util.Observable, {
47700     /**
47701      * @cfg {String} method
47702      * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
47703      */
47704     /**
47705      * @cfg {DataReader} reader
47706      * An Roo.data.DataReader (e.g. {@link Roo.data.XmlReader}) to be used to read data when executing "load" actions.
47707      * This is optional as there is built-in support for processing JSON.
47708      */
47709     /**
47710      * @cfg {DataReader} errorReader
47711      * An Roo.data.DataReader (e.g. {@link Roo.data.XmlReader}) to be used to read data when reading validation errors on "submit" actions.
47712      * This is completely optional as there is built-in support for processing JSON.
47713      */
47714     /**
47715      * @cfg {String} url
47716      * The URL to use for form actions if one isn't supplied in the action options.
47717      */
47718     /**
47719      * @cfg {Boolean} fileUpload
47720      * Set to true if this form is a file upload.
47721      */
47722      
47723     /**
47724      * @cfg {Object} baseParams
47725      * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
47726      */
47727      /**
47728      
47729     /**
47730      * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
47731      */
47732     timeout: 30,
47733
47734     // private
47735     activeAction : null,
47736
47737     /**
47738      * @cfg {Boolean} trackResetOnLoad If set to true, form.reset() resets to the last loaded
47739      * or setValues() data instead of when the form was first created.
47740      */
47741     trackResetOnLoad : false,
47742     
47743     
47744     /**
47745      * childForms - used for multi-tab forms
47746      * @type {Array}
47747      */
47748     childForms : false,
47749     
47750     /**
47751      * allItems - full list of fields.
47752      * @type {Array}
47753      */
47754     allItems : false,
47755     
47756     /**
47757      * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
47758      * element by passing it or its id or mask the form itself by passing in true.
47759      * @type Mixed
47760      */
47761     waitMsgTarget : false,
47762     
47763     /**
47764      * @type Boolean
47765      */
47766     disableMask : false,
47767     
47768     /**
47769      * @cfg {Boolean} errorMask (true|false) default false
47770      */
47771     errorMask : false,
47772     
47773     /**
47774      * @cfg {Number} maskOffset Default 100
47775      */
47776     maskOffset : 100,
47777
47778     // private
47779     initEl : function(el){
47780         this.el = Roo.get(el);
47781         this.id = this.el.id || Roo.id();
47782         this.el.on('submit', this.onSubmit, this);
47783         this.el.addClass('x-form');
47784     },
47785
47786     // private
47787     onSubmit : function(e){
47788         e.stopEvent();
47789     },
47790
47791     /**
47792      * Returns true if client-side validation on the form is successful.
47793      * @return Boolean
47794      */
47795     isValid : function(){
47796         var valid = true;
47797         var target = false;
47798         this.items.each(function(f){
47799             if(f.validate()){
47800                 return;
47801             }
47802             
47803             valid = false;
47804                 
47805             if(!target && f.el.isVisible(true)){
47806                 target = f;
47807             }
47808         });
47809         
47810         if(this.errorMask && !valid){
47811             Roo.form.BasicForm.popover.mask(this, target);
47812         }
47813         
47814         return valid;
47815     },
47816     /**
47817      * Returns array of invalid form fields.
47818      * @return Array
47819      */
47820     
47821     invalidFields : function()
47822     {
47823         var ret = [];
47824         this.items.each(function(f){
47825             if(f.validate()){
47826                 return;
47827             }
47828             ret.push(f);
47829             
47830         });
47831         
47832         return ret;
47833     },
47834     
47835     
47836     /**
47837      * DEPRICATED Returns true if any fields in this form have changed since their original load. 
47838      * @return Boolean
47839      */
47840     isDirty : function(){
47841         var dirty = false;
47842         this.items.each(function(f){
47843            if(f.isDirty()){
47844                dirty = true;
47845                return false;
47846            }
47847         });
47848         return dirty;
47849     },
47850     
47851     /**
47852      * Returns true if any fields in this form have changed since their original load. (New version)
47853      * @return Boolean
47854      */
47855     
47856     hasChanged : function()
47857     {
47858         var dirty = false;
47859         this.items.each(function(f){
47860            if(f.hasChanged()){
47861                dirty = true;
47862                return false;
47863            }
47864         });
47865         return dirty;
47866         
47867     },
47868     /**
47869      * Resets all hasChanged to 'false' -
47870      * The old 'isDirty' used 'original value..' however this breaks reset() and a few other things.
47871      * So hasChanged storage is only to be used for this purpose
47872      * @return Boolean
47873      */
47874     resetHasChanged : function()
47875     {
47876         this.items.each(function(f){
47877            f.resetHasChanged();
47878         });
47879         
47880     },
47881     
47882     
47883     /**
47884      * Performs a predefined action (submit or load) or custom actions you define on this form.
47885      * @param {String} actionName The name of the action type
47886      * @param {Object} options (optional) The options to pass to the action.  All of the config options listed
47887      * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
47888      * accept other config options):
47889      * <pre>
47890 Property          Type             Description
47891 ----------------  ---------------  ----------------------------------------------------------------------------------
47892 url               String           The url for the action (defaults to the form's url)
47893 method            String           The form method to use (defaults to the form's method, or POST if not defined)
47894 params            String/Object    The params to pass (defaults to the form's baseParams, or none if not defined)
47895 clientValidation  Boolean          Applies to submit only.  Pass true to call form.isValid() prior to posting to
47896                                    validate the form on the client (defaults to false)
47897      * </pre>
47898      * @return {BasicForm} this
47899      */
47900     doAction : function(action, options){
47901         if(typeof action == 'string'){
47902             action = new Roo.form.Action.ACTION_TYPES[action](this, options);
47903         }
47904         if(this.fireEvent('beforeaction', this, action) !== false){
47905             this.beforeAction(action);
47906             action.run.defer(100, action);
47907         }
47908         return this;
47909     },
47910
47911     /**
47912      * Shortcut to do a submit action.
47913      * @param {Object} options The options to pass to the action (see {@link #doAction} for details)
47914      * @return {BasicForm} this
47915      */
47916     submit : function(options){
47917         this.doAction('submit', options);
47918         return this;
47919     },
47920
47921     /**
47922      * Shortcut to do a load action.
47923      * @param {Object} options The options to pass to the action (see {@link #doAction} for details)
47924      * @return {BasicForm} this
47925      */
47926     load : function(options){
47927         this.doAction('load', options);
47928         return this;
47929     },
47930
47931     /**
47932      * Persists the values in this form into the passed Roo.data.Record object in a beginEdit/endEdit block.
47933      * @param {Record} record The record to edit
47934      * @return {BasicForm} this
47935      */
47936     updateRecord : function(record){
47937         record.beginEdit();
47938         var fs = record.fields;
47939         fs.each(function(f){
47940             var field = this.findField(f.name);
47941             if(field){
47942                 record.set(f.name, field.getValue());
47943             }
47944         }, this);
47945         record.endEdit();
47946         return this;
47947     },
47948
47949     /**
47950      * Loads an Roo.data.Record into this form.
47951      * @param {Record} record The record to load
47952      * @return {BasicForm} this
47953      */
47954     loadRecord : function(record){
47955         this.setValues(record.data);
47956         return this;
47957     },
47958
47959     // private
47960     beforeAction : function(action){
47961         var o = action.options;
47962         
47963         if(!this.disableMask) {
47964             if(this.waitMsgTarget === true){
47965                 this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
47966             }else if(this.waitMsgTarget){
47967                 this.waitMsgTarget = Roo.get(this.waitMsgTarget);
47968                 this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
47969             }else {
47970                 Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
47971             }
47972         }
47973         
47974          
47975     },
47976
47977     // private
47978     afterAction : function(action, success){
47979         this.activeAction = null;
47980         var o = action.options;
47981         
47982         if(!this.disableMask) {
47983             if(this.waitMsgTarget === true){
47984                 this.el.unmask();
47985             }else if(this.waitMsgTarget){
47986                 this.waitMsgTarget.unmask();
47987             }else{
47988                 Roo.MessageBox.updateProgress(1);
47989                 Roo.MessageBox.hide();
47990             }
47991         }
47992         
47993         if(success){
47994             if(o.reset){
47995                 this.reset();
47996             }
47997             Roo.callback(o.success, o.scope, [this, action]);
47998             this.fireEvent('actioncomplete', this, action);
47999             
48000         }else{
48001             
48002             // failure condition..
48003             // we have a scenario where updates need confirming.
48004             // eg. if a locking scenario exists..
48005             // we look for { errors : { needs_confirm : true }} in the response.
48006             if (
48007                 (typeof(action.result) != 'undefined')  &&
48008                 (typeof(action.result.errors) != 'undefined')  &&
48009                 (typeof(action.result.errors.needs_confirm) != 'undefined')
48010            ){
48011                 var _t = this;
48012                 Roo.MessageBox.confirm(
48013                     "Change requires confirmation",
48014                     action.result.errorMsg,
48015                     function(r) {
48016                         if (r != 'yes') {
48017                             return;
48018                         }
48019                         _t.doAction('submit', { params :  { _submit_confirmed : 1 } }  );
48020                     }
48021                     
48022                 );
48023                 
48024                 
48025                 
48026                 return;
48027             }
48028             
48029             Roo.callback(o.failure, o.scope, [this, action]);
48030             // show an error message if no failed handler is set..
48031             if (!this.hasListener('actionfailed')) {
48032                 Roo.MessageBox.alert("Error",
48033                     (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
48034                         action.result.errorMsg :
48035                         "Saving Failed, please check your entries or try again"
48036                 );
48037             }
48038             
48039             this.fireEvent('actionfailed', this, action);
48040         }
48041         
48042     },
48043
48044     /**
48045      * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
48046      * @param {String} id The value to search for
48047      * @return Field
48048      */
48049     findField : function(id){
48050         var field = this.items.get(id);
48051         if(!field){
48052             this.items.each(function(f){
48053                 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
48054                     field = f;
48055                     return false;
48056                 }
48057             });
48058         }
48059         return field || null;
48060     },
48061
48062     /**
48063      * Add a secondary form to this one, 
48064      * Used to provide tabbed forms. One form is primary, with hidden values 
48065      * which mirror the elements from the other forms.
48066      * 
48067      * @param {Roo.form.Form} form to add.
48068      * 
48069      */
48070     addForm : function(form)
48071     {
48072        
48073         if (this.childForms.indexOf(form) > -1) {
48074             // already added..
48075             return;
48076         }
48077         this.childForms.push(form);
48078         var n = '';
48079         Roo.each(form.allItems, function (fe) {
48080             
48081             n = typeof(fe.getName) == 'undefined' ? fe.name : fe.getName();
48082             if (this.findField(n)) { // already added..
48083                 return;
48084             }
48085             var add = new Roo.form.Hidden({
48086                 name : n
48087             });
48088             add.render(this.el);
48089             
48090             this.add( add );
48091         }, this);
48092         
48093     },
48094     /**
48095      * Mark fields in this form invalid in bulk.
48096      * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
48097      * @return {BasicForm} this
48098      */
48099     markInvalid : function(errors){
48100         if(errors instanceof Array){
48101             for(var i = 0, len = errors.length; i < len; i++){
48102                 var fieldError = errors[i];
48103                 var f = this.findField(fieldError.id);
48104                 if(f){
48105                     f.markInvalid(fieldError.msg);
48106                 }
48107             }
48108         }else{
48109             var field, id;
48110             for(id in errors){
48111                 if(typeof errors[id] != 'function' && (field = this.findField(id))){
48112                     field.markInvalid(errors[id]);
48113                 }
48114             }
48115         }
48116         Roo.each(this.childForms || [], function (f) {
48117             f.markInvalid(errors);
48118         });
48119         
48120         return this;
48121     },
48122
48123     /**
48124      * Set values for fields in this form in bulk.
48125      * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
48126      * @return {BasicForm} this
48127      */
48128     setValues : function(values){
48129         if(values instanceof Array){ // array of objects
48130             for(var i = 0, len = values.length; i < len; i++){
48131                 var v = values[i];
48132                 var f = this.findField(v.id);
48133                 if(f){
48134                     f.setValue(v.value);
48135                     if(this.trackResetOnLoad){
48136                         f.originalValue = f.getValue();
48137                     }
48138                 }
48139             }
48140         }else{ // object hash
48141             var field, id;
48142             for(id in values){
48143                 if(typeof values[id] != 'function' && (field = this.findField(id))){
48144                     
48145                     if (field.setFromData && 
48146                         field.valueField && 
48147                         field.displayField &&
48148                         // combos' with local stores can 
48149                         // be queried via setValue()
48150                         // to set their value..
48151                         (field.store && !field.store.isLocal)
48152                         ) {
48153                         // it's a combo
48154                         var sd = { };
48155                         sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
48156                         sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
48157                         field.setFromData(sd);
48158                         
48159                     } else {
48160                         field.setValue(values[id]);
48161                     }
48162                     
48163                     
48164                     if(this.trackResetOnLoad){
48165                         field.originalValue = field.getValue();
48166                     }
48167                 }
48168             }
48169         }
48170         this.resetHasChanged();
48171         
48172         
48173         Roo.each(this.childForms || [], function (f) {
48174             f.setValues(values);
48175             f.resetHasChanged();
48176         });
48177                 
48178         return this;
48179     },
48180  
48181     /**
48182      * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
48183      * they are returned as an array.
48184      * @param {Boolean} asString
48185      * @return {Object}
48186      */
48187     getValues : function(asString){
48188         if (this.childForms) {
48189             // copy values from the child forms
48190             Roo.each(this.childForms, function (f) {
48191                 this.setValues(f.getValues());
48192             }, this);
48193         }
48194         
48195         // use formdata
48196         if (typeof(FormData) != 'undefined' && asString !== true) {
48197             // this relies on a 'recent' version of chrome apparently...
48198             try {
48199                 var fd = (new FormData(this.el.dom)).entries();
48200                 var ret = {};
48201                 var ent = fd.next();
48202                 while (!ent.done) {
48203                     ret[ent.value[0]] = ent.value[1]; // not sure how this will handle duplicates..
48204                     ent = fd.next();
48205                 };
48206                 return ret;
48207             } catch(e) {
48208                 
48209             }
48210             
48211         }
48212         
48213         
48214         var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
48215         if(asString === true){
48216             return fs;
48217         }
48218         return Roo.urlDecode(fs);
48219     },
48220     
48221     /**
48222      * Returns the fields in this form as an object with key/value pairs. 
48223      * This differs from getValues as it calls getValue on each child item, rather than using dom data.
48224      * @return {Object}
48225      */
48226     getFieldValues : function(with_hidden)
48227     {
48228         if (this.childForms) {
48229             // copy values from the child forms
48230             // should this call getFieldValues - probably not as we do not currently copy
48231             // hidden fields when we generate..
48232             Roo.each(this.childForms, function (f) {
48233                 this.setValues(f.getValues());
48234             }, this);
48235         }
48236         
48237         var ret = {};
48238         this.items.each(function(f){
48239             if (!f.getName()) {
48240                 return;
48241             }
48242             var v = f.getValue();
48243             if (f.inputType =='radio') {
48244                 if (typeof(ret[f.getName()]) == 'undefined') {
48245                     ret[f.getName()] = ''; // empty..
48246                 }
48247                 
48248                 if (!f.el.dom.checked) {
48249                     return;
48250                     
48251                 }
48252                 v = f.el.dom.value;
48253                 
48254             }
48255             
48256             // not sure if this supported any more..
48257             if ((typeof(v) == 'object') && f.getRawValue) {
48258                 v = f.getRawValue() ; // dates..
48259             }
48260             // combo boxes where name != hiddenName...
48261             if (f.name != f.getName()) {
48262                 ret[f.name] = f.getRawValue();
48263             }
48264             ret[f.getName()] = v;
48265         });
48266         
48267         return ret;
48268     },
48269
48270     /**
48271      * Clears all invalid messages in this form.
48272      * @return {BasicForm} this
48273      */
48274     clearInvalid : function(){
48275         this.items.each(function(f){
48276            f.clearInvalid();
48277         });
48278         
48279         Roo.each(this.childForms || [], function (f) {
48280             f.clearInvalid();
48281         });
48282         
48283         
48284         return this;
48285     },
48286
48287     /**
48288      * Resets this form.
48289      * @return {BasicForm} this
48290      */
48291     reset : function(){
48292         this.items.each(function(f){
48293             f.reset();
48294         });
48295         
48296         Roo.each(this.childForms || [], function (f) {
48297             f.reset();
48298         });
48299         this.resetHasChanged();
48300         
48301         return this;
48302     },
48303
48304     /**
48305      * Add Roo.form components to this form.
48306      * @param {Field} field1
48307      * @param {Field} field2 (optional)
48308      * @param {Field} etc (optional)
48309      * @return {BasicForm} this
48310      */
48311     add : function(){
48312         this.items.addAll(Array.prototype.slice.call(arguments, 0));
48313         return this;
48314     },
48315
48316
48317     /**
48318      * Removes a field from the items collection (does NOT remove its markup).
48319      * @param {Field} field
48320      * @return {BasicForm} this
48321      */
48322     remove : function(field){
48323         this.items.remove(field);
48324         return this;
48325     },
48326
48327     /**
48328      * Looks at the fields in this form, checks them for an id attribute,
48329      * and calls applyTo on the existing dom element with that id.
48330      * @return {BasicForm} this
48331      */
48332     render : function(){
48333         this.items.each(function(f){
48334             if(f.isFormField && !f.rendered && document.getElementById(f.id)){ // if the element exists
48335                 f.applyTo(f.id);
48336             }
48337         });
48338         return this;
48339     },
48340
48341     /**
48342      * Calls {@link Ext#apply} for all fields in this form with the passed object.
48343      * @param {Object} values
48344      * @return {BasicForm} this
48345      */
48346     applyToFields : function(o){
48347         this.items.each(function(f){
48348            Roo.apply(f, o);
48349         });
48350         return this;
48351     },
48352
48353     /**
48354      * Calls {@link Ext#applyIf} for all field in this form with the passed object.
48355      * @param {Object} values
48356      * @return {BasicForm} this
48357      */
48358     applyIfToFields : function(o){
48359         this.items.each(function(f){
48360            Roo.applyIf(f, o);
48361         });
48362         return this;
48363     }
48364 });
48365
48366 // back compat
48367 Roo.BasicForm = Roo.form.BasicForm;
48368
48369 Roo.apply(Roo.form.BasicForm, {
48370     
48371     popover : {
48372         
48373         padding : 5,
48374         
48375         isApplied : false,
48376         
48377         isMasked : false,
48378         
48379         form : false,
48380         
48381         target : false,
48382         
48383         intervalID : false,
48384         
48385         maskEl : false,
48386         
48387         apply : function()
48388         {
48389             if(this.isApplied){
48390                 return;
48391             }
48392             
48393             this.maskEl = {
48394                 top : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-top-mask" }, true),
48395                 left : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-left-mask" }, true),
48396                 bottom : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-bottom-mask" }, true),
48397                 right : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-right-mask" }, true)
48398             };
48399             
48400             this.maskEl.top.enableDisplayMode("block");
48401             this.maskEl.left.enableDisplayMode("block");
48402             this.maskEl.bottom.enableDisplayMode("block");
48403             this.maskEl.right.enableDisplayMode("block");
48404             
48405             Roo.get(document.body).on('click', function(){
48406                 this.unmask();
48407             }, this);
48408             
48409             Roo.get(document.body).on('touchstart', function(){
48410                 this.unmask();
48411             }, this);
48412             
48413             this.isApplied = true
48414         },
48415         
48416         mask : function(form, target)
48417         {
48418             this.form = form;
48419             
48420             this.target = target;
48421             
48422             if(!this.form.errorMask || !target.el){
48423                 return;
48424             }
48425             
48426             var scrollable = this.target.el.findScrollableParent() || this.target.el.findParent('div.x-layout-active-content', 100, true) || Roo.get(document.body);
48427             
48428             var ot = this.target.el.calcOffsetsTo(scrollable);
48429             
48430             var scrollTo = ot[1] - this.form.maskOffset;
48431             
48432             scrollTo = Math.min(scrollTo, scrollable.dom.scrollHeight);
48433             
48434             scrollable.scrollTo('top', scrollTo);
48435             
48436             var el = this.target.wrap || this.target.el;
48437             
48438             var box = el.getBox();
48439             
48440             this.maskEl.top.setStyle('position', 'absolute');
48441             this.maskEl.top.setStyle('z-index', 10000);
48442             this.maskEl.top.setSize(Roo.lib.Dom.getDocumentWidth(), box.y - this.padding);
48443             this.maskEl.top.setLeft(0);
48444             this.maskEl.top.setTop(0);
48445             this.maskEl.top.show();
48446             
48447             this.maskEl.left.setStyle('position', 'absolute');
48448             this.maskEl.left.setStyle('z-index', 10000);
48449             this.maskEl.left.setSize(box.x - this.padding, box.height + this.padding * 2);
48450             this.maskEl.left.setLeft(0);
48451             this.maskEl.left.setTop(box.y - this.padding);
48452             this.maskEl.left.show();
48453
48454             this.maskEl.bottom.setStyle('position', 'absolute');
48455             this.maskEl.bottom.setStyle('z-index', 10000);
48456             this.maskEl.bottom.setSize(Roo.lib.Dom.getDocumentWidth(), Roo.lib.Dom.getDocumentHeight() - box.bottom - this.padding);
48457             this.maskEl.bottom.setLeft(0);
48458             this.maskEl.bottom.setTop(box.bottom + this.padding);
48459             this.maskEl.bottom.show();
48460
48461             this.maskEl.right.setStyle('position', 'absolute');
48462             this.maskEl.right.setStyle('z-index', 10000);
48463             this.maskEl.right.setSize(Roo.lib.Dom.getDocumentWidth() - box.right - this.padding, box.height + this.padding * 2);
48464             this.maskEl.right.setLeft(box.right + this.padding);
48465             this.maskEl.right.setTop(box.y - this.padding);
48466             this.maskEl.right.show();
48467
48468             this.intervalID = window.setInterval(function() {
48469                 Roo.form.BasicForm.popover.unmask();
48470             }, 10000);
48471
48472             window.onwheel = function(){ return false;};
48473             
48474             (function(){ this.isMasked = true; }).defer(500, this);
48475             
48476         },
48477         
48478         unmask : function()
48479         {
48480             if(!this.isApplied || !this.isMasked || !this.form || !this.target || !this.form.errorMask){
48481                 return;
48482             }
48483             
48484             this.maskEl.top.setStyle('position', 'absolute');
48485             this.maskEl.top.setSize(0, 0).setXY([0, 0]);
48486             this.maskEl.top.hide();
48487
48488             this.maskEl.left.setStyle('position', 'absolute');
48489             this.maskEl.left.setSize(0, 0).setXY([0, 0]);
48490             this.maskEl.left.hide();
48491
48492             this.maskEl.bottom.setStyle('position', 'absolute');
48493             this.maskEl.bottom.setSize(0, 0).setXY([0, 0]);
48494             this.maskEl.bottom.hide();
48495
48496             this.maskEl.right.setStyle('position', 'absolute');
48497             this.maskEl.right.setSize(0, 0).setXY([0, 0]);
48498             this.maskEl.right.hide();
48499             
48500             window.onwheel = function(){ return true;};
48501             
48502             if(this.intervalID){
48503                 window.clearInterval(this.intervalID);
48504                 this.intervalID = false;
48505             }
48506             
48507             this.isMasked = false;
48508             
48509         }
48510         
48511     }
48512     
48513 });/*
48514  * Based on:
48515  * Ext JS Library 1.1.1
48516  * Copyright(c) 2006-2007, Ext JS, LLC.
48517  *
48518  * Originally Released Under LGPL - original licence link has changed is not relivant.
48519  *
48520  * Fork - LGPL
48521  * <script type="text/javascript">
48522  */
48523
48524 /**
48525  * @class Roo.form.Form
48526  * @extends Roo.form.BasicForm
48527  * @children Roo.form.Column Roo.form.FieldSet Roo.form.Row Roo.form.Field Roo.Button Roo.form.TextItem
48528  * Adds the ability to dynamically render forms with JavaScript to {@link Roo.form.BasicForm}.
48529  * @constructor
48530  * @param {Object} config Configuration options
48531  */
48532 Roo.form.Form = function(config){
48533     var xitems =  [];
48534     if (config.items) {
48535         xitems = config.items;
48536         delete config.items;
48537     }
48538    
48539     
48540     Roo.form.Form.superclass.constructor.call(this, null, config);
48541     this.url = this.url || this.action;
48542     if(!this.root){
48543         this.root = new Roo.form.Layout(Roo.applyIf({
48544             id: Roo.id()
48545         }, config));
48546     }
48547     this.active = this.root;
48548     /**
48549      * Array of all the buttons that have been added to this form via {@link addButton}
48550      * @type Array
48551      */
48552     this.buttons = [];
48553     this.allItems = [];
48554     this.addEvents({
48555         /**
48556          * @event clientvalidation
48557          * If the monitorValid config option is true, this event fires repetitively to notify of valid state
48558          * @param {Form} this
48559          * @param {Boolean} valid true if the form has passed client-side validation
48560          */
48561         clientvalidation: true,
48562         /**
48563          * @event rendered
48564          * Fires when the form is rendered
48565          * @param {Roo.form.Form} form
48566          */
48567         rendered : true
48568     });
48569     
48570     if (this.progressUrl) {
48571             // push a hidden field onto the list of fields..
48572             this.addxtype( {
48573                     xns: Roo.form, 
48574                     xtype : 'Hidden', 
48575                     name : 'UPLOAD_IDENTIFIER' 
48576             });
48577         }
48578         
48579     
48580     Roo.each(xitems, this.addxtype, this);
48581     
48582 };
48583
48584 Roo.extend(Roo.form.Form, Roo.form.BasicForm, {
48585      /**
48586      * @cfg {Roo.Button} buttons[] buttons at bottom of form
48587      */
48588     
48589     /**
48590      * @cfg {Number} labelWidth The width of labels. This property cascades to child containers.
48591      */
48592     /**
48593      * @cfg {String} itemCls A css class to apply to the x-form-item of fields. This property cascades to child containers.
48594      */
48595     /**
48596      * @cfg {String} buttonAlign Valid values are "left," "center" and "right" (defaults to "center")
48597      */
48598     buttonAlign:'center',
48599
48600     /**
48601      * @cfg {Number} minButtonWidth Minimum width of all buttons in pixels (defaults to 75)
48602      */
48603     minButtonWidth:75,
48604
48605     /**
48606      * @cfg {String} labelAlign Valid values are "left," "top" and "right" (defaults to "left").
48607      * This property cascades to child containers if not set.
48608      */
48609     labelAlign:'left',
48610
48611     /**
48612      * @cfg {Boolean} monitorValid If true the form monitors its valid state <b>client-side</b> and
48613      * fires a looping event with that state. This is required to bind buttons to the valid
48614      * state using the config value formBind:true on the button.
48615      */
48616     monitorValid : false,
48617
48618     /**
48619      * @cfg {Number} monitorPoll The milliseconds to poll valid state, ignored if monitorValid is not true (defaults to 200)
48620      */
48621     monitorPoll : 200,
48622     
48623     /**
48624      * @cfg {String} progressUrl - Url to return progress data 
48625      */
48626     
48627     progressUrl : false,
48628     /**
48629      * @cfg {boolean|FormData} formData - true to use new 'FormData' post, or set to a new FormData({dom form}) Object, if
48630      * sending a formdata with extra parameters - eg uploaded elements.
48631      */
48632     
48633     formData : false,
48634     
48635     /**
48636      * Opens a new {@link Roo.form.Column} container in the layout stack. If fields are passed after the config, the
48637      * fields are added and the column is closed. If no fields are passed the column remains open
48638      * until end() is called.
48639      * @param {Object} config The config to pass to the column
48640      * @param {Field} field1 (optional)
48641      * @param {Field} field2 (optional)
48642      * @param {Field} etc (optional)
48643      * @return Column The column container object
48644      */
48645     column : function(c){
48646         var col = new Roo.form.Column(c);
48647         this.start(col);
48648         if(arguments.length > 1){ // duplicate code required because of Opera
48649             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
48650             this.end();
48651         }
48652         return col;
48653     },
48654
48655     /**
48656      * Opens a new {@link Roo.form.FieldSet} container in the layout stack. If fields are passed after the config, the
48657      * fields are added and the fieldset is closed. If no fields are passed the fieldset remains open
48658      * until end() is called.
48659      * @param {Object} config The config to pass to the fieldset
48660      * @param {Field} field1 (optional)
48661      * @param {Field} field2 (optional)
48662      * @param {Field} etc (optional)
48663      * @return FieldSet The fieldset container object
48664      */
48665     fieldset : function(c){
48666         var fs = new Roo.form.FieldSet(c);
48667         this.start(fs);
48668         if(arguments.length > 1){ // duplicate code required because of Opera
48669             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
48670             this.end();
48671         }
48672         return fs;
48673     },
48674
48675     /**
48676      * Opens a new {@link Roo.form.Layout} container in the layout stack. If fields are passed after the config, the
48677      * fields are added and the container is closed. If no fields are passed the container remains open
48678      * until end() is called.
48679      * @param {Object} config The config to pass to the Layout
48680      * @param {Field} field1 (optional)
48681      * @param {Field} field2 (optional)
48682      * @param {Field} etc (optional)
48683      * @return Layout The container object
48684      */
48685     container : function(c){
48686         var l = new Roo.form.Layout(c);
48687         this.start(l);
48688         if(arguments.length > 1){ // duplicate code required because of Opera
48689             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
48690             this.end();
48691         }
48692         return l;
48693     },
48694
48695     /**
48696      * Opens the passed container in the layout stack. The container can be any {@link Roo.form.Layout} or subclass.
48697      * @param {Object} container A Roo.form.Layout or subclass of Layout
48698      * @return {Form} this
48699      */
48700     start : function(c){
48701         // cascade label info
48702         Roo.applyIf(c, {'labelAlign': this.active.labelAlign, 'labelWidth': this.active.labelWidth, 'itemCls': this.active.itemCls});
48703         this.active.stack.push(c);
48704         c.ownerCt = this.active;
48705         this.active = c;
48706         return this;
48707     },
48708
48709     /**
48710      * Closes the current open container
48711      * @return {Form} this
48712      */
48713     end : function(){
48714         if(this.active == this.root){
48715             return this;
48716         }
48717         this.active = this.active.ownerCt;
48718         return this;
48719     },
48720
48721     /**
48722      * Add Roo.form components to the current open container (e.g. column, fieldset, etc.).  Fields added via this method
48723      * can also be passed with an additional property of fieldLabel, which if supplied, will provide the text to display
48724      * as the label of the field.
48725      * @param {Field} field1
48726      * @param {Field} field2 (optional)
48727      * @param {Field} etc. (optional)
48728      * @return {Form} this
48729      */
48730     add : function(){
48731         this.active.stack.push.apply(this.active.stack, arguments);
48732         this.allItems.push.apply(this.allItems,arguments);
48733         var r = [];
48734         for(var i = 0, a = arguments, len = a.length; i < len; i++) {
48735             if(a[i].isFormField){
48736                 r.push(a[i]);
48737             }
48738         }
48739         if(r.length > 0){
48740             Roo.form.Form.superclass.add.apply(this, r);
48741         }
48742         return this;
48743     },
48744     
48745
48746     
48747     
48748     
48749      /**
48750      * Find any element that has been added to a form, using it's ID or name
48751      * This can include framesets, columns etc. along with regular fields..
48752      * @param {String} id - id or name to find.
48753      
48754      * @return {Element} e - or false if nothing found.
48755      */
48756     findbyId : function(id)
48757     {
48758         var ret = false;
48759         if (!id) {
48760             return ret;
48761         }
48762         Roo.each(this.allItems, function(f){
48763             if (f.id == id || f.name == id ){
48764                 ret = f;
48765                 return false;
48766             }
48767         });
48768         return ret;
48769     },
48770
48771     
48772     
48773     /**
48774      * Render this form into the passed container. This should only be called once!
48775      * @param {String/HTMLElement/Element} container The element this component should be rendered into
48776      * @return {Form} this
48777      */
48778     render : function(ct)
48779     {
48780         
48781         
48782         
48783         ct = Roo.get(ct);
48784         var o = this.autoCreate || {
48785             tag: 'form',
48786             method : this.method || 'POST',
48787             id : this.id || Roo.id()
48788         };
48789         this.initEl(ct.createChild(o));
48790
48791         this.root.render(this.el);
48792         
48793        
48794              
48795         this.items.each(function(f){
48796             f.render('x-form-el-'+f.id);
48797         });
48798
48799         if(this.buttons.length > 0){
48800             // tables are required to maintain order and for correct IE layout
48801             var tb = this.el.createChild({cls:'x-form-btns-ct', cn: {
48802                 cls:"x-form-btns x-form-btns-"+this.buttonAlign,
48803                 html:'<table cellspacing="0"><tbody><tr></tr></tbody></table><div class="x-clear"></div>'
48804             }}, null, true);
48805             var tr = tb.getElementsByTagName('tr')[0];
48806             for(var i = 0, len = this.buttons.length; i < len; i++) {
48807                 var b = this.buttons[i];
48808                 var td = document.createElement('td');
48809                 td.className = 'x-form-btn-td';
48810                 b.render(tr.appendChild(td));
48811             }
48812         }
48813         if(this.monitorValid){ // initialize after render
48814             this.startMonitoring();
48815         }
48816         this.fireEvent('rendered', this);
48817         return this;
48818     },
48819
48820     /**
48821      * Adds a button to the footer of the form - this <b>must</b> be called before the form is rendered.
48822      * @param {String/Object} config A string becomes the button text, an object can either be a Button config
48823      * object or a valid Roo.DomHelper element config
48824      * @param {Function} handler The function called when the button is clicked
48825      * @param {Object} scope (optional) The scope of the handler function
48826      * @return {Roo.Button}
48827      */
48828     addButton : function(config, handler, scope){
48829         var bc = {
48830             handler: handler,
48831             scope: scope,
48832             minWidth: this.minButtonWidth,
48833             hideParent:true
48834         };
48835         if(typeof config == "string"){
48836             bc.text = config;
48837         }else{
48838             Roo.apply(bc, config);
48839         }
48840         var btn = new Roo.Button(null, bc);
48841         this.buttons.push(btn);
48842         return btn;
48843     },
48844
48845      /**
48846      * Adds a series of form elements (using the xtype property as the factory method.
48847      * Valid xtypes are:  TextField, TextArea .... Button, Layout, FieldSet, Column, (and 'end' to close a block)
48848      * @param {Object} config 
48849      */
48850     
48851     addxtype : function()
48852     {
48853         var ar = Array.prototype.slice.call(arguments, 0);
48854         var ret = false;
48855         for(var i = 0; i < ar.length; i++) {
48856             if (!ar[i]) {
48857                 continue; // skip -- if this happends something invalid got sent, we 
48858                 // should ignore it, as basically that interface element will not show up
48859                 // and that should be pretty obvious!!
48860             }
48861             
48862             if (Roo.form[ar[i].xtype]) {
48863                 ar[i].form = this;
48864                 var fe = Roo.factory(ar[i], Roo.form);
48865                 if (!ret) {
48866                     ret = fe;
48867                 }
48868                 fe.form = this;
48869                 if (fe.store) {
48870                     fe.store.form = this;
48871                 }
48872                 if (fe.isLayout) {  
48873                          
48874                     this.start(fe);
48875                     this.allItems.push(fe);
48876                     if (fe.items && fe.addxtype) {
48877                         fe.addxtype.apply(fe, fe.items);
48878                         delete fe.items;
48879                     }
48880                      this.end();
48881                     continue;
48882                 }
48883                 
48884                 
48885                  
48886                 this.add(fe);
48887               //  console.log('adding ' + ar[i].xtype);
48888             }
48889             if (ar[i].xtype == 'Button') {  
48890                 //console.log('adding button');
48891                 //console.log(ar[i]);
48892                 this.addButton(ar[i]);
48893                 this.allItems.push(fe);
48894                 continue;
48895             }
48896             
48897             if (ar[i].xtype == 'end') { // so we can add fieldsets... / layout etc.
48898                 alert('end is not supported on xtype any more, use items');
48899             //    this.end();
48900             //    //console.log('adding end');
48901             }
48902             
48903         }
48904         return ret;
48905     },
48906     
48907     /**
48908      * Starts monitoring of the valid state of this form. Usually this is done by passing the config
48909      * option "monitorValid"
48910      */
48911     startMonitoring : function(){
48912         if(!this.bound){
48913             this.bound = true;
48914             Roo.TaskMgr.start({
48915                 run : this.bindHandler,
48916                 interval : this.monitorPoll || 200,
48917                 scope: this
48918             });
48919         }
48920     },
48921
48922     /**
48923      * Stops monitoring of the valid state of this form
48924      */
48925     stopMonitoring : function(){
48926         this.bound = false;
48927     },
48928
48929     // private
48930     bindHandler : function(){
48931         if(!this.bound){
48932             return false; // stops binding
48933         }
48934         var valid = true;
48935         this.items.each(function(f){
48936             if(!f.isValid(true)){
48937                 valid = false;
48938                 return false;
48939             }
48940         });
48941         for(var i = 0, len = this.buttons.length; i < len; i++){
48942             var btn = this.buttons[i];
48943             if(btn.formBind === true && btn.disabled === valid){
48944                 btn.setDisabled(!valid);
48945             }
48946         }
48947         this.fireEvent('clientvalidation', this, valid);
48948     }
48949     
48950     
48951     
48952     
48953     
48954     
48955     
48956     
48957 });
48958
48959
48960 // back compat
48961 Roo.Form = Roo.form.Form;
48962 /*
48963  * Based on:
48964  * Ext JS Library 1.1.1
48965  * Copyright(c) 2006-2007, Ext JS, LLC.
48966  *
48967  * Originally Released Under LGPL - original licence link has changed is not relivant.
48968  *
48969  * Fork - LGPL
48970  * <script type="text/javascript">
48971  */
48972
48973 // as we use this in bootstrap.
48974 Roo.namespace('Roo.form');
48975  /**
48976  * @class Roo.form.Action
48977  * Internal Class used to handle form actions
48978  * @constructor
48979  * @param {Roo.form.BasicForm} el The form element or its id
48980  * @param {Object} config Configuration options
48981  */
48982
48983  
48984  
48985 // define the action interface
48986 Roo.form.Action = function(form, options){
48987     this.form = form;
48988     this.options = options || {};
48989 };
48990 /**
48991  * Client Validation Failed
48992  * @const 
48993  */
48994 Roo.form.Action.CLIENT_INVALID = 'client';
48995 /**
48996  * Server Validation Failed
48997  * @const 
48998  */
48999 Roo.form.Action.SERVER_INVALID = 'server';
49000  /**
49001  * Connect to Server Failed
49002  * @const 
49003  */
49004 Roo.form.Action.CONNECT_FAILURE = 'connect';
49005 /**
49006  * Reading Data from Server Failed
49007  * @const 
49008  */
49009 Roo.form.Action.LOAD_FAILURE = 'load';
49010
49011 Roo.form.Action.prototype = {
49012     type : 'default',
49013     failureType : undefined,
49014     response : undefined,
49015     result : undefined,
49016
49017     // interface method
49018     run : function(options){
49019
49020     },
49021
49022     // interface method
49023     success : function(response){
49024
49025     },
49026
49027     // interface method
49028     handleResponse : function(response){
49029
49030     },
49031
49032     // default connection failure
49033     failure : function(response){
49034         
49035         this.response = response;
49036         this.failureType = Roo.form.Action.CONNECT_FAILURE;
49037         this.form.afterAction(this, false);
49038     },
49039
49040     processResponse : function(response){
49041         this.response = response;
49042         if(!response.responseText){
49043             return true;
49044         }
49045         this.result = this.handleResponse(response);
49046         return this.result;
49047     },
49048
49049     // utility functions used internally
49050     getUrl : function(appendParams){
49051         var url = this.options.url || this.form.url || this.form.el.dom.action;
49052         if(appendParams){
49053             var p = this.getParams();
49054             if(p){
49055                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
49056             }
49057         }
49058         return url;
49059     },
49060
49061     getMethod : function(){
49062         return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
49063     },
49064
49065     getParams : function(){
49066         var bp = this.form.baseParams;
49067         var p = this.options.params;
49068         if(p){
49069             if(typeof p == "object"){
49070                 p = Roo.urlEncode(Roo.applyIf(p, bp));
49071             }else if(typeof p == 'string' && bp){
49072                 p += '&' + Roo.urlEncode(bp);
49073             }
49074         }else if(bp){
49075             p = Roo.urlEncode(bp);
49076         }
49077         return p;
49078     },
49079
49080     createCallback : function(){
49081         return {
49082             success: this.success,
49083             failure: this.failure,
49084             scope: this,
49085             timeout: (this.form.timeout*1000),
49086             upload: this.form.fileUpload ? this.success : undefined
49087         };
49088     }
49089 };
49090
49091 Roo.form.Action.Submit = function(form, options){
49092     Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
49093 };
49094
49095 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
49096     type : 'submit',
49097
49098     haveProgress : false,
49099     uploadComplete : false,
49100     
49101     // uploadProgress indicator.
49102     uploadProgress : function()
49103     {
49104         if (!this.form.progressUrl) {
49105             return;
49106         }
49107         
49108         if (!this.haveProgress) {
49109             Roo.MessageBox.progress("Uploading", "Uploading");
49110         }
49111         if (this.uploadComplete) {
49112            Roo.MessageBox.hide();
49113            return;
49114         }
49115         
49116         this.haveProgress = true;
49117    
49118         var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
49119         
49120         var c = new Roo.data.Connection();
49121         c.request({
49122             url : this.form.progressUrl,
49123             params: {
49124                 id : uid
49125             },
49126             method: 'GET',
49127             success : function(req){
49128                //console.log(data);
49129                 var rdata = false;
49130                 var edata;
49131                 try  {
49132                    rdata = Roo.decode(req.responseText)
49133                 } catch (e) {
49134                     Roo.log("Invalid data from server..");
49135                     Roo.log(edata);
49136                     return;
49137                 }
49138                 if (!rdata || !rdata.success) {
49139                     Roo.log(rdata);
49140                     Roo.MessageBox.alert(Roo.encode(rdata));
49141                     return;
49142                 }
49143                 var data = rdata.data;
49144                 
49145                 if (this.uploadComplete) {
49146                    Roo.MessageBox.hide();
49147                    return;
49148                 }
49149                    
49150                 if (data){
49151                     Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
49152                        Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
49153                     );
49154                 }
49155                 this.uploadProgress.defer(2000,this);
49156             },
49157        
49158             failure: function(data) {
49159                 Roo.log('progress url failed ');
49160                 Roo.log(data);
49161             },
49162             scope : this
49163         });
49164            
49165     },
49166     
49167     
49168     run : function()
49169     {
49170         // run get Values on the form, so it syncs any secondary forms.
49171         this.form.getValues();
49172         
49173         var o = this.options;
49174         var method = this.getMethod();
49175         var isPost = method == 'POST';
49176         if(o.clientValidation === false || this.form.isValid()){
49177             
49178             if (this.form.progressUrl) {
49179                 this.form.findField('UPLOAD_IDENTIFIER').setValue(
49180                     (new Date() * 1) + '' + Math.random());
49181                     
49182             } 
49183             
49184             
49185             Roo.Ajax.request(Roo.apply(this.createCallback(), {
49186                 form:this.form.el.dom,
49187                 url:this.getUrl(!isPost),
49188                 method: method,
49189                 params:isPost ? this.getParams() : null,
49190                 isUpload: this.form.fileUpload,
49191                 formData : this.form.formData
49192             }));
49193             
49194             this.uploadProgress();
49195
49196         }else if (o.clientValidation !== false){ // client validation failed
49197             this.failureType = Roo.form.Action.CLIENT_INVALID;
49198             this.form.afterAction(this, false);
49199         }
49200     },
49201
49202     success : function(response)
49203     {
49204         this.uploadComplete= true;
49205         if (this.haveProgress) {
49206             Roo.MessageBox.hide();
49207         }
49208         
49209         
49210         var result = this.processResponse(response);
49211         if(result === true || result.success){
49212             this.form.afterAction(this, true);
49213             return;
49214         }
49215         if(result.errors){
49216             this.form.markInvalid(result.errors);
49217             this.failureType = Roo.form.Action.SERVER_INVALID;
49218         }
49219         this.form.afterAction(this, false);
49220     },
49221     failure : function(response)
49222     {
49223         this.uploadComplete= true;
49224         if (this.haveProgress) {
49225             Roo.MessageBox.hide();
49226         }
49227         
49228         this.response = response;
49229         this.failureType = Roo.form.Action.CONNECT_FAILURE;
49230         this.form.afterAction(this, false);
49231     },
49232     
49233     handleResponse : function(response){
49234         if(this.form.errorReader){
49235             var rs = this.form.errorReader.read(response);
49236             var errors = [];
49237             if(rs.records){
49238                 for(var i = 0, len = rs.records.length; i < len; i++) {
49239                     var r = rs.records[i];
49240                     errors[i] = r.data;
49241                 }
49242             }
49243             if(errors.length < 1){
49244                 errors = null;
49245             }
49246             return {
49247                 success : rs.success,
49248                 errors : errors
49249             };
49250         }
49251         var ret = false;
49252         try {
49253             ret = Roo.decode(response.responseText);
49254         } catch (e) {
49255             ret = {
49256                 success: false,
49257                 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
49258                 errors : []
49259             };
49260         }
49261         return ret;
49262         
49263     }
49264 });
49265
49266
49267 Roo.form.Action.Load = function(form, options){
49268     Roo.form.Action.Load.superclass.constructor.call(this, form, options);
49269     this.reader = this.form.reader;
49270 };
49271
49272 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
49273     type : 'load',
49274
49275     run : function(){
49276         
49277         Roo.Ajax.request(Roo.apply(
49278                 this.createCallback(), {
49279                     method:this.getMethod(),
49280                     url:this.getUrl(false),
49281                     params:this.getParams()
49282         }));
49283     },
49284
49285     success : function(response){
49286         
49287         var result = this.processResponse(response);
49288         if(result === true || !result.success || !result.data){
49289             this.failureType = Roo.form.Action.LOAD_FAILURE;
49290             this.form.afterAction(this, false);
49291             return;
49292         }
49293         this.form.clearInvalid();
49294         this.form.setValues(result.data);
49295         this.form.afterAction(this, true);
49296     },
49297
49298     handleResponse : function(response){
49299         if(this.form.reader){
49300             var rs = this.form.reader.read(response);
49301             var data = rs.records && rs.records[0] ? rs.records[0].data : null;
49302             return {
49303                 success : rs.success,
49304                 data : data
49305             };
49306         }
49307         return Roo.decode(response.responseText);
49308     }
49309 });
49310
49311 Roo.form.Action.ACTION_TYPES = {
49312     'load' : Roo.form.Action.Load,
49313     'submit' : Roo.form.Action.Submit
49314 };/*
49315  * Based on:
49316  * Ext JS Library 1.1.1
49317  * Copyright(c) 2006-2007, Ext JS, LLC.
49318  *
49319  * Originally Released Under LGPL - original licence link has changed is not relivant.
49320  *
49321  * Fork - LGPL
49322  * <script type="text/javascript">
49323  */
49324  
49325 /**
49326  * @class Roo.form.Layout
49327  * @extends Roo.Component
49328  * @children Roo.form.Column Roo.form.Row Roo.form.Field Roo.Button Roo.form.TextItem
49329  * Creates a container for layout and rendering of fields in an {@link Roo.form.Form}.
49330  * @constructor
49331  * @param {Object} config Configuration options
49332  */
49333 Roo.form.Layout = function(config){
49334     var xitems = [];
49335     if (config.items) {
49336         xitems = config.items;
49337         delete config.items;
49338     }
49339     Roo.form.Layout.superclass.constructor.call(this, config);
49340     this.stack = [];
49341     Roo.each(xitems, this.addxtype, this);
49342      
49343 };
49344
49345 Roo.extend(Roo.form.Layout, Roo.Component, {
49346     /**
49347      * @cfg {String/Object} autoCreate
49348      * A DomHelper element spec used to autocreate the layout (defaults to {tag: 'div', cls: 'x-form-ct'})
49349      */
49350     /**
49351      * @cfg {String/Object/Function} style
49352      * A style specification string, e.g. "width:100px", or object in the form {width:"100px"}, or
49353      * a function which returns such a specification.
49354      */
49355     /**
49356      * @cfg {String} labelAlign
49357      * Valid values are "left," "top" and "right" (defaults to "left")
49358      */
49359     /**
49360      * @cfg {Number} labelWidth
49361      * Fixed width in pixels of all field labels (defaults to undefined)
49362      */
49363     /**
49364      * @cfg {Boolean} clear
49365      * True to add a clearing element at the end of this layout, equivalent to CSS clear: both (defaults to true)
49366      */
49367     clear : true,
49368     /**
49369      * @cfg {String} labelSeparator
49370      * The separator to use after field labels (defaults to ':')
49371      */
49372     labelSeparator : ':',
49373     /**
49374      * @cfg {Boolean} hideLabels
49375      * True to suppress the display of field labels in this layout (defaults to false)
49376      */
49377     hideLabels : false,
49378
49379     // private
49380     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct'},
49381     
49382     isLayout : true,
49383     
49384     // private
49385     onRender : function(ct, position){
49386         if(this.el){ // from markup
49387             this.el = Roo.get(this.el);
49388         }else {  // generate
49389             var cfg = this.getAutoCreate();
49390             this.el = ct.createChild(cfg, position);
49391         }
49392         if(this.style){
49393             this.el.applyStyles(this.style);
49394         }
49395         if(this.labelAlign){
49396             this.el.addClass('x-form-label-'+this.labelAlign);
49397         }
49398         if(this.hideLabels){
49399             this.labelStyle = "display:none";
49400             this.elementStyle = "padding-left:0;";
49401         }else{
49402             if(typeof this.labelWidth == 'number'){
49403                 this.labelStyle = "width:"+this.labelWidth+"px;";
49404                 this.elementStyle = "padding-left:"+((this.labelWidth+(typeof this.labelPad == 'number' ? this.labelPad : 5))+'px')+";";
49405             }
49406             if(this.labelAlign == 'top'){
49407                 this.labelStyle = "width:auto;";
49408                 this.elementStyle = "padding-left:0;";
49409             }
49410         }
49411         var stack = this.stack;
49412         var slen = stack.length;
49413         if(slen > 0){
49414             if(!this.fieldTpl){
49415                 var t = new Roo.Template(
49416                     '<div class="x-form-item {5}">',
49417                         '<label for="{0}" style="{2}">{1}{4}</label>',
49418                         '<div class="x-form-element" id="x-form-el-{0}" style="{3}">',
49419                         '</div>',
49420                     '</div><div class="x-form-clear-left"></div>'
49421                 );
49422                 t.disableFormats = true;
49423                 t.compile();
49424                 Roo.form.Layout.prototype.fieldTpl = t;
49425             }
49426             for(var i = 0; i < slen; i++) {
49427                 if(stack[i].isFormField){
49428                     this.renderField(stack[i]);
49429                 }else{
49430                     this.renderComponent(stack[i]);
49431                 }
49432             }
49433         }
49434         if(this.clear){
49435             this.el.createChild({cls:'x-form-clear'});
49436         }
49437     },
49438
49439     // private
49440     renderField : function(f){
49441         f.fieldEl = Roo.get(this.fieldTpl.append(this.el, [
49442                f.id, //0
49443                f.fieldLabel, //1
49444                f.labelStyle||this.labelStyle||'', //2
49445                this.elementStyle||'', //3
49446                typeof f.labelSeparator == 'undefined' ? this.labelSeparator : f.labelSeparator, //4
49447                f.itemCls||this.itemCls||''  //5
49448        ], true).getPrevSibling());
49449     },
49450
49451     // private
49452     renderComponent : function(c){
49453         c.render(c.isLayout ? this.el : this.el.createChild());    
49454     },
49455     /**
49456      * Adds a object form elements (using the xtype property as the factory method.)
49457      * Valid xtypes are:  TextField, TextArea .... Button, Layout, FieldSet, Column
49458      * @param {Object} config 
49459      */
49460     addxtype : function(o)
49461     {
49462         // create the lement.
49463         o.form = this.form;
49464         var fe = Roo.factory(o, Roo.form);
49465         this.form.allItems.push(fe);
49466         this.stack.push(fe);
49467         
49468         if (fe.isFormField) {
49469             this.form.items.add(fe);
49470         }
49471          
49472         return fe;
49473     }
49474 });
49475
49476 /**
49477  * @class Roo.form.Column
49478  * @extends Roo.form.Layout
49479  * Creates a column container for layout and rendering of fields in an {@link Roo.form.Form}.
49480  * @constructor
49481  * @param {Object} config Configuration options
49482  */
49483 Roo.form.Column = function(config){
49484     Roo.form.Column.superclass.constructor.call(this, config);
49485 };
49486
49487 Roo.extend(Roo.form.Column, Roo.form.Layout, {
49488     /**
49489      * @cfg {Number/String} width
49490      * The fixed width of the column in pixels or CSS value (defaults to "auto")
49491      */
49492     /**
49493      * @cfg {String/Object} autoCreate
49494      * A DomHelper element spec used to autocreate the column (defaults to {tag: 'div', cls: 'x-form-ct x-form-column'})
49495      */
49496
49497     // private
49498     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct x-form-column'},
49499
49500     // private
49501     onRender : function(ct, position){
49502         Roo.form.Column.superclass.onRender.call(this, ct, position);
49503         if(this.width){
49504             this.el.setWidth(this.width);
49505         }
49506     }
49507 });
49508
49509
49510 /**
49511  * @class Roo.form.Row
49512  * @extends Roo.form.Layout
49513  * @children Roo.form.Column Roo.form.Row Roo.form.Field Roo.Button Roo.form.TextItem
49514  * Creates a row container for layout and rendering of fields in an {@link Roo.form.Form}.
49515  * @constructor
49516  * @param {Object} config Configuration options
49517  */
49518
49519  
49520 Roo.form.Row = function(config){
49521     Roo.form.Row.superclass.constructor.call(this, config);
49522 };
49523  
49524 Roo.extend(Roo.form.Row, Roo.form.Layout, {
49525       /**
49526      * @cfg {Number/String} width
49527      * The fixed width of the column in pixels or CSS value (defaults to "auto")
49528      */
49529     /**
49530      * @cfg {Number/String} height
49531      * The fixed height of the column in pixels or CSS value (defaults to "auto")
49532      */
49533     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct x-form-row'},
49534     
49535     padWidth : 20,
49536     // private
49537     onRender : function(ct, position){
49538         //console.log('row render');
49539         if(!this.rowTpl){
49540             var t = new Roo.Template(
49541                 '<div class="x-form-item {5}" style="float:left;width:{6}px">',
49542                     '<label for="{0}" style="{2}">{1}{4}</label>',
49543                     '<div class="x-form-element" id="x-form-el-{0}" style="{3}">',
49544                     '</div>',
49545                 '</div>'
49546             );
49547             t.disableFormats = true;
49548             t.compile();
49549             Roo.form.Layout.prototype.rowTpl = t;
49550         }
49551         this.fieldTpl = this.rowTpl;
49552         
49553         //console.log('lw' + this.labelWidth +', la:' + this.labelAlign);
49554         var labelWidth = 100;
49555         
49556         if ((this.labelAlign != 'top')) {
49557             if (typeof this.labelWidth == 'number') {
49558                 labelWidth = this.labelWidth
49559             }
49560             this.padWidth =  20 + labelWidth;
49561             
49562         }
49563         
49564         Roo.form.Column.superclass.onRender.call(this, ct, position);
49565         if(this.width){
49566             this.el.setWidth(this.width);
49567         }
49568         if(this.height){
49569             this.el.setHeight(this.height);
49570         }
49571     },
49572     
49573     // private
49574     renderField : function(f){
49575         f.fieldEl = this.fieldTpl.append(this.el, [
49576                f.id, f.fieldLabel,
49577                f.labelStyle||this.labelStyle||'',
49578                this.elementStyle||'',
49579                typeof f.labelSeparator == 'undefined' ? this.labelSeparator : f.labelSeparator,
49580                f.itemCls||this.itemCls||'',
49581                f.width ? f.width + this.padWidth : 160 + this.padWidth
49582        ],true);
49583     }
49584 });
49585  
49586
49587 /**
49588  * @class Roo.form.FieldSet
49589  * @extends Roo.form.Layout
49590  * @children Roo.form.Column Roo.form.Row Roo.form.Field Roo.Button Roo.form.TextItem
49591  * Creates a fieldset container for layout and rendering of fields in an {@link Roo.form.Form}.
49592  * @constructor
49593  * @param {Object} config Configuration options
49594  */
49595 Roo.form.FieldSet = function(config){
49596     Roo.form.FieldSet.superclass.constructor.call(this, config);
49597 };
49598
49599 Roo.extend(Roo.form.FieldSet, Roo.form.Layout, {
49600     /**
49601      * @cfg {String} legend
49602      * The text to display as the legend for the FieldSet (defaults to '')
49603      */
49604     /**
49605      * @cfg {String/Object} autoCreate
49606      * A DomHelper element spec used to autocreate the fieldset (defaults to {tag: 'fieldset', cn: {tag:'legend'}})
49607      */
49608
49609     // private
49610     defaultAutoCreate : {tag: 'fieldset', cn: {tag:'legend'}},
49611
49612     // private
49613     onRender : function(ct, position){
49614         Roo.form.FieldSet.superclass.onRender.call(this, ct, position);
49615         if(this.legend){
49616             this.setLegend(this.legend);
49617         }
49618     },
49619
49620     // private
49621     setLegend : function(text){
49622         if(this.rendered){
49623             this.el.child('legend').update(text);
49624         }
49625     }
49626 });/*
49627  * Based on:
49628  * Ext JS Library 1.1.1
49629  * Copyright(c) 2006-2007, Ext JS, LLC.
49630  *
49631  * Originally Released Under LGPL - original licence link has changed is not relivant.
49632  *
49633  * Fork - LGPL
49634  * <script type="text/javascript">
49635  */
49636 /**
49637  * @class Roo.form.VTypes
49638  * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
49639  * @static
49640  */
49641 Roo.form.VTypes = function(){
49642     // closure these in so they are only created once.
49643     var alpha = /^[a-zA-Z_]+$/;
49644     var alphanum = /^[a-zA-Z0-9_]+$/;
49645     var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,24}$/;
49646     var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
49647
49648     // All these messages and functions are configurable
49649     return {
49650         /**
49651          * The function used to validate email addresses
49652          * @param {String} value The email address
49653          */
49654         'email' : function(v){
49655             return email.test(v);
49656         },
49657         /**
49658          * The error text to display when the email validation function returns false
49659          * @type String
49660          */
49661         'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
49662         /**
49663          * The keystroke filter mask to be applied on email input
49664          * @type RegExp
49665          */
49666         'emailMask' : /[a-z0-9_\.\-@]/i,
49667
49668         /**
49669          * The function used to validate URLs
49670          * @param {String} value The URL
49671          */
49672         'url' : function(v){
49673             return url.test(v);
49674         },
49675         /**
49676          * The error text to display when the url validation function returns false
49677          * @type String
49678          */
49679         'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
49680         
49681         /**
49682          * The function used to validate alpha values
49683          * @param {String} value The value
49684          */
49685         'alpha' : function(v){
49686             return alpha.test(v);
49687         },
49688         /**
49689          * The error text to display when the alpha validation function returns false
49690          * @type String
49691          */
49692         'alphaText' : 'This field should only contain letters and _',
49693         /**
49694          * The keystroke filter mask to be applied on alpha input
49695          * @type RegExp
49696          */
49697         'alphaMask' : /[a-z_]/i,
49698
49699         /**
49700          * The function used to validate alphanumeric values
49701          * @param {String} value The value
49702          */
49703         'alphanum' : function(v){
49704             return alphanum.test(v);
49705         },
49706         /**
49707          * The error text to display when the alphanumeric validation function returns false
49708          * @type String
49709          */
49710         'alphanumText' : 'This field should only contain letters, numbers and _',
49711         /**
49712          * The keystroke filter mask to be applied on alphanumeric input
49713          * @type RegExp
49714          */
49715         'alphanumMask' : /[a-z0-9_]/i
49716     };
49717 }();//<script type="text/javascript">
49718
49719 /**
49720  * @class Roo.form.FCKeditor
49721  * @extends Roo.form.TextArea
49722  * Wrapper around the FCKEditor http://www.fckeditor.net
49723  * @constructor
49724  * Creates a new FCKeditor
49725  * @param {Object} config Configuration options
49726  */
49727 Roo.form.FCKeditor = function(config){
49728     Roo.form.FCKeditor.superclass.constructor.call(this, config);
49729     this.addEvents({
49730          /**
49731          * @event editorinit
49732          * Fired when the editor is initialized - you can add extra handlers here..
49733          * @param {FCKeditor} this
49734          * @param {Object} the FCK object.
49735          */
49736         editorinit : true
49737     });
49738     
49739     
49740 };
49741 Roo.form.FCKeditor.editors = { };
49742 Roo.extend(Roo.form.FCKeditor, Roo.form.TextArea,
49743 {
49744     //defaultAutoCreate : {
49745     //    tag : "textarea",style   : "width:100px;height:60px;" ,autocomplete    : "off"
49746     //},
49747     // private
49748     /**
49749      * @cfg {Object} fck options - see fck manual for details.
49750      */
49751     fckconfig : false,
49752     
49753     /**
49754      * @cfg {Object} fck toolbar set (Basic or Default)
49755      */
49756     toolbarSet : 'Basic',
49757     /**
49758      * @cfg {Object} fck BasePath
49759      */ 
49760     basePath : '/fckeditor/',
49761     
49762     
49763     frame : false,
49764     
49765     value : '',
49766     
49767    
49768     onRender : function(ct, position)
49769     {
49770         if(!this.el){
49771             this.defaultAutoCreate = {
49772                 tag: "textarea",
49773                 style:"width:300px;height:60px;",
49774                 autocomplete: "new-password"
49775             };
49776         }
49777         Roo.form.FCKeditor.superclass.onRender.call(this, ct, position);
49778         /*
49779         if(this.grow){
49780             this.textSizeEl = Roo.DomHelper.append(document.body, {tag: "pre", cls: "x-form-grow-sizer"});
49781             if(this.preventScrollbars){
49782                 this.el.setStyle("overflow", "hidden");
49783             }
49784             this.el.setHeight(this.growMin);
49785         }
49786         */
49787         //console.log('onrender' + this.getId() );
49788         Roo.form.FCKeditor.editors[this.getId()] = this;
49789          
49790
49791         this.replaceTextarea() ;
49792         
49793     },
49794     
49795     getEditor : function() {
49796         return this.fckEditor;
49797     },
49798     /**
49799      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
49800      * @param {Mixed} value The value to set
49801      */
49802     
49803     
49804     setValue : function(value)
49805     {
49806         //console.log('setValue: ' + value);
49807         
49808         if(typeof(value) == 'undefined') { // not sure why this is happending...
49809             return;
49810         }
49811         Roo.form.FCKeditor.superclass.setValue.apply(this,[value]);
49812         
49813         //if(!this.el || !this.getEditor()) {
49814         //    this.value = value;
49815             //this.setValue.defer(100,this,[value]);    
49816         //    return;
49817         //} 
49818         
49819         if(!this.getEditor()) {
49820             return;
49821         }
49822         
49823         this.getEditor().SetData(value);
49824         
49825         //
49826
49827     },
49828
49829     /**
49830      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
49831      * @return {Mixed} value The field value
49832      */
49833     getValue : function()
49834     {
49835         
49836         if (this.frame && this.frame.dom.style.display == 'none') {
49837             return Roo.form.FCKeditor.superclass.getValue.call(this);
49838         }
49839         
49840         if(!this.el || !this.getEditor()) {
49841            
49842            // this.getValue.defer(100,this); 
49843             return this.value;
49844         }
49845        
49846         
49847         var value=this.getEditor().GetData();
49848         Roo.form.FCKeditor.superclass.setValue.apply(this,[value]);
49849         return Roo.form.FCKeditor.superclass.getValue.call(this);
49850         
49851
49852     },
49853
49854     /**
49855      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
49856      * @return {Mixed} value The field value
49857      */
49858     getRawValue : function()
49859     {
49860         if (this.frame && this.frame.dom.style.display == 'none') {
49861             return Roo.form.FCKeditor.superclass.getRawValue.call(this);
49862         }
49863         
49864         if(!this.el || !this.getEditor()) {
49865             //this.getRawValue.defer(100,this); 
49866             return this.value;
49867             return;
49868         }
49869         
49870         
49871         
49872         var value=this.getEditor().GetData();
49873         Roo.form.FCKeditor.superclass.setRawValue.apply(this,[value]);
49874         return Roo.form.FCKeditor.superclass.getRawValue.call(this);
49875          
49876     },
49877     
49878     setSize : function(w,h) {
49879         
49880         
49881         
49882         //if (this.frame && this.frame.dom.style.display == 'none') {
49883         //    Roo.form.FCKeditor.superclass.setSize.apply(this, [w, h]);
49884         //    return;
49885         //}
49886         //if(!this.el || !this.getEditor()) {
49887         //    this.setSize.defer(100,this, [w,h]); 
49888         //    return;
49889         //}
49890         
49891         
49892         
49893         Roo.form.FCKeditor.superclass.setSize.apply(this, [w, h]);
49894         
49895         this.frame.dom.setAttribute('width', w);
49896         this.frame.dom.setAttribute('height', h);
49897         this.frame.setSize(w,h);
49898         
49899     },
49900     
49901     toggleSourceEdit : function(value) {
49902         
49903       
49904          
49905         this.el.dom.style.display = value ? '' : 'none';
49906         this.frame.dom.style.display = value ?  'none' : '';
49907         
49908     },
49909     
49910     
49911     focus: function(tag)
49912     {
49913         if (this.frame.dom.style.display == 'none') {
49914             return Roo.form.FCKeditor.superclass.focus.call(this);
49915         }
49916         if(!this.el || !this.getEditor()) {
49917             this.focus.defer(100,this, [tag]); 
49918             return;
49919         }
49920         
49921         
49922         
49923         
49924         var tgs = this.getEditor().EditorDocument.getElementsByTagName(tag);
49925         this.getEditor().Focus();
49926         if (tgs.length) {
49927             if (!this.getEditor().Selection.GetSelection()) {
49928                 this.focus.defer(100,this, [tag]); 
49929                 return;
49930             }
49931             
49932             
49933             var r = this.getEditor().EditorDocument.createRange();
49934             r.setStart(tgs[0],0);
49935             r.setEnd(tgs[0],0);
49936             this.getEditor().Selection.GetSelection().removeAllRanges();
49937             this.getEditor().Selection.GetSelection().addRange(r);
49938             this.getEditor().Focus();
49939         }
49940         
49941     },
49942     
49943     
49944     
49945     replaceTextarea : function()
49946     {
49947         if ( document.getElementById( this.getId() + '___Frame' ) ) {
49948             return ;
49949         }
49950         //if ( !this.checkBrowser || this._isCompatibleBrowser() )
49951         //{
49952             // We must check the elements firstly using the Id and then the name.
49953         var oTextarea = document.getElementById( this.getId() );
49954         
49955         var colElementsByName = document.getElementsByName( this.getId() ) ;
49956          
49957         oTextarea.style.display = 'none' ;
49958
49959         if ( oTextarea.tabIndex ) {            
49960             this.TabIndex = oTextarea.tabIndex ;
49961         }
49962         
49963         this._insertHtmlBefore( this._getConfigHtml(), oTextarea ) ;
49964         this._insertHtmlBefore( this._getIFrameHtml(), oTextarea ) ;
49965         this.frame = Roo.get(this.getId() + '___Frame')
49966     },
49967     
49968     _getConfigHtml : function()
49969     {
49970         var sConfig = '' ;
49971
49972         for ( var o in this.fckconfig ) {
49973             sConfig += sConfig.length > 0  ? '&amp;' : '';
49974             sConfig += encodeURIComponent( o ) + '=' + encodeURIComponent( this.fckconfig[o] ) ;
49975         }
49976
49977         return '<input type="hidden" id="' + this.getId() + '___Config" value="' + sConfig + '" style="display:none" />' ;
49978     },
49979     
49980     
49981     _getIFrameHtml : function()
49982     {
49983         var sFile = 'fckeditor.html' ;
49984         /* no idea what this is about..
49985         try
49986         {
49987             if ( (/fcksource=true/i).test( window.top.location.search ) )
49988                 sFile = 'fckeditor.original.html' ;
49989         }
49990         catch (e) { 
49991         */
49992
49993         var sLink = this.basePath + 'editor/' + sFile + '?InstanceName=' + encodeURIComponent( this.getId() ) ;
49994         sLink += this.toolbarSet ? ( '&amp;Toolbar=' + this.toolbarSet)  : '';
49995         
49996         
49997         var html = '<iframe id="' + this.getId() +
49998             '___Frame" src="' + sLink +
49999             '" width="' + this.width +
50000             '" height="' + this.height + '"' +
50001             (this.tabIndex ?  ' tabindex="' + this.tabIndex + '"' :'' ) +
50002             ' frameborder="0" scrolling="no"></iframe>' ;
50003
50004         return html ;
50005     },
50006     
50007     _insertHtmlBefore : function( html, element )
50008     {
50009         if ( element.insertAdjacentHTML )       {
50010             // IE
50011             element.insertAdjacentHTML( 'beforeBegin', html ) ;
50012         } else { // Gecko
50013             var oRange = document.createRange() ;
50014             oRange.setStartBefore( element ) ;
50015             var oFragment = oRange.createContextualFragment( html );
50016             element.parentNode.insertBefore( oFragment, element ) ;
50017         }
50018     }
50019     
50020     
50021   
50022     
50023     
50024     
50025     
50026
50027 });
50028
50029 //Roo.reg('fckeditor', Roo.form.FCKeditor);
50030
50031 function FCKeditor_OnComplete(editorInstance){
50032     var f = Roo.form.FCKeditor.editors[editorInstance.Name];
50033     f.fckEditor = editorInstance;
50034     //console.log("loaded");
50035     f.fireEvent('editorinit', f, editorInstance);
50036
50037   
50038
50039  
50040
50041
50042
50043
50044
50045
50046
50047
50048
50049
50050
50051
50052
50053
50054
50055 //<script type="text/javascript">
50056 /**
50057  * @class Roo.form.GridField
50058  * @extends Roo.form.Field
50059  * Embed a grid (or editable grid into a form)
50060  * STATUS ALPHA
50061  * 
50062  * This embeds a grid in a form, the value of the field should be the json encoded array of rows
50063  * it needs 
50064  * xgrid.store = Roo.data.Store
50065  * xgrid.store.proxy = Roo.data.MemoryProxy (data = [] )
50066  * xgrid.store.reader = Roo.data.JsonReader 
50067  * 
50068  * 
50069  * @constructor
50070  * Creates a new GridField
50071  * @param {Object} config Configuration options
50072  */
50073 Roo.form.GridField = function(config){
50074     Roo.form.GridField.superclass.constructor.call(this, config);
50075      
50076 };
50077
50078 Roo.extend(Roo.form.GridField, Roo.form.Field,  {
50079     /**
50080      * @cfg {Number} width  - used to restrict width of grid..
50081      */
50082     width : 100,
50083     /**
50084      * @cfg {Number} height - used to restrict height of grid..
50085      */
50086     height : 50,
50087      /**
50088      * @cfg {Object} xgrid (xtype'd description of grid) { xtype : 'Grid', dataSource: .... }
50089          * 
50090          *}
50091      */
50092     xgrid : false, 
50093     /**
50094      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
50095      * {tag: "input", type: "checkbox", autocomplete: "off"})
50096      */
50097    // defaultAutoCreate : { tag: 'div' },
50098     defaultAutoCreate : { tag: 'input', type: 'hidden', autocomplete: 'new-password'},
50099     /**
50100      * @cfg {String} addTitle Text to include for adding a title.
50101      */
50102     addTitle : false,
50103     //
50104     onResize : function(){
50105         Roo.form.Field.superclass.onResize.apply(this, arguments);
50106     },
50107
50108     initEvents : function(){
50109         // Roo.form.Checkbox.superclass.initEvents.call(this);
50110         // has no events...
50111        
50112     },
50113
50114
50115     getResizeEl : function(){
50116         return this.wrap;
50117     },
50118
50119     getPositionEl : function(){
50120         return this.wrap;
50121     },
50122
50123     // private
50124     onRender : function(ct, position){
50125         
50126         this.style = this.style || 'overflow: hidden; border:1px solid #c3daf9;';
50127         var style = this.style;
50128         delete this.style;
50129         
50130         Roo.form.GridField.superclass.onRender.call(this, ct, position);
50131         this.wrap = this.el.wrap({cls: ''}); // not sure why ive done thsi...
50132         this.viewEl = this.wrap.createChild({ tag: 'div' });
50133         if (style) {
50134             this.viewEl.applyStyles(style);
50135         }
50136         if (this.width) {
50137             this.viewEl.setWidth(this.width);
50138         }
50139         if (this.height) {
50140             this.viewEl.setHeight(this.height);
50141         }
50142         //if(this.inputValue !== undefined){
50143         //this.setValue(this.value);
50144         
50145         
50146         this.grid = new Roo.grid[this.xgrid.xtype](this.viewEl, this.xgrid);
50147         
50148         
50149         this.grid.render();
50150         this.grid.getDataSource().on('remove', this.refreshValue, this);
50151         this.grid.getDataSource().on('update', this.refreshValue, this);
50152         this.grid.on('afteredit', this.refreshValue, this);
50153  
50154     },
50155      
50156     
50157     /**
50158      * Sets the value of the item. 
50159      * @param {String} either an object  or a string..
50160      */
50161     setValue : function(v){
50162         //this.value = v;
50163         v = v || []; // empty set..
50164         // this does not seem smart - it really only affects memoryproxy grids..
50165         if (this.grid && this.grid.getDataSource() && typeof(v) != 'undefined') {
50166             var ds = this.grid.getDataSource();
50167             // assumes a json reader..
50168             var data = {}
50169             data[ds.reader.meta.root ] =  typeof(v) == 'string' ? Roo.decode(v) : v;
50170             ds.loadData( data);
50171         }
50172         // clear selection so it does not get stale.
50173         if (this.grid.sm) { 
50174             this.grid.sm.clearSelections();
50175         }
50176         
50177         Roo.form.GridField.superclass.setValue.call(this, v);
50178         this.refreshValue();
50179         // should load data in the grid really....
50180     },
50181     
50182     // private
50183     refreshValue: function() {
50184          var val = [];
50185         this.grid.getDataSource().each(function(r) {
50186             val.push(r.data);
50187         });
50188         this.el.dom.value = Roo.encode(val);
50189     }
50190     
50191      
50192     
50193     
50194 });/*
50195  * Based on:
50196  * Ext JS Library 1.1.1
50197  * Copyright(c) 2006-2007, Ext JS, LLC.
50198  *
50199  * Originally Released Under LGPL - original licence link has changed is not relivant.
50200  *
50201  * Fork - LGPL
50202  * <script type="text/javascript">
50203  */
50204 /**
50205  * @class Roo.form.DisplayField
50206  * @extends Roo.form.Field
50207  * A generic Field to display non-editable data.
50208  * @cfg {Boolean} closable (true|false) default false
50209  * @constructor
50210  * Creates a new Display Field item.
50211  * @param {Object} config Configuration options
50212  */
50213 Roo.form.DisplayField = function(config){
50214     Roo.form.DisplayField.superclass.constructor.call(this, config);
50215     
50216     this.addEvents({
50217         /**
50218          * @event close
50219          * Fires after the click the close btn
50220              * @param {Roo.form.DisplayField} this
50221              */
50222         close : true
50223     });
50224 };
50225
50226 Roo.extend(Roo.form.DisplayField, Roo.form.TextField,  {
50227     inputType:      'hidden',
50228     allowBlank:     true,
50229     readOnly:         true,
50230     
50231  
50232     /**
50233      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
50234      */
50235     focusClass : undefined,
50236     /**
50237      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
50238      */
50239     fieldClass: 'x-form-field',
50240     
50241      /**
50242      * @cfg {Function} valueRenderer The renderer for the field (so you can reformat output). should return raw HTML
50243      */
50244     valueRenderer: undefined,
50245     
50246     width: 100,
50247     /**
50248      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
50249      * {tag: "input", type: "checkbox", autocomplete: "off"})
50250      */
50251      
50252  //   defaultAutoCreate : { tag: 'input', type: 'hidden', autocomplete: 'off'},
50253  
50254     closable : false,
50255     
50256     onResize : function(){
50257         Roo.form.DisplayField.superclass.onResize.apply(this, arguments);
50258         
50259     },
50260
50261     initEvents : function(){
50262         // Roo.form.Checkbox.superclass.initEvents.call(this);
50263         // has no events...
50264         
50265         if(this.closable){
50266             this.closeEl.on('click', this.onClose, this);
50267         }
50268        
50269     },
50270
50271
50272     getResizeEl : function(){
50273         return this.wrap;
50274     },
50275
50276     getPositionEl : function(){
50277         return this.wrap;
50278     },
50279
50280     // private
50281     onRender : function(ct, position){
50282         
50283         Roo.form.DisplayField.superclass.onRender.call(this, ct, position);
50284         //if(this.inputValue !== undefined){
50285         this.wrap = this.el.wrap();
50286         
50287         this.viewEl = this.wrap.createChild({ tag: 'div', cls: 'x-form-displayfield'});
50288         
50289         if(this.closable){
50290             this.closeEl = this.wrap.createChild({ tag: 'div', cls: 'x-dlg-close'});
50291         }
50292         
50293         if (this.bodyStyle) {
50294             this.viewEl.applyStyles(this.bodyStyle);
50295         }
50296         //this.viewEl.setStyle('padding', '2px');
50297         
50298         this.setValue(this.value);
50299         
50300     },
50301 /*
50302     // private
50303     initValue : Roo.emptyFn,
50304
50305   */
50306
50307         // private
50308     onClick : function(){
50309         
50310     },
50311
50312     /**
50313      * Sets the checked state of the checkbox.
50314      * @param {Boolean/String} checked True, 'true', '1', or 'on' to check the checkbox, any other value will uncheck it.
50315      */
50316     setValue : function(v){
50317         this.value = v;
50318         var html = this.valueRenderer ?  this.valueRenderer(v) : String.format('{0}', v);
50319         // this might be called before we have a dom element..
50320         if (!this.viewEl) {
50321             return;
50322         }
50323         this.viewEl.dom.innerHTML = html;
50324         Roo.form.DisplayField.superclass.setValue.call(this, v);
50325
50326     },
50327     
50328     onClose : function(e)
50329     {
50330         e.preventDefault();
50331         
50332         this.fireEvent('close', this);
50333     }
50334 });/*
50335  * 
50336  * Licence- LGPL
50337  * 
50338  */
50339
50340 /**
50341  * @class Roo.form.DayPicker
50342  * @extends Roo.form.Field
50343  * A Day picker show [M] [T] [W] ....
50344  * @constructor
50345  * Creates a new Day Picker
50346  * @param {Object} config Configuration options
50347  */
50348 Roo.form.DayPicker= function(config){
50349     Roo.form.DayPicker.superclass.constructor.call(this, config);
50350      
50351 };
50352
50353 Roo.extend(Roo.form.DayPicker, Roo.form.Field,  {
50354     /**
50355      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
50356      */
50357     focusClass : undefined,
50358     /**
50359      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
50360      */
50361     fieldClass: "x-form-field",
50362    
50363     /**
50364      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
50365      * {tag: "input", type: "checkbox", autocomplete: "off"})
50366      */
50367     defaultAutoCreate : { tag: "input", type: 'hidden', autocomplete: "new-password"},
50368     
50369    
50370     actionMode : 'viewEl', 
50371     //
50372     // private
50373  
50374     inputType : 'hidden',
50375     
50376      
50377     inputElement: false, // real input element?
50378     basedOn: false, // ????
50379     
50380     isFormField: true, // not sure where this is needed!!!!
50381
50382     onResize : function(){
50383         Roo.form.Checkbox.superclass.onResize.apply(this, arguments);
50384         if(!this.boxLabel){
50385             this.el.alignTo(this.wrap, 'c-c');
50386         }
50387     },
50388
50389     initEvents : function(){
50390         Roo.form.Checkbox.superclass.initEvents.call(this);
50391         this.el.on("click", this.onClick,  this);
50392         this.el.on("change", this.onClick,  this);
50393     },
50394
50395
50396     getResizeEl : function(){
50397         return this.wrap;
50398     },
50399
50400     getPositionEl : function(){
50401         return this.wrap;
50402     },
50403
50404     
50405     // private
50406     onRender : function(ct, position){
50407         Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
50408        
50409         this.wrap = this.el.wrap({cls: 'x-form-daypick-item '});
50410         
50411         var r1 = '<table><tr>';
50412         var r2 = '<tr class="x-form-daypick-icons">';
50413         for (var i=0; i < 7; i++) {
50414             r1+= '<td><div>' + Date.dayNames[i].substring(0,3) + '</div></td>';
50415             r2+= '<td><img class="x-menu-item-icon" src="' + Roo.BLANK_IMAGE_URL  +'"></td>';
50416         }
50417         
50418         var viewEl = this.wrap.createChild( r1 + '</tr>' + r2 + '</tr></table>');
50419         viewEl.select('img').on('click', this.onClick, this);
50420         this.viewEl = viewEl;   
50421         
50422         
50423         // this will not work on Chrome!!!
50424         this.el.on('DOMAttrModified', this.setFromHidden,  this); //ff
50425         this.el.on('propertychange', this.setFromHidden,  this);  //ie
50426         
50427         
50428           
50429
50430     },
50431
50432     // private
50433     initValue : Roo.emptyFn,
50434
50435     /**
50436      * Returns the checked state of the checkbox.
50437      * @return {Boolean} True if checked, else false
50438      */
50439     getValue : function(){
50440         return this.el.dom.value;
50441         
50442     },
50443
50444         // private
50445     onClick : function(e){ 
50446         //this.setChecked(!this.checked);
50447         Roo.get(e.target).toggleClass('x-menu-item-checked');
50448         this.refreshValue();
50449         //if(this.el.dom.checked != this.checked){
50450         //    this.setValue(this.el.dom.checked);
50451        // }
50452     },
50453     
50454     // private
50455     refreshValue : function()
50456     {
50457         var val = '';
50458         this.viewEl.select('img',true).each(function(e,i,n)  {
50459             val += e.is(".x-menu-item-checked") ? String(n) : '';
50460         });
50461         this.setValue(val, true);
50462     },
50463
50464     /**
50465      * Sets the checked state of the checkbox.
50466      * On is always based on a string comparison between inputValue and the param.
50467      * @param {Boolean/String} value - the value to set 
50468      * @param {Boolean/String} suppressEvent - whether to suppress the checkchange event.
50469      */
50470     setValue : function(v,suppressEvent){
50471         if (!this.el.dom) {
50472             return;
50473         }
50474         var old = this.el.dom.value ;
50475         this.el.dom.value = v;
50476         if (suppressEvent) {
50477             return ;
50478         }
50479          
50480         // update display..
50481         this.viewEl.select('img',true).each(function(e,i,n)  {
50482             
50483             var on = e.is(".x-menu-item-checked");
50484             var newv = v.indexOf(String(n)) > -1;
50485             if (on != newv) {
50486                 e.toggleClass('x-menu-item-checked');
50487             }
50488             
50489         });
50490         
50491         
50492         this.fireEvent('change', this, v, old);
50493         
50494         
50495     },
50496    
50497     // handle setting of hidden value by some other method!!?!?
50498     setFromHidden: function()
50499     {
50500         if(!this.el){
50501             return;
50502         }
50503         //console.log("SET FROM HIDDEN");
50504         //alert('setFrom hidden');
50505         this.setValue(this.el.dom.value);
50506     },
50507     
50508     onDestroy : function()
50509     {
50510         if(this.viewEl){
50511             Roo.get(this.viewEl).remove();
50512         }
50513          
50514         Roo.form.DayPicker.superclass.onDestroy.call(this);
50515     }
50516
50517 });/*
50518  * RooJS Library 1.1.1
50519  * Copyright(c) 2008-2011  Alan Knowles
50520  *
50521  * License - LGPL
50522  */
50523  
50524
50525 /**
50526  * @class Roo.form.ComboCheck
50527  * @extends Roo.form.ComboBox
50528  * A combobox for multiple select items.
50529  *
50530  * FIXME - could do with a reset button..
50531  * 
50532  * @constructor
50533  * Create a new ComboCheck
50534  * @param {Object} config Configuration options
50535  */
50536 Roo.form.ComboCheck = function(config){
50537     Roo.form.ComboCheck.superclass.constructor.call(this, config);
50538     // should verify some data...
50539     // like
50540     // hiddenName = required..
50541     // displayField = required
50542     // valudField == required
50543     var req= [ 'hiddenName', 'displayField', 'valueField' ];
50544     var _t = this;
50545     Roo.each(req, function(e) {
50546         if ((typeof(_t[e]) == 'undefined' ) || !_t[e].length) {
50547             throw "Roo.form.ComboCheck : missing value for: " + e;
50548         }
50549     });
50550     
50551     
50552 };
50553
50554 Roo.extend(Roo.form.ComboCheck, Roo.form.ComboBox, {
50555      
50556      
50557     editable : false,
50558      
50559     selectedClass: 'x-menu-item-checked', 
50560     
50561     // private
50562     onRender : function(ct, position){
50563         var _t = this;
50564         
50565         
50566         
50567         if(!this.tpl){
50568             var cls = 'x-combo-list';
50569
50570             
50571             this.tpl =  new Roo.Template({
50572                 html :  '<div class="'+cls+'-item x-menu-check-item">' +
50573                    '<img class="x-menu-item-icon" style="margin: 0px;" src="' + Roo.BLANK_IMAGE_URL + '">' + 
50574                    '<span>{' + this.displayField + '}</span>' +
50575                     '</div>' 
50576                 
50577             });
50578         }
50579  
50580         
50581         Roo.form.ComboCheck.superclass.onRender.call(this, ct, position);
50582         this.view.singleSelect = false;
50583         this.view.multiSelect = true;
50584         this.view.toggleSelect = true;
50585         this.pageTb.add(new Roo.Toolbar.Fill(), {
50586             
50587             text: 'Done',
50588             handler: function()
50589             {
50590                 _t.collapse();
50591             }
50592         });
50593     },
50594     
50595     onViewOver : function(e, t){
50596         // do nothing...
50597         return;
50598         
50599     },
50600     
50601     onViewClick : function(doFocus,index){
50602         return;
50603         
50604     },
50605     select: function () {
50606         //Roo.log("SELECT CALLED");
50607     },
50608      
50609     selectByValue : function(xv, scrollIntoView){
50610         var ar = this.getValueArray();
50611         var sels = [];
50612         
50613         Roo.each(ar, function(v) {
50614             if(v === undefined || v === null){
50615                 return;
50616             }
50617             var r = this.findRecord(this.valueField, v);
50618             if(r){
50619                 sels.push(this.store.indexOf(r))
50620                 
50621             }
50622         },this);
50623         this.view.select(sels);
50624         return false;
50625     },
50626     
50627     
50628     
50629     onSelect : function(record, index){
50630        // Roo.log("onselect Called");
50631        // this is only called by the clear button now..
50632         this.view.clearSelections();
50633         this.setValue('[]');
50634         if (this.value != this.valueBefore) {
50635             this.fireEvent('change', this, this.value, this.valueBefore);
50636             this.valueBefore = this.value;
50637         }
50638     },
50639     getValueArray : function()
50640     {
50641         var ar = [] ;
50642         
50643         try {
50644             //Roo.log(this.value);
50645             if (typeof(this.value) == 'undefined') {
50646                 return [];
50647             }
50648             var ar = Roo.decode(this.value);
50649             return  ar instanceof Array ? ar : []; //?? valid?
50650             
50651         } catch(e) {
50652             Roo.log(e + "\nRoo.form.ComboCheck:getValueArray  invalid data:" + this.getValue());
50653             return [];
50654         }
50655          
50656     },
50657     expand : function ()
50658     {
50659         
50660         Roo.form.ComboCheck.superclass.expand.call(this);
50661         this.valueBefore = typeof(this.value) == 'undefined' ? '' : this.value;
50662         //this.valueBefore = typeof(this.valueBefore) == 'undefined' ? '' : this.valueBefore;
50663         
50664
50665     },
50666     
50667     collapse : function(){
50668         Roo.form.ComboCheck.superclass.collapse.call(this);
50669         var sl = this.view.getSelectedIndexes();
50670         var st = this.store;
50671         var nv = [];
50672         var tv = [];
50673         var r;
50674         Roo.each(sl, function(i) {
50675             r = st.getAt(i);
50676             nv.push(r.get(this.valueField));
50677         },this);
50678         this.setValue(Roo.encode(nv));
50679         if (this.value != this.valueBefore) {
50680
50681             this.fireEvent('change', this, this.value, this.valueBefore);
50682             this.valueBefore = this.value;
50683         }
50684         
50685     },
50686     
50687     setValue : function(v){
50688         // Roo.log(v);
50689         this.value = v;
50690         
50691         var vals = this.getValueArray();
50692         var tv = [];
50693         Roo.each(vals, function(k) {
50694             var r = this.findRecord(this.valueField, k);
50695             if(r){
50696                 tv.push(r.data[this.displayField]);
50697             }else if(this.valueNotFoundText !== undefined){
50698                 tv.push( this.valueNotFoundText );
50699             }
50700         },this);
50701        // Roo.log(tv);
50702         
50703         Roo.form.ComboBox.superclass.setValue.call(this, tv.join(', '));
50704         this.hiddenField.value = v;
50705         this.value = v;
50706     }
50707     
50708 });/*
50709  * Based on:
50710  * Ext JS Library 1.1.1
50711  * Copyright(c) 2006-2007, Ext JS, LLC.
50712  *
50713  * Originally Released Under LGPL - original licence link has changed is not relivant.
50714  *
50715  * Fork - LGPL
50716  * <script type="text/javascript">
50717  */
50718  
50719 /**
50720  * @class Roo.form.Signature
50721  * @extends Roo.form.Field
50722  * Signature field.  
50723  * @constructor
50724  * 
50725  * @param {Object} config Configuration options
50726  */
50727
50728 Roo.form.Signature = function(config){
50729     Roo.form.Signature.superclass.constructor.call(this, config);
50730     
50731     this.addEvents({// not in used??
50732          /**
50733          * @event confirm
50734          * Fires when the 'confirm' icon is pressed (add a listener to enable add button)
50735              * @param {Roo.form.Signature} combo This combo box
50736              */
50737         'confirm' : true,
50738         /**
50739          * @event reset
50740          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
50741              * @param {Roo.form.ComboBox} combo This combo box
50742              * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
50743              */
50744         'reset' : true
50745     });
50746 };
50747
50748 Roo.extend(Roo.form.Signature, Roo.form.Field,  {
50749     /**
50750      * @cfg {Object} labels Label to use when rendering a form.
50751      * defaults to 
50752      * labels : { 
50753      *      clear : "Clear",
50754      *      confirm : "Confirm"
50755      *  }
50756      */
50757     labels : { 
50758         clear : "Clear",
50759         confirm : "Confirm"
50760     },
50761     /**
50762      * @cfg {Number} width The signature panel width (defaults to 300)
50763      */
50764     width: 300,
50765     /**
50766      * @cfg {Number} height The signature panel height (defaults to 100)
50767      */
50768     height : 100,
50769     /**
50770      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to false)
50771      */
50772     allowBlank : false,
50773     
50774     //private
50775     // {Object} signPanel The signature SVG panel element (defaults to {})
50776     signPanel : {},
50777     // {Boolean} isMouseDown False to validate that the mouse down event (defaults to false)
50778     isMouseDown : false,
50779     // {Boolean} isConfirmed validate the signature is confirmed or not for submitting form (defaults to false)
50780     isConfirmed : false,
50781     // {String} signatureTmp SVG mapping string (defaults to empty string)
50782     signatureTmp : '',
50783     
50784     
50785     defaultAutoCreate : { // modified by initCompnoent..
50786         tag: "input",
50787         type:"hidden"
50788     },
50789
50790     // private
50791     onRender : function(ct, position){
50792         
50793         Roo.form.Signature.superclass.onRender.call(this, ct, position);
50794         
50795         this.wrap = this.el.wrap({
50796             cls:'x-form-signature-wrap', style : 'width: ' + this.width + 'px', cn:{cls:'x-form-signature'}
50797         });
50798         
50799         this.createToolbar(this);
50800         this.signPanel = this.wrap.createChild({
50801                 tag: 'div',
50802                 style: 'width: ' + this.width + 'px; height: ' + this.height + 'px; border: 0;'
50803             }, this.el
50804         );
50805             
50806         this.svgID = Roo.id();
50807         this.svgEl = this.signPanel.createChild({
50808               xmlns : 'http://www.w3.org/2000/svg',
50809               tag : 'svg',
50810               id : this.svgID + "-svg",
50811               width: this.width,
50812               height: this.height,
50813               viewBox: '0 0 '+this.width+' '+this.height,
50814               cn : [
50815                 {
50816                     tag: "rect",
50817                     id: this.svgID + "-svg-r",
50818                     width: this.width,
50819                     height: this.height,
50820                     fill: "#ffa"
50821                 },
50822                 {
50823                     tag: "line",
50824                     id: this.svgID + "-svg-l",
50825                     x1: "0", // start
50826                     y1: (this.height*0.8), // start set the line in 80% of height
50827                     x2: this.width, // end
50828                     y2: (this.height*0.8), // end set the line in 80% of height
50829                     'stroke': "#666",
50830                     'stroke-width': "1",
50831                     'stroke-dasharray': "3",
50832                     'shape-rendering': "crispEdges",
50833                     'pointer-events': "none"
50834                 },
50835                 {
50836                     tag: "path",
50837                     id: this.svgID + "-svg-p",
50838                     'stroke': "navy",
50839                     'stroke-width': "3",
50840                     'fill': "none",
50841                     'pointer-events': 'none'
50842                 }
50843               ]
50844         });
50845         this.createSVG();
50846         this.svgBox = this.svgEl.dom.getScreenCTM();
50847     },
50848     createSVG : function(){ 
50849         var svg = this.signPanel;
50850         var r = svg.select('#'+ this.svgID + '-svg-r', true).first().dom;
50851         var t = this;
50852
50853         r.addEventListener('mousedown', function(e) { return t.down(e); }, false);
50854         r.addEventListener('mousemove', function(e) { return t.move(e); }, false);
50855         r.addEventListener('mouseup', function(e) { return t.up(e); }, false);
50856         r.addEventListener('mouseout', function(e) { return t.up(e); }, false);
50857         r.addEventListener('touchstart', function(e) { return t.down(e); }, false);
50858         r.addEventListener('touchmove', function(e) { return t.move(e); }, false);
50859         r.addEventListener('touchend', function(e) { return t.up(e); }, false);
50860         
50861     },
50862     isTouchEvent : function(e){
50863         return e.type.match(/^touch/);
50864     },
50865     getCoords : function (e) {
50866         var pt    = this.svgEl.dom.createSVGPoint();
50867         pt.x = e.clientX; 
50868         pt.y = e.clientY;
50869         if (this.isTouchEvent(e)) {
50870             pt.x =  e.targetTouches[0].clientX;
50871             pt.y = e.targetTouches[0].clientY;
50872         }
50873         var a = this.svgEl.dom.getScreenCTM();
50874         var b = a.inverse();
50875         var mx = pt.matrixTransform(b);
50876         return mx.x + ',' + mx.y;
50877     },
50878     //mouse event headler 
50879     down : function (e) {
50880         this.signatureTmp += 'M' + this.getCoords(e) + ' ';
50881         this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr('d', this.signatureTmp);
50882         
50883         this.isMouseDown = true;
50884         
50885         e.preventDefault();
50886     },
50887     move : function (e) {
50888         if (this.isMouseDown) {
50889             this.signatureTmp += 'L' + this.getCoords(e) + ' ';
50890             this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr( 'd', this.signatureTmp);
50891         }
50892         
50893         e.preventDefault();
50894     },
50895     up : function (e) {
50896         this.isMouseDown = false;
50897         var sp = this.signatureTmp.split(' ');
50898         
50899         if(sp.length > 1){
50900             if(!sp[sp.length-2].match(/^L/)){
50901                 sp.pop();
50902                 sp.pop();
50903                 sp.push("");
50904                 this.signatureTmp = sp.join(" ");
50905             }
50906         }
50907         if(this.getValue() != this.signatureTmp){
50908             this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#ffa');
50909             this.isConfirmed = false;
50910         }
50911         e.preventDefault();
50912     },
50913     
50914     /**
50915      * Protected method that will not generally be called directly. It
50916      * is called when the editor creates its toolbar. Override this method if you need to
50917      * add custom toolbar buttons.
50918      * @param {HtmlEditor} editor
50919      */
50920     createToolbar : function(editor){
50921          function btn(id, toggle, handler){
50922             var xid = fid + '-'+ id ;
50923             return {
50924                 id : xid,
50925                 cmd : id,
50926                 cls : 'x-btn-icon x-edit-'+id,
50927                 enableToggle:toggle !== false,
50928                 scope: editor, // was editor...
50929                 handler:handler||editor.relayBtnCmd,
50930                 clickEvent:'mousedown',
50931                 tooltip: etb.buttonTips[id] || undefined, ///tips ???
50932                 tabIndex:-1
50933             };
50934         }
50935         
50936         
50937         var tb = new Roo.Toolbar(editor.wrap.dom.firstChild);
50938         this.tb = tb;
50939         this.tb.add(
50940            {
50941                 cls : ' x-signature-btn x-signature-'+id,
50942                 scope: editor, // was editor...
50943                 handler: this.reset,
50944                 clickEvent:'mousedown',
50945                 text: this.labels.clear
50946             },
50947             {
50948                  xtype : 'Fill',
50949                  xns: Roo.Toolbar
50950             }, 
50951             {
50952                 cls : '  x-signature-btn x-signature-'+id,
50953                 scope: editor, // was editor...
50954                 handler: this.confirmHandler,
50955                 clickEvent:'mousedown',
50956                 text: this.labels.confirm
50957             }
50958         );
50959     
50960     },
50961     //public
50962     /**
50963      * when user is clicked confirm then show this image.....
50964      * 
50965      * @return {String} Image Data URI
50966      */
50967     getImageDataURI : function(){
50968         var svg = this.svgEl.dom.parentNode.innerHTML;
50969         var src = 'data:image/svg+xml;base64,'+window.btoa(svg);
50970         return src; 
50971     },
50972     /**
50973      * 
50974      * @return {Boolean} this.isConfirmed
50975      */
50976     getConfirmed : function(){
50977         return this.isConfirmed;
50978     },
50979     /**
50980      * 
50981      * @return {Number} this.width
50982      */
50983     getWidth : function(){
50984         return this.width;
50985     },
50986     /**
50987      * 
50988      * @return {Number} this.height
50989      */
50990     getHeight : function(){
50991         return this.height;
50992     },
50993     // private
50994     getSignature : function(){
50995         return this.signatureTmp;
50996     },
50997     // private
50998     reset : function(){
50999         this.signatureTmp = '';
51000         this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#ffa');
51001         this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr( 'd', '');
51002         this.isConfirmed = false;
51003         Roo.form.Signature.superclass.reset.call(this);
51004     },
51005     setSignature : function(s){
51006         this.signatureTmp = s;
51007         this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#ffa');
51008         this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr( 'd', s);
51009         this.setValue(s);
51010         this.isConfirmed = false;
51011         Roo.form.Signature.superclass.reset.call(this);
51012     }, 
51013     test : function(){
51014 //        Roo.log(this.signPanel.dom.contentWindow.up())
51015     },
51016     //private
51017     setConfirmed : function(){
51018         
51019         
51020         
51021 //        Roo.log(Roo.get(this.signPanel.dom.contentWindow.r).attr('fill', '#cfc'));
51022     },
51023     // private
51024     confirmHandler : function(){
51025         if(!this.getSignature()){
51026             return;
51027         }
51028         
51029         this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#cfc');
51030         this.setValue(this.getSignature());
51031         this.isConfirmed = true;
51032         
51033         this.fireEvent('confirm', this);
51034     },
51035     // private
51036     // Subclasses should provide the validation implementation by overriding this
51037     validateValue : function(value){
51038         if(this.allowBlank){
51039             return true;
51040         }
51041         
51042         if(this.isConfirmed){
51043             return true;
51044         }
51045         return false;
51046     }
51047 });/*
51048  * Based on:
51049  * Ext JS Library 1.1.1
51050  * Copyright(c) 2006-2007, Ext JS, LLC.
51051  *
51052  * Originally Released Under LGPL - original licence link has changed is not relivant.
51053  *
51054  * Fork - LGPL
51055  * <script type="text/javascript">
51056  */
51057  
51058
51059 /**
51060  * @class Roo.form.ComboBox
51061  * @extends Roo.form.TriggerField
51062  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
51063  * @constructor
51064  * Create a new ComboBox.
51065  * @param {Object} config Configuration options
51066  */
51067 Roo.form.Select = function(config){
51068     Roo.form.Select.superclass.constructor.call(this, config);
51069      
51070 };
51071
51072 Roo.extend(Roo.form.Select , Roo.form.ComboBox, {
51073     /**
51074      * @cfg {String/HTMLElement/Element} transform The id, DOM node or element of an existing select to convert to a ComboBox
51075      */
51076     /**
51077      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
51078      * rendering into an Roo.Editor, defaults to false)
51079      */
51080     /**
51081      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
51082      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
51083      */
51084     /**
51085      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
51086      */
51087     /**
51088      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
51089      * the dropdown list (defaults to undefined, with no header element)
51090      */
51091
51092      /**
51093      * @cfg {String/Roo.Template} tpl The template to use to render the output
51094      */
51095      
51096     // private
51097     defaultAutoCreate : {tag: "select"  },
51098     /**
51099      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
51100      */
51101     listWidth: undefined,
51102     /**
51103      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
51104      * mode = 'remote' or 'text' if mode = 'local')
51105      */
51106     displayField: undefined,
51107     /**
51108      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
51109      * mode = 'remote' or 'value' if mode = 'local'). 
51110      * Note: use of a valueField requires the user make a selection
51111      * in order for a value to be mapped.
51112      */
51113     valueField: undefined,
51114     
51115     
51116     /**
51117      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
51118      * field's data value (defaults to the underlying DOM element's name)
51119      */
51120     hiddenName: undefined,
51121     /**
51122      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
51123      */
51124     listClass: '',
51125     /**
51126      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
51127      */
51128     selectedClass: 'x-combo-selected',
51129     /**
51130      * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
51131      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-arrow-trigger'
51132      * which displays a downward arrow icon).
51133      */
51134     triggerClass : 'x-form-arrow-trigger',
51135     /**
51136      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
51137      */
51138     shadow:'sides',
51139     /**
51140      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
51141      * anchor positions (defaults to 'tl-bl')
51142      */
51143     listAlign: 'tl-bl?',
51144     /**
51145      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
51146      */
51147     maxHeight: 300,
51148     /**
51149      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
51150      * query specified by the allQuery config option (defaults to 'query')
51151      */
51152     triggerAction: 'query',
51153     /**
51154      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
51155      * (defaults to 4, does not apply if editable = false)
51156      */
51157     minChars : 4,
51158     /**
51159      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
51160      * delay (typeAheadDelay) if it matches a known value (defaults to false)
51161      */
51162     typeAhead: false,
51163     /**
51164      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
51165      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
51166      */
51167     queryDelay: 500,
51168     /**
51169      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
51170      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
51171      */
51172     pageSize: 0,
51173     /**
51174      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
51175      * when editable = true (defaults to false)
51176      */
51177     selectOnFocus:false,
51178     /**
51179      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
51180      */
51181     queryParam: 'query',
51182     /**
51183      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
51184      * when mode = 'remote' (defaults to 'Loading...')
51185      */
51186     loadingText: 'Loading...',
51187     /**
51188      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
51189      */
51190     resizable: false,
51191     /**
51192      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
51193      */
51194     handleHeight : 8,
51195     /**
51196      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
51197      * traditional select (defaults to true)
51198      */
51199     editable: true,
51200     /**
51201      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
51202      */
51203     allQuery: '',
51204     /**
51205      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
51206      */
51207     mode: 'remote',
51208     /**
51209      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
51210      * listWidth has a higher value)
51211      */
51212     minListWidth : 70,
51213     /**
51214      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
51215      * allow the user to set arbitrary text into the field (defaults to false)
51216      */
51217     forceSelection:false,
51218     /**
51219      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
51220      * if typeAhead = true (defaults to 250)
51221      */
51222     typeAheadDelay : 250,
51223     /**
51224      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
51225      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
51226      */
51227     valueNotFoundText : undefined,
51228     
51229     /**
51230      * @cfg {String} defaultValue The value displayed after loading the store.
51231      */
51232     defaultValue: '',
51233     
51234     /**
51235      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
51236      */
51237     blockFocus : false,
51238     
51239     /**
51240      * @cfg {Boolean} disableClear Disable showing of clear button.
51241      */
51242     disableClear : false,
51243     /**
51244      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
51245      */
51246     alwaysQuery : false,
51247     
51248     //private
51249     addicon : false,
51250     editicon: false,
51251     
51252     // element that contains real text value.. (when hidden is used..)
51253      
51254     // private
51255     onRender : function(ct, position){
51256         Roo.form.Field.prototype.onRender.call(this, ct, position);
51257         
51258         if(this.store){
51259             this.store.on('beforeload', this.onBeforeLoad, this);
51260             this.store.on('load', this.onLoad, this);
51261             this.store.on('loadexception', this.onLoadException, this);
51262             this.store.load({});
51263         }
51264         
51265         
51266         
51267     },
51268
51269     // private
51270     initEvents : function(){
51271         //Roo.form.ComboBox.superclass.initEvents.call(this);
51272  
51273     },
51274
51275     onDestroy : function(){
51276        
51277         if(this.store){
51278             this.store.un('beforeload', this.onBeforeLoad, this);
51279             this.store.un('load', this.onLoad, this);
51280             this.store.un('loadexception', this.onLoadException, this);
51281         }
51282         //Roo.form.ComboBox.superclass.onDestroy.call(this);
51283     },
51284
51285     // private
51286     fireKey : function(e){
51287         if(e.isNavKeyPress() && !this.list.isVisible()){
51288             this.fireEvent("specialkey", this, e);
51289         }
51290     },
51291
51292     // private
51293     onResize: function(w, h){
51294         
51295         return; 
51296     
51297         
51298     },
51299
51300     /**
51301      * Allow or prevent the user from directly editing the field text.  If false is passed,
51302      * the user will only be able to select from the items defined in the dropdown list.  This method
51303      * is the runtime equivalent of setting the 'editable' config option at config time.
51304      * @param {Boolean} value True to allow the user to directly edit the field text
51305      */
51306     setEditable : function(value){
51307          
51308     },
51309
51310     // private
51311     onBeforeLoad : function(){
51312         
51313         Roo.log("Select before load");
51314         return;
51315     
51316         this.innerList.update(this.loadingText ?
51317                '<div class="loading-indicator">'+this.loadingText+'</div>' : '');
51318         //this.restrictHeight();
51319         this.selectedIndex = -1;
51320     },
51321
51322     // private
51323     onLoad : function(){
51324
51325     
51326         var dom = this.el.dom;
51327         dom.innerHTML = '';
51328          var od = dom.ownerDocument;
51329          
51330         if (this.emptyText) {
51331             var op = od.createElement('option');
51332             op.setAttribute('value', '');
51333             op.innerHTML = String.format('{0}', this.emptyText);
51334             dom.appendChild(op);
51335         }
51336         if(this.store.getCount() > 0){
51337            
51338             var vf = this.valueField;
51339             var df = this.displayField;
51340             this.store.data.each(function(r) {
51341                 // which colmsn to use... testing - cdoe / title..
51342                 var op = od.createElement('option');
51343                 op.setAttribute('value', r.data[vf]);
51344                 op.innerHTML = String.format('{0}', r.data[df]);
51345                 dom.appendChild(op);
51346             });
51347             if (typeof(this.defaultValue != 'undefined')) {
51348                 this.setValue(this.defaultValue);
51349             }
51350             
51351              
51352         }else{
51353             //this.onEmptyResults();
51354         }
51355         //this.el.focus();
51356     },
51357     // private
51358     onLoadException : function()
51359     {
51360         dom.innerHTML = '';
51361             
51362         Roo.log("Select on load exception");
51363         return;
51364     
51365         this.collapse();
51366         Roo.log(this.store.reader.jsonData);
51367         if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
51368             Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
51369         }
51370         
51371         
51372     },
51373     // private
51374     onTypeAhead : function(){
51375          
51376     },
51377
51378     // private
51379     onSelect : function(record, index){
51380         Roo.log('on select?');
51381         return;
51382         if(this.fireEvent('beforeselect', this, record, index) !== false){
51383             this.setFromData(index > -1 ? record.data : false);
51384             this.collapse();
51385             this.fireEvent('select', this, record, index);
51386         }
51387     },
51388
51389     /**
51390      * Returns the currently selected field value or empty string if no value is set.
51391      * @return {String} value The selected value
51392      */
51393     getValue : function(){
51394         var dom = this.el.dom;
51395         this.value = dom.options[dom.selectedIndex].value;
51396         return this.value;
51397         
51398     },
51399
51400     /**
51401      * Clears any text/value currently set in the field
51402      */
51403     clearValue : function(){
51404         this.value = '';
51405         this.el.dom.selectedIndex = this.emptyText ? 0 : -1;
51406         
51407     },
51408
51409     /**
51410      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
51411      * will be displayed in the field.  If the value does not match the data value of an existing item,
51412      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
51413      * Otherwise the field will be blank (although the value will still be set).
51414      * @param {String} value The value to match
51415      */
51416     setValue : function(v){
51417         var d = this.el.dom;
51418         for (var i =0; i < d.options.length;i++) {
51419             if (v == d.options[i].value) {
51420                 d.selectedIndex = i;
51421                 this.value = v;
51422                 return;
51423             }
51424         }
51425         this.clearValue();
51426     },
51427     /**
51428      * @property {Object} the last set data for the element
51429      */
51430     
51431     lastData : false,
51432     /**
51433      * Sets the value of the field based on a object which is related to the record format for the store.
51434      * @param {Object} value the value to set as. or false on reset?
51435      */
51436     setFromData : function(o){
51437         Roo.log('setfrom data?');
51438          
51439         
51440         
51441     },
51442     // private
51443     reset : function(){
51444         this.clearValue();
51445     },
51446     // private
51447     findRecord : function(prop, value){
51448         
51449         return false;
51450     
51451         var record;
51452         if(this.store.getCount() > 0){
51453             this.store.each(function(r){
51454                 if(r.data[prop] == value){
51455                     record = r;
51456                     return false;
51457                 }
51458                 return true;
51459             });
51460         }
51461         return record;
51462     },
51463     
51464     getName: function()
51465     {
51466         // returns hidden if it's set..
51467         if (!this.rendered) {return ''};
51468         return !this.hiddenName && this.el.dom.name  ? this.el.dom.name : (this.hiddenName || '');
51469         
51470     },
51471      
51472
51473     
51474
51475     // private
51476     onEmptyResults : function(){
51477         Roo.log('empty results');
51478         //this.collapse();
51479     },
51480
51481     /**
51482      * Returns true if the dropdown list is expanded, else false.
51483      */
51484     isExpanded : function(){
51485         return false;
51486     },
51487
51488     /**
51489      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
51490      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
51491      * @param {String} value The data value of the item to select
51492      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
51493      * selected item if it is not currently in view (defaults to true)
51494      * @return {Boolean} True if the value matched an item in the list, else false
51495      */
51496     selectByValue : function(v, scrollIntoView){
51497         Roo.log('select By Value');
51498         return false;
51499     
51500         if(v !== undefined && v !== null){
51501             var r = this.findRecord(this.valueField || this.displayField, v);
51502             if(r){
51503                 this.select(this.store.indexOf(r), scrollIntoView);
51504                 return true;
51505             }
51506         }
51507         return false;
51508     },
51509
51510     /**
51511      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
51512      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
51513      * @param {Number} index The zero-based index of the list item to select
51514      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
51515      * selected item if it is not currently in view (defaults to true)
51516      */
51517     select : function(index, scrollIntoView){
51518         Roo.log('select ');
51519         return  ;
51520         
51521         this.selectedIndex = index;
51522         this.view.select(index);
51523         if(scrollIntoView !== false){
51524             var el = this.view.getNode(index);
51525             if(el){
51526                 this.innerList.scrollChildIntoView(el, false);
51527             }
51528         }
51529     },
51530
51531       
51532
51533     // private
51534     validateBlur : function(){
51535         
51536         return;
51537         
51538     },
51539
51540     // private
51541     initQuery : function(){
51542         this.doQuery(this.getRawValue());
51543     },
51544
51545     // private
51546     doForce : function(){
51547         if(this.el.dom.value.length > 0){
51548             this.el.dom.value =
51549                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
51550              
51551         }
51552     },
51553
51554     /**
51555      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
51556      * query allowing the query action to be canceled if needed.
51557      * @param {String} query The SQL query to execute
51558      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
51559      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
51560      * saved in the current store (defaults to false)
51561      */
51562     doQuery : function(q, forceAll){
51563         
51564         Roo.log('doQuery?');
51565         if(q === undefined || q === null){
51566             q = '';
51567         }
51568         var qe = {
51569             query: q,
51570             forceAll: forceAll,
51571             combo: this,
51572             cancel:false
51573         };
51574         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
51575             return false;
51576         }
51577         q = qe.query;
51578         forceAll = qe.forceAll;
51579         if(forceAll === true || (q.length >= this.minChars)){
51580             if(this.lastQuery != q || this.alwaysQuery){
51581                 this.lastQuery = q;
51582                 if(this.mode == 'local'){
51583                     this.selectedIndex = -1;
51584                     if(forceAll){
51585                         this.store.clearFilter();
51586                     }else{
51587                         this.store.filter(this.displayField, q);
51588                     }
51589                     this.onLoad();
51590                 }else{
51591                     this.store.baseParams[this.queryParam] = q;
51592                     this.store.load({
51593                         params: this.getParams(q)
51594                     });
51595                     this.expand();
51596                 }
51597             }else{
51598                 this.selectedIndex = -1;
51599                 this.onLoad();   
51600             }
51601         }
51602     },
51603
51604     // private
51605     getParams : function(q){
51606         var p = {};
51607         //p[this.queryParam] = q;
51608         if(this.pageSize){
51609             p.start = 0;
51610             p.limit = this.pageSize;
51611         }
51612         return p;
51613     },
51614
51615     /**
51616      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
51617      */
51618     collapse : function(){
51619         
51620     },
51621
51622     // private
51623     collapseIf : function(e){
51624         
51625     },
51626
51627     /**
51628      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
51629      */
51630     expand : function(){
51631         
51632     } ,
51633
51634     // private
51635      
51636
51637     /** 
51638     * @cfg {Boolean} grow 
51639     * @hide 
51640     */
51641     /** 
51642     * @cfg {Number} growMin 
51643     * @hide 
51644     */
51645     /** 
51646     * @cfg {Number} growMax 
51647     * @hide 
51648     */
51649     /**
51650      * @hide
51651      * @method autoSize
51652      */
51653     
51654     setWidth : function()
51655     {
51656         
51657     },
51658     getResizeEl : function(){
51659         return this.el;
51660     }
51661 });//<script type="text/javasscript">
51662  
51663
51664 /**
51665  * @class Roo.DDView
51666  * A DnD enabled version of Roo.View.
51667  * @param {Element/String} container The Element in which to create the View.
51668  * @param {String} tpl The template string used to create the markup for each element of the View
51669  * @param {Object} config The configuration properties. These include all the config options of
51670  * {@link Roo.View} plus some specific to this class.<br>
51671  * <p>
51672  * Drag/drop is implemented by adding {@link Roo.data.Record}s to the target DDView. If copying is
51673  * not being performed, the original {@link Roo.data.Record} is removed from the source DDView.<br>
51674  * <p>
51675  * The following extra CSS rules are needed to provide insertion point highlighting:<pre><code>
51676 .x-view-drag-insert-above {
51677         border-top:1px dotted #3366cc;
51678 }
51679 .x-view-drag-insert-below {
51680         border-bottom:1px dotted #3366cc;
51681 }
51682 </code></pre>
51683  * 
51684  */
51685  
51686 Roo.DDView = function(container, tpl, config) {
51687     Roo.DDView.superclass.constructor.apply(this, arguments);
51688     this.getEl().setStyle("outline", "0px none");
51689     this.getEl().unselectable();
51690     if (this.dragGroup) {
51691         this.setDraggable(this.dragGroup.split(","));
51692     }
51693     if (this.dropGroup) {
51694         this.setDroppable(this.dropGroup.split(","));
51695     }
51696     if (this.deletable) {
51697         this.setDeletable();
51698     }
51699     this.isDirtyFlag = false;
51700         this.addEvents({
51701                 "drop" : true
51702         });
51703 };
51704
51705 Roo.extend(Roo.DDView, Roo.View, {
51706 /**     @cfg {String/Array} dragGroup The ddgroup name(s) for the View's DragZone. */
51707 /**     @cfg {String/Array} dropGroup The ddgroup name(s) for the View's DropZone. */
51708 /**     @cfg {Boolean} copy Causes drag operations to copy nodes rather than move. */
51709 /**     @cfg {Boolean} allowCopy Causes ctrl/drag operations to copy nodes rather than move. */
51710
51711         isFormField: true,
51712
51713         reset: Roo.emptyFn,
51714         
51715         clearInvalid: Roo.form.Field.prototype.clearInvalid,
51716
51717         validate: function() {
51718                 return true;
51719         },
51720         
51721         destroy: function() {
51722                 this.purgeListeners();
51723                 this.getEl.removeAllListeners();
51724                 this.getEl().remove();
51725                 if (this.dragZone) {
51726                         if (this.dragZone.destroy) {
51727                                 this.dragZone.destroy();
51728                         }
51729                 }
51730                 if (this.dropZone) {
51731                         if (this.dropZone.destroy) {
51732                                 this.dropZone.destroy();
51733                         }
51734                 }
51735         },
51736
51737 /**     Allows this class to be an Roo.form.Field so it can be found using {@link Roo.form.BasicForm#findField}. */
51738         getName: function() {
51739                 return this.name;
51740         },
51741
51742 /**     Loads the View from a JSON string representing the Records to put into the Store. */
51743         setValue: function(v) {
51744                 if (!this.store) {
51745                         throw "DDView.setValue(). DDView must be constructed with a valid Store";
51746                 }
51747                 var data = {};
51748                 data[this.store.reader.meta.root] = v ? [].concat(v) : [];
51749                 this.store.proxy = new Roo.data.MemoryProxy(data);
51750                 this.store.load();
51751         },
51752
51753 /**     @return {String} a parenthesised list of the ids of the Records in the View. */
51754         getValue: function() {
51755                 var result = '(';
51756                 this.store.each(function(rec) {
51757                         result += rec.id + ',';
51758                 });
51759                 return result.substr(0, result.length - 1) + ')';
51760         },
51761         
51762         getIds: function() {
51763                 var i = 0, result = new Array(this.store.getCount());
51764                 this.store.each(function(rec) {
51765                         result[i++] = rec.id;
51766                 });
51767                 return result;
51768         },
51769         
51770         isDirty: function() {
51771                 return this.isDirtyFlag;
51772         },
51773
51774 /**
51775  *      Part of the Roo.dd.DropZone interface. If no target node is found, the
51776  *      whole Element becomes the target, and this causes the drop gesture to append.
51777  */
51778     getTargetFromEvent : function(e) {
51779                 var target = e.getTarget();
51780                 while ((target !== null) && (target.parentNode != this.el.dom)) {
51781                 target = target.parentNode;
51782                 }
51783                 if (!target) {
51784                         target = this.el.dom.lastChild || this.el.dom;
51785                 }
51786                 return target;
51787     },
51788
51789 /**
51790  *      Create the drag data which consists of an object which has the property "ddel" as
51791  *      the drag proxy element. 
51792  */
51793     getDragData : function(e) {
51794         var target = this.findItemFromChild(e.getTarget());
51795                 if(target) {
51796                         this.handleSelection(e);
51797                         var selNodes = this.getSelectedNodes();
51798             var dragData = {
51799                 source: this,
51800                 copy: this.copy || (this.allowCopy && e.ctrlKey),
51801                 nodes: selNodes,
51802                 records: []
51803                         };
51804                         var selectedIndices = this.getSelectedIndexes();
51805                         for (var i = 0; i < selectedIndices.length; i++) {
51806                                 dragData.records.push(this.store.getAt(selectedIndices[i]));
51807                         }
51808                         if (selNodes.length == 1) {
51809                                 dragData.ddel = target.cloneNode(true); // the div element
51810                         } else {
51811                                 var div = document.createElement('div'); // create the multi element drag "ghost"
51812                                 div.className = 'multi-proxy';
51813                                 for (var i = 0, len = selNodes.length; i < len; i++) {
51814                                         div.appendChild(selNodes[i].cloneNode(true));
51815                                 }
51816                                 dragData.ddel = div;
51817                         }
51818             //console.log(dragData)
51819             //console.log(dragData.ddel.innerHTML)
51820                         return dragData;
51821                 }
51822         //console.log('nodragData')
51823                 return false;
51824     },
51825     
51826 /**     Specify to which ddGroup items in this DDView may be dragged. */
51827     setDraggable: function(ddGroup) {
51828         if (ddGroup instanceof Array) {
51829                 Roo.each(ddGroup, this.setDraggable, this);
51830                 return;
51831         }
51832         if (this.dragZone) {
51833                 this.dragZone.addToGroup(ddGroup);
51834         } else {
51835                         this.dragZone = new Roo.dd.DragZone(this.getEl(), {
51836                                 containerScroll: true,
51837                                 ddGroup: ddGroup 
51838
51839                         });
51840 //                      Draggability implies selection. DragZone's mousedown selects the element.
51841                         if (!this.multiSelect) { this.singleSelect = true; }
51842
51843 //                      Wire the DragZone's handlers up to methods in *this*
51844                         this.dragZone.getDragData = this.getDragData.createDelegate(this);
51845                 }
51846     },
51847
51848 /**     Specify from which ddGroup this DDView accepts drops. */
51849     setDroppable: function(ddGroup) {
51850         if (ddGroup instanceof Array) {
51851                 Roo.each(ddGroup, this.setDroppable, this);
51852                 return;
51853         }
51854         if (this.dropZone) {
51855                 this.dropZone.addToGroup(ddGroup);
51856         } else {
51857                         this.dropZone = new Roo.dd.DropZone(this.getEl(), {
51858                                 containerScroll: true,
51859                                 ddGroup: ddGroup
51860                         });
51861
51862 //                      Wire the DropZone's handlers up to methods in *this*
51863                         this.dropZone.getTargetFromEvent = this.getTargetFromEvent.createDelegate(this);
51864                         this.dropZone.onNodeEnter = this.onNodeEnter.createDelegate(this);
51865                         this.dropZone.onNodeOver = this.onNodeOver.createDelegate(this);
51866                         this.dropZone.onNodeOut = this.onNodeOut.createDelegate(this);
51867                         this.dropZone.onNodeDrop = this.onNodeDrop.createDelegate(this);
51868                 }
51869     },
51870
51871 /**     Decide whether to drop above or below a View node. */
51872     getDropPoint : function(e, n, dd){
51873         if (n == this.el.dom) { return "above"; }
51874                 var t = Roo.lib.Dom.getY(n), b = t + n.offsetHeight;
51875                 var c = t + (b - t) / 2;
51876                 var y = Roo.lib.Event.getPageY(e);
51877                 if(y <= c) {
51878                         return "above";
51879                 }else{
51880                         return "below";
51881                 }
51882     },
51883
51884     onNodeEnter : function(n, dd, e, data){
51885                 return false;
51886     },
51887     
51888     onNodeOver : function(n, dd, e, data){
51889                 var pt = this.getDropPoint(e, n, dd);
51890                 // set the insert point style on the target node
51891                 var dragElClass = this.dropNotAllowed;
51892                 if (pt) {
51893                         var targetElClass;
51894                         if (pt == "above"){
51895                                 dragElClass = n.previousSibling ? "x-tree-drop-ok-between" : "x-tree-drop-ok-above";
51896                                 targetElClass = "x-view-drag-insert-above";
51897                         } else {
51898                                 dragElClass = n.nextSibling ? "x-tree-drop-ok-between" : "x-tree-drop-ok-below";
51899                                 targetElClass = "x-view-drag-insert-below";
51900                         }
51901                         if (this.lastInsertClass != targetElClass){
51902                                 Roo.fly(n).replaceClass(this.lastInsertClass, targetElClass);
51903                                 this.lastInsertClass = targetElClass;
51904                         }
51905                 }
51906                 return dragElClass;
51907         },
51908
51909     onNodeOut : function(n, dd, e, data){
51910                 this.removeDropIndicators(n);
51911     },
51912
51913     onNodeDrop : function(n, dd, e, data){
51914         if (this.fireEvent("drop", this, n, dd, e, data) === false) {
51915                 return false;
51916         }
51917         var pt = this.getDropPoint(e, n, dd);
51918                 var insertAt = (n == this.el.dom) ? this.nodes.length : n.nodeIndex;
51919                 if (pt == "below") { insertAt++; }
51920                 for (var i = 0; i < data.records.length; i++) {
51921                         var r = data.records[i];
51922                         var dup = this.store.getById(r.id);
51923                         if (dup && (dd != this.dragZone)) {
51924                                 Roo.fly(this.getNode(this.store.indexOf(dup))).frame("red", 1);
51925                         } else {
51926                                 if (data.copy) {
51927                                         this.store.insert(insertAt++, r.copy());
51928                                 } else {
51929                                         data.source.isDirtyFlag = true;
51930                                         r.store.remove(r);
51931                                         this.store.insert(insertAt++, r);
51932                                 }
51933                                 this.isDirtyFlag = true;
51934                         }
51935                 }
51936                 this.dragZone.cachedTarget = null;
51937                 return true;
51938     },
51939
51940     removeDropIndicators : function(n){
51941                 if(n){
51942                         Roo.fly(n).removeClass([
51943                                 "x-view-drag-insert-above",
51944                                 "x-view-drag-insert-below"]);
51945                         this.lastInsertClass = "_noclass";
51946                 }
51947     },
51948
51949 /**
51950  *      Utility method. Add a delete option to the DDView's context menu.
51951  *      @param {String} imageUrl The URL of the "delete" icon image.
51952  */
51953         setDeletable: function(imageUrl) {
51954                 if (!this.singleSelect && !this.multiSelect) {
51955                         this.singleSelect = true;
51956                 }
51957                 var c = this.getContextMenu();
51958                 this.contextMenu.on("itemclick", function(item) {
51959                         switch (item.id) {
51960                                 case "delete":
51961                                         this.remove(this.getSelectedIndexes());
51962                                         break;
51963                         }
51964                 }, this);
51965                 this.contextMenu.add({
51966                         icon: imageUrl,
51967                         id: "delete",
51968                         text: 'Delete'
51969                 });
51970         },
51971         
51972 /**     Return the context menu for this DDView. */
51973         getContextMenu: function() {
51974                 if (!this.contextMenu) {
51975 //                      Create the View's context menu
51976                         this.contextMenu = new Roo.menu.Menu({
51977                                 id: this.id + "-contextmenu"
51978                         });
51979                         this.el.on("contextmenu", this.showContextMenu, this);
51980                 }
51981                 return this.contextMenu;
51982         },
51983         
51984         disableContextMenu: function() {
51985                 if (this.contextMenu) {
51986                         this.el.un("contextmenu", this.showContextMenu, this);
51987                 }
51988         },
51989
51990         showContextMenu: function(e, item) {
51991         item = this.findItemFromChild(e.getTarget());
51992                 if (item) {
51993                         e.stopEvent();
51994                         this.select(this.getNode(item), this.multiSelect && e.ctrlKey, true);
51995                         this.contextMenu.showAt(e.getXY());
51996             }
51997     },
51998
51999 /**
52000  *      Remove {@link Roo.data.Record}s at the specified indices.
52001  *      @param {Array/Number} selectedIndices The index (or Array of indices) of Records to remove.
52002  */
52003     remove: function(selectedIndices) {
52004                 selectedIndices = [].concat(selectedIndices);
52005                 for (var i = 0; i < selectedIndices.length; i++) {
52006                         var rec = this.store.getAt(selectedIndices[i]);
52007                         this.store.remove(rec);
52008                 }
52009     },
52010
52011 /**
52012  *      Double click fires the event, but also, if this is draggable, and there is only one other
52013  *      related DropZone, it transfers the selected node.
52014  */
52015     onDblClick : function(e){
52016         var item = this.findItemFromChild(e.getTarget());
52017         if(item){
52018             if (this.fireEvent("dblclick", this, this.indexOf(item), item, e) === false) {
52019                 return false;
52020             }
52021             if (this.dragGroup) {
52022                     var targets = Roo.dd.DragDropMgr.getRelated(this.dragZone, true);
52023                     while (targets.indexOf(this.dropZone) > -1) {
52024                             targets.remove(this.dropZone);
52025                                 }
52026                     if (targets.length == 1) {
52027                                         this.dragZone.cachedTarget = null;
52028                         var el = Roo.get(targets[0].getEl());
52029                         var box = el.getBox(true);
52030                         targets[0].onNodeDrop(el.dom, {
52031                                 target: el.dom,
52032                                 xy: [box.x, box.y + box.height - 1]
52033                         }, null, this.getDragData(e));
52034                     }
52035                 }
52036         }
52037     },
52038     
52039     handleSelection: function(e) {
52040                 this.dragZone.cachedTarget = null;
52041         var item = this.findItemFromChild(e.getTarget());
52042         if (!item) {
52043                 this.clearSelections(true);
52044                 return;
52045         }
52046                 if (item && (this.multiSelect || this.singleSelect)){
52047                         if(this.multiSelect && e.shiftKey && (!e.ctrlKey) && this.lastSelection){
52048                                 this.select(this.getNodes(this.indexOf(this.lastSelection), item.nodeIndex), false);
52049                         }else if (this.isSelected(this.getNode(item)) && e.ctrlKey){
52050                                 this.unselect(item);
52051                         } else {
52052                                 this.select(item, this.multiSelect && e.ctrlKey);
52053                                 this.lastSelection = item;
52054                         }
52055                 }
52056     },
52057
52058     onItemClick : function(item, index, e){
52059                 if(this.fireEvent("beforeclick", this, index, item, e) === false){
52060                         return false;
52061                 }
52062                 return true;
52063     },
52064
52065     unselect : function(nodeInfo, suppressEvent){
52066                 var node = this.getNode(nodeInfo);
52067                 if(node && this.isSelected(node)){
52068                         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
52069                                 Roo.fly(node).removeClass(this.selectedClass);
52070                                 this.selections.remove(node);
52071                                 if(!suppressEvent){
52072                                         this.fireEvent("selectionchange", this, this.selections);
52073                                 }
52074                         }
52075                 }
52076     }
52077 });
52078 /*
52079  * Based on:
52080  * Ext JS Library 1.1.1
52081  * Copyright(c) 2006-2007, Ext JS, LLC.
52082  *
52083  * Originally Released Under LGPL - original licence link has changed is not relivant.
52084  *
52085  * Fork - LGPL
52086  * <script type="text/javascript">
52087  */
52088  
52089 /**
52090  * @class Roo.LayoutManager
52091  * @extends Roo.util.Observable
52092  * Base class for layout managers.
52093  */
52094 Roo.LayoutManager = function(container, config){
52095     Roo.LayoutManager.superclass.constructor.call(this);
52096     this.el = Roo.get(container);
52097     // ie scrollbar fix
52098     if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
52099         document.body.scroll = "no";
52100     }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
52101         this.el.position('relative');
52102     }
52103     this.id = this.el.id;
52104     this.el.addClass("x-layout-container");
52105     /** false to disable window resize monitoring @type Boolean */
52106     this.monitorWindowResize = true;
52107     this.regions = {};
52108     this.addEvents({
52109         /**
52110          * @event layout
52111          * Fires when a layout is performed. 
52112          * @param {Roo.LayoutManager} this
52113          */
52114         "layout" : true,
52115         /**
52116          * @event regionresized
52117          * Fires when the user resizes a region. 
52118          * @param {Roo.LayoutRegion} region The resized region
52119          * @param {Number} newSize The new size (width for east/west, height for north/south)
52120          */
52121         "regionresized" : true,
52122         /**
52123          * @event regioncollapsed
52124          * Fires when a region is collapsed. 
52125          * @param {Roo.LayoutRegion} region The collapsed region
52126          */
52127         "regioncollapsed" : true,
52128         /**
52129          * @event regionexpanded
52130          * Fires when a region is expanded.  
52131          * @param {Roo.LayoutRegion} region The expanded region
52132          */
52133         "regionexpanded" : true
52134     });
52135     this.updating = false;
52136     Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
52137 };
52138
52139 Roo.extend(Roo.LayoutManager, Roo.util.Observable, {
52140     /**
52141      * Returns true if this layout is currently being updated
52142      * @return {Boolean}
52143      */
52144     isUpdating : function(){
52145         return this.updating; 
52146     },
52147     
52148     /**
52149      * Suspend the LayoutManager from doing auto-layouts while
52150      * making multiple add or remove calls
52151      */
52152     beginUpdate : function(){
52153         this.updating = true;    
52154     },
52155     
52156     /**
52157      * Restore auto-layouts and optionally disable the manager from performing a layout
52158      * @param {Boolean} noLayout true to disable a layout update 
52159      */
52160     endUpdate : function(noLayout){
52161         this.updating = false;
52162         if(!noLayout){
52163             this.layout();
52164         }    
52165     },
52166     
52167     layout: function(){
52168         
52169     },
52170     
52171     onRegionResized : function(region, newSize){
52172         this.fireEvent("regionresized", region, newSize);
52173         this.layout();
52174     },
52175     
52176     onRegionCollapsed : function(region){
52177         this.fireEvent("regioncollapsed", region);
52178     },
52179     
52180     onRegionExpanded : function(region){
52181         this.fireEvent("regionexpanded", region);
52182     },
52183         
52184     /**
52185      * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
52186      * performs box-model adjustments.
52187      * @return {Object} The size as an object {width: (the width), height: (the height)}
52188      */
52189     getViewSize : function(){
52190         var size;
52191         if(this.el.dom != document.body){
52192             size = this.el.getSize();
52193         }else{
52194             size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
52195         }
52196         size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
52197         size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
52198         return size;
52199     },
52200     
52201     /**
52202      * Returns the Element this layout is bound to.
52203      * @return {Roo.Element}
52204      */
52205     getEl : function(){
52206         return this.el;
52207     },
52208     
52209     /**
52210      * Returns the specified region.
52211      * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
52212      * @return {Roo.LayoutRegion}
52213      */
52214     getRegion : function(target){
52215         return this.regions[target.toLowerCase()];
52216     },
52217     
52218     onWindowResize : function(){
52219         if(this.monitorWindowResize){
52220             this.layout();
52221         }
52222     }
52223 });/*
52224  * Based on:
52225  * Ext JS Library 1.1.1
52226  * Copyright(c) 2006-2007, Ext JS, LLC.
52227  *
52228  * Originally Released Under LGPL - original licence link has changed is not relivant.
52229  *
52230  * Fork - LGPL
52231  * <script type="text/javascript">
52232  */
52233 /**
52234  * @class Roo.BorderLayout
52235  * @extends Roo.LayoutManager
52236  * @children Roo.ContentPanel
52237  * This class represents a common layout manager used in desktop applications. For screenshots and more details,
52238  * please see: <br><br>
52239  * <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>
52240  * <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>
52241  * Example:
52242  <pre><code>
52243  var layout = new Roo.BorderLayout(document.body, {
52244     north: {
52245         initialSize: 25,
52246         titlebar: false
52247     },
52248     west: {
52249         split:true,
52250         initialSize: 200,
52251         minSize: 175,
52252         maxSize: 400,
52253         titlebar: true,
52254         collapsible: true
52255     },
52256     east: {
52257         split:true,
52258         initialSize: 202,
52259         minSize: 175,
52260         maxSize: 400,
52261         titlebar: true,
52262         collapsible: true
52263     },
52264     south: {
52265         split:true,
52266         initialSize: 100,
52267         minSize: 100,
52268         maxSize: 200,
52269         titlebar: true,
52270         collapsible: true
52271     },
52272     center: {
52273         titlebar: true,
52274         autoScroll:true,
52275         resizeTabs: true,
52276         minTabWidth: 50,
52277         preferredTabWidth: 150
52278     }
52279 });
52280
52281 // shorthand
52282 var CP = Roo.ContentPanel;
52283
52284 layout.beginUpdate();
52285 layout.add("north", new CP("north", "North"));
52286 layout.add("south", new CP("south", {title: "South", closable: true}));
52287 layout.add("west", new CP("west", {title: "West"}));
52288 layout.add("east", new CP("autoTabs", {title: "Auto Tabs", closable: true}));
52289 layout.add("center", new CP("center1", {title: "Close Me", closable: true}));
52290 layout.add("center", new CP("center2", {title: "Center Panel", closable: false}));
52291 layout.getRegion("center").showPanel("center1");
52292 layout.endUpdate();
52293 </code></pre>
52294
52295 <b>The container the layout is rendered into can be either the body element or any other element.
52296 If it is not the body element, the container needs to either be an absolute positioned element,
52297 or you will need to add "position:relative" to the css of the container.  You will also need to specify
52298 the container size if it is not the body element.</b>
52299
52300 * @constructor
52301 * Create a new BorderLayout
52302 * @param {String/HTMLElement/Element} container The container this layout is bound to
52303 * @param {Object} config Configuration options
52304  */
52305 Roo.BorderLayout = function(container, config){
52306     config = config || {};
52307     Roo.BorderLayout.superclass.constructor.call(this, container, config);
52308     this.factory = config.factory || Roo.BorderLayout.RegionFactory;
52309     for(var i = 0, len = this.factory.validRegions.length; i < len; i++) {
52310         var target = this.factory.validRegions[i];
52311         if(config[target]){
52312             this.addRegion(target, config[target]);
52313         }
52314     }
52315 };
52316
52317 Roo.extend(Roo.BorderLayout, Roo.LayoutManager, {
52318         
52319         /**
52320          * @cfg {Roo.LayoutRegion} east
52321          */
52322         /**
52323          * @cfg {Roo.LayoutRegion} west
52324          */
52325         /**
52326          * @cfg {Roo.LayoutRegion} north
52327          */
52328         /**
52329          * @cfg {Roo.LayoutRegion} south
52330          */
52331         /**
52332          * @cfg {Roo.LayoutRegion} center
52333          */
52334     /**
52335      * Creates and adds a new region if it doesn't already exist.
52336      * @param {String} target The target region key (north, south, east, west or center).
52337      * @param {Object} config The regions config object
52338      * @return {BorderLayoutRegion} The new region
52339      */
52340     addRegion : function(target, config){
52341         if(!this.regions[target]){
52342             var r = this.factory.create(target, this, config);
52343             this.bindRegion(target, r);
52344         }
52345         return this.regions[target];
52346     },
52347
52348     // private (kinda)
52349     bindRegion : function(name, r){
52350         this.regions[name] = r;
52351         r.on("visibilitychange", this.layout, this);
52352         r.on("paneladded", this.layout, this);
52353         r.on("panelremoved", this.layout, this);
52354         r.on("invalidated", this.layout, this);
52355         r.on("resized", this.onRegionResized, this);
52356         r.on("collapsed", this.onRegionCollapsed, this);
52357         r.on("expanded", this.onRegionExpanded, this);
52358     },
52359
52360     /**
52361      * Performs a layout update.
52362      */
52363     layout : function(){
52364         if(this.updating) {
52365             return;
52366         }
52367         var size = this.getViewSize();
52368         var w = size.width;
52369         var h = size.height;
52370         var centerW = w;
52371         var centerH = h;
52372         var centerY = 0;
52373         var centerX = 0;
52374         //var x = 0, y = 0;
52375
52376         var rs = this.regions;
52377         var north = rs["north"];
52378         var south = rs["south"]; 
52379         var west = rs["west"];
52380         var east = rs["east"];
52381         var center = rs["center"];
52382         //if(this.hideOnLayout){ // not supported anymore
52383             //c.el.setStyle("display", "none");
52384         //}
52385         if(north && north.isVisible()){
52386             var b = north.getBox();
52387             var m = north.getMargins();
52388             b.width = w - (m.left+m.right);
52389             b.x = m.left;
52390             b.y = m.top;
52391             centerY = b.height + b.y + m.bottom;
52392             centerH -= centerY;
52393             north.updateBox(this.safeBox(b));
52394         }
52395         if(south && south.isVisible()){
52396             var b = south.getBox();
52397             var m = south.getMargins();
52398             b.width = w - (m.left+m.right);
52399             b.x = m.left;
52400             var totalHeight = (b.height + m.top + m.bottom);
52401             b.y = h - totalHeight + m.top;
52402             centerH -= totalHeight;
52403             south.updateBox(this.safeBox(b));
52404         }
52405         if(west && west.isVisible()){
52406             var b = west.getBox();
52407             var m = west.getMargins();
52408             b.height = centerH - (m.top+m.bottom);
52409             b.x = m.left;
52410             b.y = centerY + m.top;
52411             var totalWidth = (b.width + m.left + m.right);
52412             centerX += totalWidth;
52413             centerW -= totalWidth;
52414             west.updateBox(this.safeBox(b));
52415         }
52416         if(east && east.isVisible()){
52417             var b = east.getBox();
52418             var m = east.getMargins();
52419             b.height = centerH - (m.top+m.bottom);
52420             var totalWidth = (b.width + m.left + m.right);
52421             b.x = w - totalWidth + m.left;
52422             b.y = centerY + m.top;
52423             centerW -= totalWidth;
52424             east.updateBox(this.safeBox(b));
52425         }
52426         if(center){
52427             var m = center.getMargins();
52428             var centerBox = {
52429                 x: centerX + m.left,
52430                 y: centerY + m.top,
52431                 width: centerW - (m.left+m.right),
52432                 height: centerH - (m.top+m.bottom)
52433             };
52434             //if(this.hideOnLayout){
52435                 //center.el.setStyle("display", "block");
52436             //}
52437             center.updateBox(this.safeBox(centerBox));
52438         }
52439         this.el.repaint();
52440         this.fireEvent("layout", this);
52441     },
52442
52443     // private
52444     safeBox : function(box){
52445         box.width = Math.max(0, box.width);
52446         box.height = Math.max(0, box.height);
52447         return box;
52448     },
52449
52450     /**
52451      * Adds a ContentPanel (or subclass) to this layout.
52452      * @param {String} target The target region key (north, south, east, west or center).
52453      * @param {Roo.ContentPanel} panel The panel to add
52454      * @return {Roo.ContentPanel} The added panel
52455      */
52456     add : function(target, panel){
52457          
52458         target = target.toLowerCase();
52459         return this.regions[target].add(panel);
52460     },
52461
52462     /**
52463      * Remove a ContentPanel (or subclass) to this layout.
52464      * @param {String} target The target region key (north, south, east, west or center).
52465      * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
52466      * @return {Roo.ContentPanel} The removed panel
52467      */
52468     remove : function(target, panel){
52469         target = target.toLowerCase();
52470         return this.regions[target].remove(panel);
52471     },
52472
52473     /**
52474      * Searches all regions for a panel with the specified id
52475      * @param {String} panelId
52476      * @return {Roo.ContentPanel} The panel or null if it wasn't found
52477      */
52478     findPanel : function(panelId){
52479         var rs = this.regions;
52480         for(var target in rs){
52481             if(typeof rs[target] != "function"){
52482                 var p = rs[target].getPanel(panelId);
52483                 if(p){
52484                     return p;
52485                 }
52486             }
52487         }
52488         return null;
52489     },
52490
52491     /**
52492      * Searches all regions for a panel with the specified id and activates (shows) it.
52493      * @param {String/ContentPanel} panelId The panels id or the panel itself
52494      * @return {Roo.ContentPanel} The shown panel or null
52495      */
52496     showPanel : function(panelId) {
52497       var rs = this.regions;
52498       for(var target in rs){
52499          var r = rs[target];
52500          if(typeof r != "function"){
52501             if(r.hasPanel(panelId)){
52502                return r.showPanel(panelId);
52503             }
52504          }
52505       }
52506       return null;
52507    },
52508
52509    /**
52510      * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
52511      * @param {Roo.state.Provider} provider (optional) An alternate state provider
52512      */
52513     restoreState : function(provider){
52514         if(!provider){
52515             provider = Roo.state.Manager;
52516         }
52517         var sm = new Roo.LayoutStateManager();
52518         sm.init(this, provider);
52519     },
52520
52521     /**
52522      * Adds a batch of multiple ContentPanels dynamically by passing a special regions config object.  This config
52523      * object should contain properties for each region to add ContentPanels to, and each property's value should be
52524      * a valid ContentPanel config object.  Example:
52525      * <pre><code>
52526 // Create the main layout
52527 var layout = new Roo.BorderLayout('main-ct', {
52528     west: {
52529         split:true,
52530         minSize: 175,
52531         titlebar: true
52532     },
52533     center: {
52534         title:'Components'
52535     }
52536 }, 'main-ct');
52537
52538 // Create and add multiple ContentPanels at once via configs
52539 layout.batchAdd({
52540    west: {
52541        id: 'source-files',
52542        autoCreate:true,
52543        title:'Ext Source Files',
52544        autoScroll:true,
52545        fitToFrame:true
52546    },
52547    center : {
52548        el: cview,
52549        autoScroll:true,
52550        fitToFrame:true,
52551        toolbar: tb,
52552        resizeEl:'cbody'
52553    }
52554 });
52555 </code></pre>
52556      * @param {Object} regions An object containing ContentPanel configs by region name
52557      */
52558     batchAdd : function(regions){
52559         this.beginUpdate();
52560         for(var rname in regions){
52561             var lr = this.regions[rname];
52562             if(lr){
52563                 this.addTypedPanels(lr, regions[rname]);
52564             }
52565         }
52566         this.endUpdate();
52567     },
52568
52569     // private
52570     addTypedPanels : function(lr, ps){
52571         if(typeof ps == 'string'){
52572             lr.add(new Roo.ContentPanel(ps));
52573         }
52574         else if(ps instanceof Array){
52575             for(var i =0, len = ps.length; i < len; i++){
52576                 this.addTypedPanels(lr, ps[i]);
52577             }
52578         }
52579         else if(!ps.events){ // raw config?
52580             var el = ps.el;
52581             delete ps.el; // prevent conflict
52582             lr.add(new Roo.ContentPanel(el || Roo.id(), ps));
52583         }
52584         else {  // panel object assumed!
52585             lr.add(ps);
52586         }
52587     },
52588     /**
52589      * Adds a xtype elements to the layout.
52590      * <pre><code>
52591
52592 layout.addxtype({
52593        xtype : 'ContentPanel',
52594        region: 'west',
52595        items: [ .... ]
52596    }
52597 );
52598
52599 layout.addxtype({
52600         xtype : 'NestedLayoutPanel',
52601         region: 'west',
52602         layout: {
52603            center: { },
52604            west: { }   
52605         },
52606         items : [ ... list of content panels or nested layout panels.. ]
52607    }
52608 );
52609 </code></pre>
52610      * @param {Object} cfg Xtype definition of item to add.
52611      */
52612     addxtype : function(cfg)
52613     {
52614         // basically accepts a pannel...
52615         // can accept a layout region..!?!?
52616         //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
52617         
52618         if (!cfg.xtype.match(/Panel$/)) {
52619             return false;
52620         }
52621         var ret = false;
52622         
52623         if (typeof(cfg.region) == 'undefined') {
52624             Roo.log("Failed to add Panel, region was not set");
52625             Roo.log(cfg);
52626             return false;
52627         }
52628         var region = cfg.region;
52629         delete cfg.region;
52630         
52631           
52632         var xitems = [];
52633         if (cfg.items) {
52634             xitems = cfg.items;
52635             delete cfg.items;
52636         }
52637         var nb = false;
52638         
52639         switch(cfg.xtype) 
52640         {
52641             case 'ContentPanel':  // ContentPanel (el, cfg)
52642             case 'ScrollPanel':  // ContentPanel (el, cfg)
52643             case 'ViewPanel': 
52644                 if(cfg.autoCreate) {
52645                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
52646                 } else {
52647                     var el = this.el.createChild();
52648                     ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
52649                 }
52650                 
52651                 this.add(region, ret);
52652                 break;
52653             
52654             
52655             case 'TreePanel': // our new panel!
52656                 cfg.el = this.el.createChild();
52657                 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
52658                 this.add(region, ret);
52659                 break;
52660             
52661             case 'NestedLayoutPanel': 
52662                 // create a new Layout (which is  a Border Layout...
52663                 var el = this.el.createChild();
52664                 var clayout = cfg.layout;
52665                 delete cfg.layout;
52666                 clayout.items   = clayout.items  || [];
52667                 // replace this exitems with the clayout ones..
52668                 xitems = clayout.items;
52669                  
52670                 
52671                 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
52672                     cfg.background = false;
52673                 }
52674                 var layout = new Roo.BorderLayout(el, clayout);
52675                 
52676                 ret = new Roo[cfg.xtype](layout, cfg); // new panel!!!!!
52677                 //console.log('adding nested layout panel '  + cfg.toSource());
52678                 this.add(region, ret);
52679                 nb = {}; /// find first...
52680                 break;
52681                 
52682             case 'GridPanel': 
52683             
52684                 // needs grid and region
52685                 
52686                 //var el = this.getRegion(region).el.createChild();
52687                 var el = this.el.createChild();
52688                 // create the grid first...
52689                 
52690                 var grid = new Roo.grid[cfg.grid.xtype](el, cfg.grid);
52691                 delete cfg.grid;
52692                 if (region == 'center' && this.active ) {
52693                     cfg.background = false;
52694                 }
52695                 ret = new Roo[cfg.xtype](grid, cfg); // new panel!!!!!
52696                 
52697                 this.add(region, ret);
52698                 if (cfg.background) {
52699                     ret.on('activate', function(gp) {
52700                         if (!gp.grid.rendered) {
52701                             gp.grid.render();
52702                         }
52703                     });
52704                 } else {
52705                     grid.render();
52706                 }
52707                 break;
52708            
52709            
52710            
52711                 
52712                 
52713                 
52714             default:
52715                 if (typeof(Roo[cfg.xtype]) != 'undefined') {
52716                     
52717                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
52718                     this.add(region, ret);
52719                 } else {
52720                 
52721                     alert("Can not add '" + cfg.xtype + "' to BorderLayout");
52722                     return null;
52723                 }
52724                 
52725              // GridPanel (grid, cfg)
52726             
52727         }
52728         this.beginUpdate();
52729         // add children..
52730         var region = '';
52731         var abn = {};
52732         Roo.each(xitems, function(i)  {
52733             region = nb && i.region ? i.region : false;
52734             
52735             var add = ret.addxtype(i);
52736            
52737             if (region) {
52738                 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
52739                 if (!i.background) {
52740                     abn[region] = nb[region] ;
52741                 }
52742             }
52743             
52744         });
52745         this.endUpdate();
52746
52747         // make the last non-background panel active..
52748         //if (nb) { Roo.log(abn); }
52749         if (nb) {
52750             
52751             for(var r in abn) {
52752                 region = this.getRegion(r);
52753                 if (region) {
52754                     // tried using nb[r], but it does not work..
52755                      
52756                     region.showPanel(abn[r]);
52757                    
52758                 }
52759             }
52760         }
52761         return ret;
52762         
52763     }
52764 });
52765
52766 /**
52767  * Shortcut for creating a new BorderLayout object and adding one or more ContentPanels to it in a single step, handling
52768  * the beginUpdate and endUpdate calls internally.  The key to this method is the <b>panels</b> property that can be
52769  * provided with each region config, which allows you to add ContentPanel configs in addition to the region configs
52770  * during creation.  The following code is equivalent to the constructor-based example at the beginning of this class:
52771  * <pre><code>
52772 // shorthand
52773 var CP = Roo.ContentPanel;
52774
52775 var layout = Roo.BorderLayout.create({
52776     north: {
52777         initialSize: 25,
52778         titlebar: false,
52779         panels: [new CP("north", "North")]
52780     },
52781     west: {
52782         split:true,
52783         initialSize: 200,
52784         minSize: 175,
52785         maxSize: 400,
52786         titlebar: true,
52787         collapsible: true,
52788         panels: [new CP("west", {title: "West"})]
52789     },
52790     east: {
52791         split:true,
52792         initialSize: 202,
52793         minSize: 175,
52794         maxSize: 400,
52795         titlebar: true,
52796         collapsible: true,
52797         panels: [new CP("autoTabs", {title: "Auto Tabs", closable: true})]
52798     },
52799     south: {
52800         split:true,
52801         initialSize: 100,
52802         minSize: 100,
52803         maxSize: 200,
52804         titlebar: true,
52805         collapsible: true,
52806         panels: [new CP("south", {title: "South", closable: true})]
52807     },
52808     center: {
52809         titlebar: true,
52810         autoScroll:true,
52811         resizeTabs: true,
52812         minTabWidth: 50,
52813         preferredTabWidth: 150,
52814         panels: [
52815             new CP("center1", {title: "Close Me", closable: true}),
52816             new CP("center2", {title: "Center Panel", closable: false})
52817         ]
52818     }
52819 }, document.body);
52820
52821 layout.getRegion("center").showPanel("center1");
52822 </code></pre>
52823  * @param config
52824  * @param targetEl
52825  */
52826 Roo.BorderLayout.create = function(config, targetEl){
52827     var layout = new Roo.BorderLayout(targetEl || document.body, config);
52828     layout.beginUpdate();
52829     var regions = Roo.BorderLayout.RegionFactory.validRegions;
52830     for(var j = 0, jlen = regions.length; j < jlen; j++){
52831         var lr = regions[j];
52832         if(layout.regions[lr] && config[lr].panels){
52833             var r = layout.regions[lr];
52834             var ps = config[lr].panels;
52835             layout.addTypedPanels(r, ps);
52836         }
52837     }
52838     layout.endUpdate();
52839     return layout;
52840 };
52841
52842 // private
52843 Roo.BorderLayout.RegionFactory = {
52844     // private
52845     validRegions : ["north","south","east","west","center"],
52846
52847     // private
52848     create : function(target, mgr, config){
52849         target = target.toLowerCase();
52850         if(config.lightweight || config.basic){
52851             return new Roo.BasicLayoutRegion(mgr, config, target);
52852         }
52853         switch(target){
52854             case "north":
52855                 return new Roo.NorthLayoutRegion(mgr, config);
52856             case "south":
52857                 return new Roo.SouthLayoutRegion(mgr, config);
52858             case "east":
52859                 return new Roo.EastLayoutRegion(mgr, config);
52860             case "west":
52861                 return new Roo.WestLayoutRegion(mgr, config);
52862             case "center":
52863                 return new Roo.CenterLayoutRegion(mgr, config);
52864         }
52865         throw 'Layout region "'+target+'" not supported.';
52866     }
52867 };/*
52868  * Based on:
52869  * Ext JS Library 1.1.1
52870  * Copyright(c) 2006-2007, Ext JS, LLC.
52871  *
52872  * Originally Released Under LGPL - original licence link has changed is not relivant.
52873  *
52874  * Fork - LGPL
52875  * <script type="text/javascript">
52876  */
52877  
52878 /**
52879  * @class Roo.BasicLayoutRegion
52880  * @extends Roo.util.Observable
52881  * This class represents a lightweight region in a layout manager. This region does not move dom nodes
52882  * and does not have a titlebar, tabs or any other features. All it does is size and position 
52883  * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
52884  */
52885 Roo.BasicLayoutRegion = function(mgr, config, pos, skipConfig){
52886     this.mgr = mgr;
52887     this.position  = pos;
52888     this.events = {
52889         /**
52890          * @scope Roo.BasicLayoutRegion
52891          */
52892         
52893         /**
52894          * @event beforeremove
52895          * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
52896          * @param {Roo.LayoutRegion} this
52897          * @param {Roo.ContentPanel} panel The panel
52898          * @param {Object} e The cancel event object
52899          */
52900         "beforeremove" : true,
52901         /**
52902          * @event invalidated
52903          * Fires when the layout for this region is changed.
52904          * @param {Roo.LayoutRegion} this
52905          */
52906         "invalidated" : true,
52907         /**
52908          * @event visibilitychange
52909          * Fires when this region is shown or hidden 
52910          * @param {Roo.LayoutRegion} this
52911          * @param {Boolean} visibility true or false
52912          */
52913         "visibilitychange" : true,
52914         /**
52915          * @event paneladded
52916          * Fires when a panel is added. 
52917          * @param {Roo.LayoutRegion} this
52918          * @param {Roo.ContentPanel} panel The panel
52919          */
52920         "paneladded" : true,
52921         /**
52922          * @event panelremoved
52923          * Fires when a panel is removed. 
52924          * @param {Roo.LayoutRegion} this
52925          * @param {Roo.ContentPanel} panel The panel
52926          */
52927         "panelremoved" : true,
52928         /**
52929          * @event beforecollapse
52930          * Fires when this region before collapse.
52931          * @param {Roo.LayoutRegion} this
52932          */
52933         "beforecollapse" : true,
52934         /**
52935          * @event collapsed
52936          * Fires when this region is collapsed.
52937          * @param {Roo.LayoutRegion} this
52938          */
52939         "collapsed" : true,
52940         /**
52941          * @event expanded
52942          * Fires when this region is expanded.
52943          * @param {Roo.LayoutRegion} this
52944          */
52945         "expanded" : true,
52946         /**
52947          * @event slideshow
52948          * Fires when this region is slid into view.
52949          * @param {Roo.LayoutRegion} this
52950          */
52951         "slideshow" : true,
52952         /**
52953          * @event slidehide
52954          * Fires when this region slides out of view. 
52955          * @param {Roo.LayoutRegion} this
52956          */
52957         "slidehide" : true,
52958         /**
52959          * @event panelactivated
52960          * Fires when a panel is activated. 
52961          * @param {Roo.LayoutRegion} this
52962          * @param {Roo.ContentPanel} panel The activated panel
52963          */
52964         "panelactivated" : true,
52965         /**
52966          * @event resized
52967          * Fires when the user resizes this region. 
52968          * @param {Roo.LayoutRegion} this
52969          * @param {Number} newSize The new size (width for east/west, height for north/south)
52970          */
52971         "resized" : true
52972     };
52973     /** A collection of panels in this region. @type Roo.util.MixedCollection */
52974     this.panels = new Roo.util.MixedCollection();
52975     this.panels.getKey = this.getPanelId.createDelegate(this);
52976     this.box = null;
52977     this.activePanel = null;
52978     // ensure listeners are added...
52979     
52980     if (config.listeners || config.events) {
52981         Roo.BasicLayoutRegion.superclass.constructor.call(this, {
52982             listeners : config.listeners || {},
52983             events : config.events || {}
52984         });
52985     }
52986     
52987     if(skipConfig !== true){
52988         this.applyConfig(config);
52989     }
52990 };
52991
52992 Roo.extend(Roo.BasicLayoutRegion, Roo.util.Observable, {
52993     getPanelId : function(p){
52994         return p.getId();
52995     },
52996     
52997     applyConfig : function(config){
52998         this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
52999         this.config = config;
53000         
53001     },
53002     
53003     /**
53004      * Resizes the region to the specified size. For vertical regions (west, east) this adjusts 
53005      * the width, for horizontal (north, south) the height.
53006      * @param {Number} newSize The new width or height
53007      */
53008     resizeTo : function(newSize){
53009         var el = this.el ? this.el :
53010                  (this.activePanel ? this.activePanel.getEl() : null);
53011         if(el){
53012             switch(this.position){
53013                 case "east":
53014                 case "west":
53015                     el.setWidth(newSize);
53016                     this.fireEvent("resized", this, newSize);
53017                 break;
53018                 case "north":
53019                 case "south":
53020                     el.setHeight(newSize);
53021                     this.fireEvent("resized", this, newSize);
53022                 break;                
53023             }
53024         }
53025     },
53026     
53027     getBox : function(){
53028         return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
53029     },
53030     
53031     getMargins : function(){
53032         return this.margins;
53033     },
53034     
53035     updateBox : function(box){
53036         this.box = box;
53037         var el = this.activePanel.getEl();
53038         el.dom.style.left = box.x + "px";
53039         el.dom.style.top = box.y + "px";
53040         this.activePanel.setSize(box.width, box.height);
53041     },
53042     
53043     /**
53044      * Returns the container element for this region.
53045      * @return {Roo.Element}
53046      */
53047     getEl : function(){
53048         return this.activePanel;
53049     },
53050     
53051     /**
53052      * Returns true if this region is currently visible.
53053      * @return {Boolean}
53054      */
53055     isVisible : function(){
53056         return this.activePanel ? true : false;
53057     },
53058     
53059     setActivePanel : function(panel){
53060         panel = this.getPanel(panel);
53061         if(this.activePanel && this.activePanel != panel){
53062             this.activePanel.setActiveState(false);
53063             this.activePanel.getEl().setLeftTop(-10000,-10000);
53064         }
53065         this.activePanel = panel;
53066         panel.setActiveState(true);
53067         if(this.box){
53068             panel.setSize(this.box.width, this.box.height);
53069         }
53070         this.fireEvent("panelactivated", this, panel);
53071         this.fireEvent("invalidated");
53072     },
53073     
53074     /**
53075      * Show the specified panel.
53076      * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
53077      * @return {Roo.ContentPanel} The shown panel or null
53078      */
53079     showPanel : function(panel){
53080         if(panel = this.getPanel(panel)){
53081             this.setActivePanel(panel);
53082         }
53083         return panel;
53084     },
53085     
53086     /**
53087      * Get the active panel for this region.
53088      * @return {Roo.ContentPanel} The active panel or null
53089      */
53090     getActivePanel : function(){
53091         return this.activePanel;
53092     },
53093     
53094     /**
53095      * Add the passed ContentPanel(s)
53096      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
53097      * @return {Roo.ContentPanel} The panel added (if only one was added)
53098      */
53099     add : function(panel){
53100         if(arguments.length > 1){
53101             for(var i = 0, len = arguments.length; i < len; i++) {
53102                 this.add(arguments[i]);
53103             }
53104             return null;
53105         }
53106         if(this.hasPanel(panel)){
53107             this.showPanel(panel);
53108             return panel;
53109         }
53110         var el = panel.getEl();
53111         if(el.dom.parentNode != this.mgr.el.dom){
53112             this.mgr.el.dom.appendChild(el.dom);
53113         }
53114         if(panel.setRegion){
53115             panel.setRegion(this);
53116         }
53117         this.panels.add(panel);
53118         el.setStyle("position", "absolute");
53119         if(!panel.background){
53120             this.setActivePanel(panel);
53121             if(this.config.initialSize && this.panels.getCount()==1){
53122                 this.resizeTo(this.config.initialSize);
53123             }
53124         }
53125         this.fireEvent("paneladded", this, panel);
53126         return panel;
53127     },
53128     
53129     /**
53130      * Returns true if the panel is in this region.
53131      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
53132      * @return {Boolean}
53133      */
53134     hasPanel : function(panel){
53135         if(typeof panel == "object"){ // must be panel obj
53136             panel = panel.getId();
53137         }
53138         return this.getPanel(panel) ? true : false;
53139     },
53140     
53141     /**
53142      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
53143      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
53144      * @param {Boolean} preservePanel Overrides the config preservePanel option
53145      * @return {Roo.ContentPanel} The panel that was removed
53146      */
53147     remove : function(panel, preservePanel){
53148         panel = this.getPanel(panel);
53149         if(!panel){
53150             return null;
53151         }
53152         var e = {};
53153         this.fireEvent("beforeremove", this, panel, e);
53154         if(e.cancel === true){
53155             return null;
53156         }
53157         var panelId = panel.getId();
53158         this.panels.removeKey(panelId);
53159         return panel;
53160     },
53161     
53162     /**
53163      * Returns the panel specified or null if it's not in this region.
53164      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
53165      * @return {Roo.ContentPanel}
53166      */
53167     getPanel : function(id){
53168         if(typeof id == "object"){ // must be panel obj
53169             return id;
53170         }
53171         return this.panels.get(id);
53172     },
53173     
53174     /**
53175      * Returns this regions position (north/south/east/west/center).
53176      * @return {String} 
53177      */
53178     getPosition: function(){
53179         return this.position;    
53180     }
53181 });/*
53182  * Based on:
53183  * Ext JS Library 1.1.1
53184  * Copyright(c) 2006-2007, Ext JS, LLC.
53185  *
53186  * Originally Released Under LGPL - original licence link has changed is not relivant.
53187  *
53188  * Fork - LGPL
53189  * <script type="text/javascript">
53190  */
53191  
53192 /**
53193  * @class Roo.LayoutRegion
53194  * @extends Roo.BasicLayoutRegion
53195  * This class represents a region in a layout manager.
53196  * @cfg {Boolean}   collapsible     False to disable collapsing (defaults to true)
53197  * @cfg {Boolean}   collapsed       True to set the initial display to collapsed (defaults to false)
53198  * @cfg {Boolean}   floatable       False to disable floating (defaults to true)
53199  * @cfg {Object}    margins         Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
53200  * @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})
53201  * @cfg {String}    tabPosition     (top|bottom) "top" or "bottom" (defaults to "bottom")
53202  * @cfg {String}    collapsedTitle  Optional string message to display in the collapsed block of a north or south region
53203  * @cfg {Boolean}   alwaysShowTabs  True to always display tabs even when there is only 1 panel (defaults to false)
53204  * @cfg {Boolean}   autoScroll      True to enable overflow scrolling (defaults to false)
53205  * @cfg {Boolean}   titlebar        True to display a title bar (defaults to true)
53206  * @cfg {String}    title           The title for the region (overrides panel titles)
53207  * @cfg {Boolean}   animate         True to animate expand/collapse (defaults to false)
53208  * @cfg {Boolean}   autoHide        False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
53209  * @cfg {Boolean}   preservePanels  True to preserve removed panels so they can be readded later (defaults to false)
53210  * @cfg {Boolean}   closeOnTab      True to place the close icon on the tabs instead of the region titlebar (defaults to false)
53211  * @cfg {Boolean}   hideTabs        True to hide the tab strip (defaults to false)
53212  * @cfg {Boolean}   resizeTabs      True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
53213  *                      the space available, similar to FireFox 1.5 tabs (defaults to false)
53214  * @cfg {Number}    minTabWidth     The minimum tab width (defaults to 40)
53215  * @cfg {Number}    preferredTabWidth The preferred tab width (defaults to 150)
53216  * @cfg {Boolean}   showPin         True to show a pin button
53217  * @cfg {Boolean}   hidden          True to start the region hidden (defaults to false)
53218  * @cfg {Boolean}   hideWhenEmpty   True to hide the region when it has no panels
53219  * @cfg {Boolean}   disableTabTips  True to disable tab tooltips
53220  * @cfg {Number}    width           For East/West panels
53221  * @cfg {Number}    height          For North/South panels
53222  * @cfg {Boolean}   split           To show the splitter
53223  * @cfg {Boolean}   toolbar         xtype configuration for a toolbar - shows on right of tabbar
53224  */
53225 Roo.LayoutRegion = function(mgr, config, pos){
53226     Roo.LayoutRegion.superclass.constructor.call(this, mgr, config, pos, true);
53227     var dh = Roo.DomHelper;
53228     /** This region's container element 
53229     * @type Roo.Element */
53230     this.el = dh.append(mgr.el.dom, {tag: "div", cls: "x-layout-panel x-layout-panel-" + this.position}, true);
53231     /** This region's title element 
53232     * @type Roo.Element */
53233
53234     this.titleEl = dh.append(this.el.dom, {tag: "div", unselectable: "on", cls: "x-unselectable x-layout-panel-hd x-layout-title-"+this.position, children:[
53235         {tag: "span", cls: "x-unselectable x-layout-panel-hd-text", unselectable: "on", html: "&#160;"},
53236         {tag: "div", cls: "x-unselectable x-layout-panel-hd-tools", unselectable: "on"}
53237     ]}, true);
53238     this.titleEl.enableDisplayMode();
53239     /** This region's title text element 
53240     * @type HTMLElement */
53241     this.titleTextEl = this.titleEl.dom.firstChild;
53242     this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
53243     this.closeBtn = this.createTool(this.tools.dom, "x-layout-close");
53244     this.closeBtn.enableDisplayMode();
53245     this.closeBtn.on("click", this.closeClicked, this);
53246     this.closeBtn.hide();
53247
53248     this.createBody(config);
53249     this.visible = true;
53250     this.collapsed = false;
53251
53252     if(config.hideWhenEmpty){
53253         this.hide();
53254         this.on("paneladded", this.validateVisibility, this);
53255         this.on("panelremoved", this.validateVisibility, this);
53256     }
53257     this.applyConfig(config);
53258 };
53259
53260 Roo.extend(Roo.LayoutRegion, Roo.BasicLayoutRegion, {
53261
53262     createBody : function(){
53263         /** This region's body element 
53264         * @type Roo.Element */
53265         this.bodyEl = this.el.createChild({tag: "div", cls: "x-layout-panel-body"});
53266     },
53267
53268     applyConfig : function(c){
53269         if(c.collapsible && this.position != "center" && !this.collapsedEl){
53270             var dh = Roo.DomHelper;
53271             if(c.titlebar !== false){
53272                 this.collapseBtn = this.createTool(this.tools.dom, "x-layout-collapse-"+this.position);
53273                 this.collapseBtn.on("click", this.collapse, this);
53274                 this.collapseBtn.enableDisplayMode();
53275
53276                 if(c.showPin === true || this.showPin){
53277                     this.stickBtn = this.createTool(this.tools.dom, "x-layout-stick");
53278                     this.stickBtn.enableDisplayMode();
53279                     this.stickBtn.on("click", this.expand, this);
53280                     this.stickBtn.hide();
53281                 }
53282             }
53283             /** This region's collapsed element
53284             * @type Roo.Element */
53285             this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
53286                 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
53287             ]}, true);
53288             if(c.floatable !== false){
53289                this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
53290                this.collapsedEl.on("click", this.collapseClick, this);
53291             }
53292
53293             if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
53294                 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
53295                    id: "message", unselectable: "on", style:{"float":"left"}});
53296                this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
53297              }
53298             this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
53299             this.expandBtn.on("click", this.expand, this);
53300         }
53301         if(this.collapseBtn){
53302             this.collapseBtn.setVisible(c.collapsible == true);
53303         }
53304         this.cmargins = c.cmargins || this.cmargins ||
53305                          (this.position == "west" || this.position == "east" ?
53306                              {top: 0, left: 2, right:2, bottom: 0} :
53307                              {top: 2, left: 0, right:0, bottom: 2});
53308         this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
53309         this.bottomTabs = c.tabPosition != "top";
53310         this.autoScroll = c.autoScroll || false;
53311         if(this.autoScroll){
53312             this.bodyEl.setStyle("overflow", "auto");
53313         }else{
53314             this.bodyEl.setStyle("overflow", "hidden");
53315         }
53316         //if(c.titlebar !== false){
53317             if((!c.titlebar && !c.title) || c.titlebar === false){
53318                 this.titleEl.hide();
53319             }else{
53320                 this.titleEl.show();
53321                 if(c.title){
53322                     this.titleTextEl.innerHTML = c.title;
53323                 }
53324             }
53325         //}
53326         this.duration = c.duration || .30;
53327         this.slideDuration = c.slideDuration || .45;
53328         this.config = c;
53329         if(c.collapsed){
53330             this.collapse(true);
53331         }
53332         if(c.hidden){
53333             this.hide();
53334         }
53335     },
53336     /**
53337      * Returns true if this region is currently visible.
53338      * @return {Boolean}
53339      */
53340     isVisible : function(){
53341         return this.visible;
53342     },
53343
53344     /**
53345      * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
53346      * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&amp;#160;")
53347      */
53348     setCollapsedTitle : function(title){
53349         title = title || "&#160;";
53350         if(this.collapsedTitleTextEl){
53351             this.collapsedTitleTextEl.innerHTML = title;
53352         }
53353     },
53354
53355     getBox : function(){
53356         var b;
53357         if(!this.collapsed){
53358             b = this.el.getBox(false, true);
53359         }else{
53360             b = this.collapsedEl.getBox(false, true);
53361         }
53362         return b;
53363     },
53364
53365     getMargins : function(){
53366         return this.collapsed ? this.cmargins : this.margins;
53367     },
53368
53369     highlight : function(){
53370         this.el.addClass("x-layout-panel-dragover");
53371     },
53372
53373     unhighlight : function(){
53374         this.el.removeClass("x-layout-panel-dragover");
53375     },
53376
53377     updateBox : function(box){
53378         this.box = box;
53379         if(!this.collapsed){
53380             this.el.dom.style.left = box.x + "px";
53381             this.el.dom.style.top = box.y + "px";
53382             this.updateBody(box.width, box.height);
53383         }else{
53384             this.collapsedEl.dom.style.left = box.x + "px";
53385             this.collapsedEl.dom.style.top = box.y + "px";
53386             this.collapsedEl.setSize(box.width, box.height);
53387         }
53388         if(this.tabs){
53389             this.tabs.autoSizeTabs();
53390         }
53391     },
53392
53393     updateBody : function(w, h){
53394         if(w !== null){
53395             this.el.setWidth(w);
53396             w -= this.el.getBorderWidth("rl");
53397             if(this.config.adjustments){
53398                 w += this.config.adjustments[0];
53399             }
53400         }
53401         if(h !== null){
53402             this.el.setHeight(h);
53403             h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
53404             h -= this.el.getBorderWidth("tb");
53405             if(this.config.adjustments){
53406                 h += this.config.adjustments[1];
53407             }
53408             this.bodyEl.setHeight(h);
53409             if(this.tabs){
53410                 h = this.tabs.syncHeight(h);
53411             }
53412         }
53413         if(this.panelSize){
53414             w = w !== null ? w : this.panelSize.width;
53415             h = h !== null ? h : this.panelSize.height;
53416         }
53417         if(this.activePanel){
53418             var el = this.activePanel.getEl();
53419             w = w !== null ? w : el.getWidth();
53420             h = h !== null ? h : el.getHeight();
53421             this.panelSize = {width: w, height: h};
53422             this.activePanel.setSize(w, h);
53423         }
53424         if(Roo.isIE && this.tabs){
53425             this.tabs.el.repaint();
53426         }
53427     },
53428
53429     /**
53430      * Returns the container element for this region.
53431      * @return {Roo.Element}
53432      */
53433     getEl : function(){
53434         return this.el;
53435     },
53436
53437     /**
53438      * Hides this region.
53439      */
53440     hide : function(){
53441         if(!this.collapsed){
53442             this.el.dom.style.left = "-2000px";
53443             this.el.hide();
53444         }else{
53445             this.collapsedEl.dom.style.left = "-2000px";
53446             this.collapsedEl.hide();
53447         }
53448         this.visible = false;
53449         this.fireEvent("visibilitychange", this, false);
53450     },
53451
53452     /**
53453      * Shows this region if it was previously hidden.
53454      */
53455     show : function(){
53456         if(!this.collapsed){
53457             this.el.show();
53458         }else{
53459             this.collapsedEl.show();
53460         }
53461         this.visible = true;
53462         this.fireEvent("visibilitychange", this, true);
53463     },
53464
53465     closeClicked : function(){
53466         if(this.activePanel){
53467             this.remove(this.activePanel);
53468         }
53469     },
53470
53471     collapseClick : function(e){
53472         if(this.isSlid){
53473            e.stopPropagation();
53474            this.slideIn();
53475         }else{
53476            e.stopPropagation();
53477            this.slideOut();
53478         }
53479     },
53480
53481     /**
53482      * Collapses this region.
53483      * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
53484      */
53485     collapse : function(skipAnim, skipCheck){
53486         if(this.collapsed) {
53487             return;
53488         }
53489         
53490         if(skipCheck || this.fireEvent("beforecollapse", this) != false){
53491             
53492             this.collapsed = true;
53493             if(this.split){
53494                 this.split.el.hide();
53495             }
53496             if(this.config.animate && skipAnim !== true){
53497                 this.fireEvent("invalidated", this);
53498                 this.animateCollapse();
53499             }else{
53500                 this.el.setLocation(-20000,-20000);
53501                 this.el.hide();
53502                 this.collapsedEl.show();
53503                 this.fireEvent("collapsed", this);
53504                 this.fireEvent("invalidated", this);
53505             }
53506         }
53507         
53508     },
53509
53510     animateCollapse : function(){
53511         // overridden
53512     },
53513
53514     /**
53515      * Expands this region if it was previously collapsed.
53516      * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
53517      * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
53518      */
53519     expand : function(e, skipAnim){
53520         if(e) {
53521             e.stopPropagation();
53522         }
53523         if(!this.collapsed || this.el.hasActiveFx()) {
53524             return;
53525         }
53526         if(this.isSlid){
53527             this.afterSlideIn();
53528             skipAnim = true;
53529         }
53530         this.collapsed = false;
53531         if(this.config.animate && skipAnim !== true){
53532             this.animateExpand();
53533         }else{
53534             this.el.show();
53535             if(this.split){
53536                 this.split.el.show();
53537             }
53538             this.collapsedEl.setLocation(-2000,-2000);
53539             this.collapsedEl.hide();
53540             this.fireEvent("invalidated", this);
53541             this.fireEvent("expanded", this);
53542         }
53543     },
53544
53545     animateExpand : function(){
53546         // overridden
53547     },
53548
53549     initTabs : function()
53550     {
53551         this.bodyEl.setStyle("overflow", "hidden");
53552         var ts = new Roo.TabPanel(
53553                 this.bodyEl.dom,
53554                 {
53555                     tabPosition: this.bottomTabs ? 'bottom' : 'top',
53556                     disableTooltips: this.config.disableTabTips,
53557                     toolbar : this.config.toolbar
53558                 }
53559         );
53560         if(this.config.hideTabs){
53561             ts.stripWrap.setDisplayed(false);
53562         }
53563         this.tabs = ts;
53564         ts.resizeTabs = this.config.resizeTabs === true;
53565         ts.minTabWidth = this.config.minTabWidth || 40;
53566         ts.maxTabWidth = this.config.maxTabWidth || 250;
53567         ts.preferredTabWidth = this.config.preferredTabWidth || 150;
53568         ts.monitorResize = false;
53569         ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
53570         ts.bodyEl.addClass('x-layout-tabs-body');
53571         this.panels.each(this.initPanelAsTab, this);
53572     },
53573
53574     initPanelAsTab : function(panel){
53575         var ti = this.tabs.addTab(panel.getEl().id, panel.getTitle(), null,
53576                     this.config.closeOnTab && panel.isClosable());
53577         if(panel.tabTip !== undefined){
53578             ti.setTooltip(panel.tabTip);
53579         }
53580         ti.on("activate", function(){
53581               this.setActivePanel(panel);
53582         }, this);
53583         if(this.config.closeOnTab){
53584             ti.on("beforeclose", function(t, e){
53585                 e.cancel = true;
53586                 this.remove(panel);
53587             }, this);
53588         }
53589         return ti;
53590     },
53591
53592     updatePanelTitle : function(panel, title){
53593         if(this.activePanel == panel){
53594             this.updateTitle(title);
53595         }
53596         if(this.tabs){
53597             var ti = this.tabs.getTab(panel.getEl().id);
53598             ti.setText(title);
53599             if(panel.tabTip !== undefined){
53600                 ti.setTooltip(panel.tabTip);
53601             }
53602         }
53603     },
53604
53605     updateTitle : function(title){
53606         if(this.titleTextEl && !this.config.title){
53607             this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : "&#160;");
53608         }
53609     },
53610
53611     setActivePanel : function(panel){
53612         panel = this.getPanel(panel);
53613         if(this.activePanel && this.activePanel != panel){
53614             this.activePanel.setActiveState(false);
53615         }
53616         this.activePanel = panel;
53617         panel.setActiveState(true);
53618         if(this.panelSize){
53619             panel.setSize(this.panelSize.width, this.panelSize.height);
53620         }
53621         if(this.closeBtn){
53622             this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
53623         }
53624         this.updateTitle(panel.getTitle());
53625         if(this.tabs){
53626             this.fireEvent("invalidated", this);
53627         }
53628         this.fireEvent("panelactivated", this, panel);
53629     },
53630
53631     /**
53632      * Shows the specified panel.
53633      * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
53634      * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
53635      */
53636     showPanel : function(panel)
53637     {
53638         panel = this.getPanel(panel);
53639         if(panel){
53640             if(this.tabs){
53641                 var tab = this.tabs.getTab(panel.getEl().id);
53642                 if(tab.isHidden()){
53643                     this.tabs.unhideTab(tab.id);
53644                 }
53645                 tab.activate();
53646             }else{
53647                 this.setActivePanel(panel);
53648             }
53649         }
53650         return panel;
53651     },
53652
53653     /**
53654      * Get the active panel for this region.
53655      * @return {Roo.ContentPanel} The active panel or null
53656      */
53657     getActivePanel : function(){
53658         return this.activePanel;
53659     },
53660
53661     validateVisibility : function(){
53662         if(this.panels.getCount() < 1){
53663             this.updateTitle("&#160;");
53664             this.closeBtn.hide();
53665             this.hide();
53666         }else{
53667             if(!this.isVisible()){
53668                 this.show();
53669             }
53670         }
53671     },
53672
53673     /**
53674      * Adds the passed ContentPanel(s) to this region.
53675      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
53676      * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
53677      */
53678     add : function(panel){
53679         if(arguments.length > 1){
53680             for(var i = 0, len = arguments.length; i < len; i++) {
53681                 this.add(arguments[i]);
53682             }
53683             return null;
53684         }
53685         if(this.hasPanel(panel)){
53686             this.showPanel(panel);
53687             return panel;
53688         }
53689         panel.setRegion(this);
53690         this.panels.add(panel);
53691         if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
53692             this.bodyEl.dom.appendChild(panel.getEl().dom);
53693             if(panel.background !== true){
53694                 this.setActivePanel(panel);
53695             }
53696             this.fireEvent("paneladded", this, panel);
53697             return panel;
53698         }
53699         if(!this.tabs){
53700             this.initTabs();
53701         }else{
53702             this.initPanelAsTab(panel);
53703         }
53704         if(panel.background !== true){
53705             this.tabs.activate(panel.getEl().id);
53706         }
53707         this.fireEvent("paneladded", this, panel);
53708         return panel;
53709     },
53710
53711     /**
53712      * Hides the tab for the specified panel.
53713      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
53714      */
53715     hidePanel : function(panel){
53716         if(this.tabs && (panel = this.getPanel(panel))){
53717             this.tabs.hideTab(panel.getEl().id);
53718         }
53719     },
53720
53721     /**
53722      * Unhides the tab for a previously hidden panel.
53723      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
53724      */
53725     unhidePanel : function(panel){
53726         if(this.tabs && (panel = this.getPanel(panel))){
53727             this.tabs.unhideTab(panel.getEl().id);
53728         }
53729     },
53730
53731     clearPanels : function(){
53732         while(this.panels.getCount() > 0){
53733              this.remove(this.panels.first());
53734         }
53735     },
53736
53737     /**
53738      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
53739      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
53740      * @param {Boolean} preservePanel Overrides the config preservePanel option
53741      * @return {Roo.ContentPanel} The panel that was removed
53742      */
53743     remove : function(panel, preservePanel){
53744         panel = this.getPanel(panel);
53745         if(!panel){
53746             return null;
53747         }
53748         var e = {};
53749         this.fireEvent("beforeremove", this, panel, e);
53750         if(e.cancel === true){
53751             return null;
53752         }
53753         preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
53754         var panelId = panel.getId();
53755         this.panels.removeKey(panelId);
53756         if(preservePanel){
53757             document.body.appendChild(panel.getEl().dom);
53758         }
53759         if(this.tabs){
53760             this.tabs.removeTab(panel.getEl().id);
53761         }else if (!preservePanel){
53762             this.bodyEl.dom.removeChild(panel.getEl().dom);
53763         }
53764         if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
53765             var p = this.panels.first();
53766             var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
53767             tempEl.appendChild(p.getEl().dom);
53768             this.bodyEl.update("");
53769             this.bodyEl.dom.appendChild(p.getEl().dom);
53770             tempEl = null;
53771             this.updateTitle(p.getTitle());
53772             this.tabs = null;
53773             this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
53774             this.setActivePanel(p);
53775         }
53776         panel.setRegion(null);
53777         if(this.activePanel == panel){
53778             this.activePanel = null;
53779         }
53780         if(this.config.autoDestroy !== false && preservePanel !== true){
53781             try{panel.destroy();}catch(e){}
53782         }
53783         this.fireEvent("panelremoved", this, panel);
53784         return panel;
53785     },
53786
53787     /**
53788      * Returns the TabPanel component used by this region
53789      * @return {Roo.TabPanel}
53790      */
53791     getTabs : function(){
53792         return this.tabs;
53793     },
53794
53795     createTool : function(parentEl, className){
53796         var btn = Roo.DomHelper.append(parentEl, {tag: "div", cls: "x-layout-tools-button",
53797             children: [{tag: "div", cls: "x-layout-tools-button-inner " + className, html: "&#160;"}]}, true);
53798         btn.addClassOnOver("x-layout-tools-button-over");
53799         return btn;
53800     }
53801 });/*
53802  * Based on:
53803  * Ext JS Library 1.1.1
53804  * Copyright(c) 2006-2007, Ext JS, LLC.
53805  *
53806  * Originally Released Under LGPL - original licence link has changed is not relivant.
53807  *
53808  * Fork - LGPL
53809  * <script type="text/javascript">
53810  */
53811  
53812
53813
53814 /**
53815  * @class Roo.SplitLayoutRegion
53816  * @extends Roo.LayoutRegion
53817  * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
53818  */
53819 Roo.SplitLayoutRegion = function(mgr, config, pos, cursor){
53820     this.cursor = cursor;
53821     Roo.SplitLayoutRegion.superclass.constructor.call(this, mgr, config, pos);
53822 };
53823
53824 Roo.extend(Roo.SplitLayoutRegion, Roo.LayoutRegion, {
53825     splitTip : "Drag to resize.",
53826     collapsibleSplitTip : "Drag to resize. Double click to hide.",
53827     useSplitTips : false,
53828
53829     applyConfig : function(config){
53830         Roo.SplitLayoutRegion.superclass.applyConfig.call(this, config);
53831         if(config.split){
53832             if(!this.split){
53833                 var splitEl = Roo.DomHelper.append(this.mgr.el.dom, 
53834                         {tag: "div", id: this.el.id + "-split", cls: "x-layout-split x-layout-split-"+this.position, html: "&#160;"});
53835                 /** The SplitBar for this region 
53836                 * @type Roo.SplitBar */
53837                 this.split = new Roo.SplitBar(splitEl, this.el, this.orientation);
53838                 this.split.on("moved", this.onSplitMove, this);
53839                 this.split.useShim = config.useShim === true;
53840                 this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
53841                 if(this.useSplitTips){
53842                     this.split.el.dom.title = config.collapsible ? this.collapsibleSplitTip : this.splitTip;
53843                 }
53844                 if(config.collapsible){
53845                     this.split.el.on("dblclick", this.collapse,  this);
53846                 }
53847             }
53848             if(typeof config.minSize != "undefined"){
53849                 this.split.minSize = config.minSize;
53850             }
53851             if(typeof config.maxSize != "undefined"){
53852                 this.split.maxSize = config.maxSize;
53853             }
53854             if(config.hideWhenEmpty || config.hidden || config.collapsed){
53855                 this.hideSplitter();
53856             }
53857         }
53858     },
53859
53860     getHMaxSize : function(){
53861          var cmax = this.config.maxSize || 10000;
53862          var center = this.mgr.getRegion("center");
53863          return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
53864     },
53865
53866     getVMaxSize : function(){
53867          var cmax = this.config.maxSize || 10000;
53868          var center = this.mgr.getRegion("center");
53869          return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
53870     },
53871
53872     onSplitMove : function(split, newSize){
53873         this.fireEvent("resized", this, newSize);
53874     },
53875     
53876     /** 
53877      * Returns the {@link Roo.SplitBar} for this region.
53878      * @return {Roo.SplitBar}
53879      */
53880     getSplitBar : function(){
53881         return this.split;
53882     },
53883     
53884     hide : function(){
53885         this.hideSplitter();
53886         Roo.SplitLayoutRegion.superclass.hide.call(this);
53887     },
53888
53889     hideSplitter : function(){
53890         if(this.split){
53891             this.split.el.setLocation(-2000,-2000);
53892             this.split.el.hide();
53893         }
53894     },
53895
53896     show : function(){
53897         if(this.split){
53898             this.split.el.show();
53899         }
53900         Roo.SplitLayoutRegion.superclass.show.call(this);
53901     },
53902     
53903     beforeSlide: function(){
53904         if(Roo.isGecko){// firefox overflow auto bug workaround
53905             this.bodyEl.clip();
53906             if(this.tabs) {
53907                 this.tabs.bodyEl.clip();
53908             }
53909             if(this.activePanel){
53910                 this.activePanel.getEl().clip();
53911                 
53912                 if(this.activePanel.beforeSlide){
53913                     this.activePanel.beforeSlide();
53914                 }
53915             }
53916         }
53917     },
53918     
53919     afterSlide : function(){
53920         if(Roo.isGecko){// firefox overflow auto bug workaround
53921             this.bodyEl.unclip();
53922             if(this.tabs) {
53923                 this.tabs.bodyEl.unclip();
53924             }
53925             if(this.activePanel){
53926                 this.activePanel.getEl().unclip();
53927                 if(this.activePanel.afterSlide){
53928                     this.activePanel.afterSlide();
53929                 }
53930             }
53931         }
53932     },
53933
53934     initAutoHide : function(){
53935         if(this.autoHide !== false){
53936             if(!this.autoHideHd){
53937                 var st = new Roo.util.DelayedTask(this.slideIn, this);
53938                 this.autoHideHd = {
53939                     "mouseout": function(e){
53940                         if(!e.within(this.el, true)){
53941                             st.delay(500);
53942                         }
53943                     },
53944                     "mouseover" : function(e){
53945                         st.cancel();
53946                     },
53947                     scope : this
53948                 };
53949             }
53950             this.el.on(this.autoHideHd);
53951         }
53952     },
53953
53954     clearAutoHide : function(){
53955         if(this.autoHide !== false){
53956             this.el.un("mouseout", this.autoHideHd.mouseout);
53957             this.el.un("mouseover", this.autoHideHd.mouseover);
53958         }
53959     },
53960
53961     clearMonitor : function(){
53962         Roo.get(document).un("click", this.slideInIf, this);
53963     },
53964
53965     // these names are backwards but not changed for compat
53966     slideOut : function(){
53967         if(this.isSlid || this.el.hasActiveFx()){
53968             return;
53969         }
53970         this.isSlid = true;
53971         if(this.collapseBtn){
53972             this.collapseBtn.hide();
53973         }
53974         this.closeBtnState = this.closeBtn.getStyle('display');
53975         this.closeBtn.hide();
53976         if(this.stickBtn){
53977             this.stickBtn.show();
53978         }
53979         this.el.show();
53980         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
53981         this.beforeSlide();
53982         this.el.setStyle("z-index", 10001);
53983         this.el.slideIn(this.getSlideAnchor(), {
53984             callback: function(){
53985                 this.afterSlide();
53986                 this.initAutoHide();
53987                 Roo.get(document).on("click", this.slideInIf, this);
53988                 this.fireEvent("slideshow", this);
53989             },
53990             scope: this,
53991             block: true
53992         });
53993     },
53994
53995     afterSlideIn : function(){
53996         this.clearAutoHide();
53997         this.isSlid = false;
53998         this.clearMonitor();
53999         this.el.setStyle("z-index", "");
54000         if(this.collapseBtn){
54001             this.collapseBtn.show();
54002         }
54003         this.closeBtn.setStyle('display', this.closeBtnState);
54004         if(this.stickBtn){
54005             this.stickBtn.hide();
54006         }
54007         this.fireEvent("slidehide", this);
54008     },
54009
54010     slideIn : function(cb){
54011         if(!this.isSlid || this.el.hasActiveFx()){
54012             Roo.callback(cb);
54013             return;
54014         }
54015         this.isSlid = false;
54016         this.beforeSlide();
54017         this.el.slideOut(this.getSlideAnchor(), {
54018             callback: function(){
54019                 this.el.setLeftTop(-10000, -10000);
54020                 this.afterSlide();
54021                 this.afterSlideIn();
54022                 Roo.callback(cb);
54023             },
54024             scope: this,
54025             block: true
54026         });
54027     },
54028     
54029     slideInIf : function(e){
54030         if(!e.within(this.el)){
54031             this.slideIn();
54032         }
54033     },
54034
54035     animateCollapse : function(){
54036         this.beforeSlide();
54037         this.el.setStyle("z-index", 20000);
54038         var anchor = this.getSlideAnchor();
54039         this.el.slideOut(anchor, {
54040             callback : function(){
54041                 this.el.setStyle("z-index", "");
54042                 this.collapsedEl.slideIn(anchor, {duration:.3});
54043                 this.afterSlide();
54044                 this.el.setLocation(-10000,-10000);
54045                 this.el.hide();
54046                 this.fireEvent("collapsed", this);
54047             },
54048             scope: this,
54049             block: true
54050         });
54051     },
54052
54053     animateExpand : function(){
54054         this.beforeSlide();
54055         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
54056         this.el.setStyle("z-index", 20000);
54057         this.collapsedEl.hide({
54058             duration:.1
54059         });
54060         this.el.slideIn(this.getSlideAnchor(), {
54061             callback : function(){
54062                 this.el.setStyle("z-index", "");
54063                 this.afterSlide();
54064                 if(this.split){
54065                     this.split.el.show();
54066                 }
54067                 this.fireEvent("invalidated", this);
54068                 this.fireEvent("expanded", this);
54069             },
54070             scope: this,
54071             block: true
54072         });
54073     },
54074
54075     anchors : {
54076         "west" : "left",
54077         "east" : "right",
54078         "north" : "top",
54079         "south" : "bottom"
54080     },
54081
54082     sanchors : {
54083         "west" : "l",
54084         "east" : "r",
54085         "north" : "t",
54086         "south" : "b"
54087     },
54088
54089     canchors : {
54090         "west" : "tl-tr",
54091         "east" : "tr-tl",
54092         "north" : "tl-bl",
54093         "south" : "bl-tl"
54094     },
54095
54096     getAnchor : function(){
54097         return this.anchors[this.position];
54098     },
54099
54100     getCollapseAnchor : function(){
54101         return this.canchors[this.position];
54102     },
54103
54104     getSlideAnchor : function(){
54105         return this.sanchors[this.position];
54106     },
54107
54108     getAlignAdj : function(){
54109         var cm = this.cmargins;
54110         switch(this.position){
54111             case "west":
54112                 return [0, 0];
54113             break;
54114             case "east":
54115                 return [0, 0];
54116             break;
54117             case "north":
54118                 return [0, 0];
54119             break;
54120             case "south":
54121                 return [0, 0];
54122             break;
54123         }
54124     },
54125
54126     getExpandAdj : function(){
54127         var c = this.collapsedEl, cm = this.cmargins;
54128         switch(this.position){
54129             case "west":
54130                 return [-(cm.right+c.getWidth()+cm.left), 0];
54131             break;
54132             case "east":
54133                 return [cm.right+c.getWidth()+cm.left, 0];
54134             break;
54135             case "north":
54136                 return [0, -(cm.top+cm.bottom+c.getHeight())];
54137             break;
54138             case "south":
54139                 return [0, cm.top+cm.bottom+c.getHeight()];
54140             break;
54141         }
54142     }
54143 });/*
54144  * Based on:
54145  * Ext JS Library 1.1.1
54146  * Copyright(c) 2006-2007, Ext JS, LLC.
54147  *
54148  * Originally Released Under LGPL - original licence link has changed is not relivant.
54149  *
54150  * Fork - LGPL
54151  * <script type="text/javascript">
54152  */
54153 /*
54154  * These classes are private internal classes
54155  */
54156 Roo.CenterLayoutRegion = function(mgr, config){
54157     Roo.LayoutRegion.call(this, mgr, config, "center");
54158     this.visible = true;
54159     this.minWidth = config.minWidth || 20;
54160     this.minHeight = config.minHeight || 20;
54161 };
54162
54163 Roo.extend(Roo.CenterLayoutRegion, Roo.LayoutRegion, {
54164     hide : function(){
54165         // center panel can't be hidden
54166     },
54167     
54168     show : function(){
54169         // center panel can't be hidden
54170     },
54171     
54172     getMinWidth: function(){
54173         return this.minWidth;
54174     },
54175     
54176     getMinHeight: function(){
54177         return this.minHeight;
54178     }
54179 });
54180
54181
54182 Roo.NorthLayoutRegion = function(mgr, config){
54183     Roo.LayoutRegion.call(this, mgr, config, "north", "n-resize");
54184     if(this.split){
54185         this.split.placement = Roo.SplitBar.TOP;
54186         this.split.orientation = Roo.SplitBar.VERTICAL;
54187         this.split.el.addClass("x-layout-split-v");
54188     }
54189     var size = config.initialSize || config.height;
54190     if(typeof size != "undefined"){
54191         this.el.setHeight(size);
54192     }
54193 };
54194 Roo.extend(Roo.NorthLayoutRegion, Roo.SplitLayoutRegion, {
54195     orientation: Roo.SplitBar.VERTICAL,
54196     getBox : function(){
54197         if(this.collapsed){
54198             return this.collapsedEl.getBox();
54199         }
54200         var box = this.el.getBox();
54201         if(this.split){
54202             box.height += this.split.el.getHeight();
54203         }
54204         return box;
54205     },
54206     
54207     updateBox : function(box){
54208         if(this.split && !this.collapsed){
54209             box.height -= this.split.el.getHeight();
54210             this.split.el.setLeft(box.x);
54211             this.split.el.setTop(box.y+box.height);
54212             this.split.el.setWidth(box.width);
54213         }
54214         if(this.collapsed){
54215             this.updateBody(box.width, null);
54216         }
54217         Roo.LayoutRegion.prototype.updateBox.call(this, box);
54218     }
54219 });
54220
54221 Roo.SouthLayoutRegion = function(mgr, config){
54222     Roo.SplitLayoutRegion.call(this, mgr, config, "south", "s-resize");
54223     if(this.split){
54224         this.split.placement = Roo.SplitBar.BOTTOM;
54225         this.split.orientation = Roo.SplitBar.VERTICAL;
54226         this.split.el.addClass("x-layout-split-v");
54227     }
54228     var size = config.initialSize || config.height;
54229     if(typeof size != "undefined"){
54230         this.el.setHeight(size);
54231     }
54232 };
54233 Roo.extend(Roo.SouthLayoutRegion, Roo.SplitLayoutRegion, {
54234     orientation: Roo.SplitBar.VERTICAL,
54235     getBox : function(){
54236         if(this.collapsed){
54237             return this.collapsedEl.getBox();
54238         }
54239         var box = this.el.getBox();
54240         if(this.split){
54241             var sh = this.split.el.getHeight();
54242             box.height += sh;
54243             box.y -= sh;
54244         }
54245         return box;
54246     },
54247     
54248     updateBox : function(box){
54249         if(this.split && !this.collapsed){
54250             var sh = this.split.el.getHeight();
54251             box.height -= sh;
54252             box.y += sh;
54253             this.split.el.setLeft(box.x);
54254             this.split.el.setTop(box.y-sh);
54255             this.split.el.setWidth(box.width);
54256         }
54257         if(this.collapsed){
54258             this.updateBody(box.width, null);
54259         }
54260         Roo.LayoutRegion.prototype.updateBox.call(this, box);
54261     }
54262 });
54263
54264 Roo.EastLayoutRegion = function(mgr, config){
54265     Roo.SplitLayoutRegion.call(this, mgr, config, "east", "e-resize");
54266     if(this.split){
54267         this.split.placement = Roo.SplitBar.RIGHT;
54268         this.split.orientation = Roo.SplitBar.HORIZONTAL;
54269         this.split.el.addClass("x-layout-split-h");
54270     }
54271     var size = config.initialSize || config.width;
54272     if(typeof size != "undefined"){
54273         this.el.setWidth(size);
54274     }
54275 };
54276 Roo.extend(Roo.EastLayoutRegion, Roo.SplitLayoutRegion, {
54277     orientation: Roo.SplitBar.HORIZONTAL,
54278     getBox : function(){
54279         if(this.collapsed){
54280             return this.collapsedEl.getBox();
54281         }
54282         var box = this.el.getBox();
54283         if(this.split){
54284             var sw = this.split.el.getWidth();
54285             box.width += sw;
54286             box.x -= sw;
54287         }
54288         return box;
54289     },
54290
54291     updateBox : function(box){
54292         if(this.split && !this.collapsed){
54293             var sw = this.split.el.getWidth();
54294             box.width -= sw;
54295             this.split.el.setLeft(box.x);
54296             this.split.el.setTop(box.y);
54297             this.split.el.setHeight(box.height);
54298             box.x += sw;
54299         }
54300         if(this.collapsed){
54301             this.updateBody(null, box.height);
54302         }
54303         Roo.LayoutRegion.prototype.updateBox.call(this, box);
54304     }
54305 });
54306
54307 Roo.WestLayoutRegion = function(mgr, config){
54308     Roo.SplitLayoutRegion.call(this, mgr, config, "west", "w-resize");
54309     if(this.split){
54310         this.split.placement = Roo.SplitBar.LEFT;
54311         this.split.orientation = Roo.SplitBar.HORIZONTAL;
54312         this.split.el.addClass("x-layout-split-h");
54313     }
54314     var size = config.initialSize || config.width;
54315     if(typeof size != "undefined"){
54316         this.el.setWidth(size);
54317     }
54318 };
54319 Roo.extend(Roo.WestLayoutRegion, Roo.SplitLayoutRegion, {
54320     orientation: Roo.SplitBar.HORIZONTAL,
54321     getBox : function(){
54322         if(this.collapsed){
54323             return this.collapsedEl.getBox();
54324         }
54325         var box = this.el.getBox();
54326         if(this.split){
54327             box.width += this.split.el.getWidth();
54328         }
54329         return box;
54330     },
54331     
54332     updateBox : function(box){
54333         if(this.split && !this.collapsed){
54334             var sw = this.split.el.getWidth();
54335             box.width -= sw;
54336             this.split.el.setLeft(box.x+box.width);
54337             this.split.el.setTop(box.y);
54338             this.split.el.setHeight(box.height);
54339         }
54340         if(this.collapsed){
54341             this.updateBody(null, box.height);
54342         }
54343         Roo.LayoutRegion.prototype.updateBox.call(this, box);
54344     }
54345 });
54346 /*
54347  * Based on:
54348  * Ext JS Library 1.1.1
54349  * Copyright(c) 2006-2007, Ext JS, LLC.
54350  *
54351  * Originally Released Under LGPL - original licence link has changed is not relivant.
54352  *
54353  * Fork - LGPL
54354  * <script type="text/javascript">
54355  */
54356  
54357  
54358 /*
54359  * Private internal class for reading and applying state
54360  */
54361 Roo.LayoutStateManager = function(layout){
54362      // default empty state
54363      this.state = {
54364         north: {},
54365         south: {},
54366         east: {},
54367         west: {}       
54368     };
54369 };
54370
54371 Roo.LayoutStateManager.prototype = {
54372     init : function(layout, provider){
54373         this.provider = provider;
54374         var state = provider.get(layout.id+"-layout-state");
54375         if(state){
54376             var wasUpdating = layout.isUpdating();
54377             if(!wasUpdating){
54378                 layout.beginUpdate();
54379             }
54380             for(var key in state){
54381                 if(typeof state[key] != "function"){
54382                     var rstate = state[key];
54383                     var r = layout.getRegion(key);
54384                     if(r && rstate){
54385                         if(rstate.size){
54386                             r.resizeTo(rstate.size);
54387                         }
54388                         if(rstate.collapsed == true){
54389                             r.collapse(true);
54390                         }else{
54391                             r.expand(null, true);
54392                         }
54393                     }
54394                 }
54395             }
54396             if(!wasUpdating){
54397                 layout.endUpdate();
54398             }
54399             this.state = state; 
54400         }
54401         this.layout = layout;
54402         layout.on("regionresized", this.onRegionResized, this);
54403         layout.on("regioncollapsed", this.onRegionCollapsed, this);
54404         layout.on("regionexpanded", this.onRegionExpanded, this);
54405     },
54406     
54407     storeState : function(){
54408         this.provider.set(this.layout.id+"-layout-state", this.state);
54409     },
54410     
54411     onRegionResized : function(region, newSize){
54412         this.state[region.getPosition()].size = newSize;
54413         this.storeState();
54414     },
54415     
54416     onRegionCollapsed : function(region){
54417         this.state[region.getPosition()].collapsed = true;
54418         this.storeState();
54419     },
54420     
54421     onRegionExpanded : function(region){
54422         this.state[region.getPosition()].collapsed = false;
54423         this.storeState();
54424     }
54425 };/*
54426  * Based on:
54427  * Ext JS Library 1.1.1
54428  * Copyright(c) 2006-2007, Ext JS, LLC.
54429  *
54430  * Originally Released Under LGPL - original licence link has changed is not relivant.
54431  *
54432  * Fork - LGPL
54433  * <script type="text/javascript">
54434  */
54435 /**
54436  * @class Roo.ContentPanel
54437  * @extends Roo.util.Observable
54438  * @children Roo.form.Form Roo.JsonView Roo.View
54439  * @parent Roo.BorderLayout Roo.LayoutDialog builder-top
54440  * A basic ContentPanel element.
54441  * @cfg {Boolean}   fitToFrame    True for this panel to adjust its size to fit when the region resizes  (defaults to false)
54442  * @cfg {Boolean}   fitContainer   When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container  (defaults to false)
54443  * @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
54444  * @cfg {Boolean}   closable      True if the panel can be closed/removed
54445  * @cfg {Boolean}   background    True if the panel should not be activated when it is added (defaults to false)
54446  * @cfg {String|HTMLElement|Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
54447  * @cfg {Roo.Toolbar}   toolbar       A toolbar for this panel
54448  * @cfg {Boolean} autoScroll    True to scroll overflow in this panel (use with {@link #fitToFrame})
54449  * @cfg {String} title          The title for this panel
54450  * @cfg {Array} adjustments     Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
54451  * @cfg {String} url            Calls {@link #setUrl} with this value
54452  * @cfg {String} region [required]   (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
54453  * @cfg {String|Object} params  When used with {@link #url}, calls {@link #setUrl} with this value
54454  * @cfg {Boolean} loadOnce      When used with {@link #url}, calls {@link #setUrl} with this value
54455  * @cfg {String}    content        Raw content to fill content panel with (uses setContent on construction.)
54456  * @cfg {String}    style  Extra style to add to the content panel
54457  * @cfg {Roo.menu.Menu} menu  popup menu
54458
54459  * @constructor
54460  * Create a new ContentPanel.
54461  * @param {String/HTMLElement/Roo.Element} el The container element for this panel
54462  * @param {String/Object} config A string to set only the title or a config object
54463  * @param {String} content (optional) Set the HTML content for this panel
54464  * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
54465  */
54466 Roo.ContentPanel = function(el, config, content){
54467     
54468      
54469     /*
54470     if(el.autoCreate || el.xtype){ // xtype is available if this is called from factory
54471         config = el;
54472         el = Roo.id();
54473     }
54474     if (config && config.parentLayout) { 
54475         el = config.parentLayout.el.createChild(); 
54476     }
54477     */
54478     if(el.autoCreate){ // xtype is available if this is called from factory
54479         config = el;
54480         el = Roo.id();
54481     }
54482     this.el = Roo.get(el);
54483     if(!this.el && config && config.autoCreate){
54484         if(typeof config.autoCreate == "object"){
54485             if(!config.autoCreate.id){
54486                 config.autoCreate.id = config.id||el;
54487             }
54488             this.el = Roo.DomHelper.append(document.body,
54489                         config.autoCreate, true);
54490         }else{
54491             this.el = Roo.DomHelper.append(document.body,
54492                         {tag: "div", cls: "x-layout-inactive-content", id: config.id||el}, true);
54493         }
54494     }
54495     
54496     
54497     this.closable = false;
54498     this.loaded = false;
54499     this.active = false;
54500     if(typeof config == "string"){
54501         this.title = config;
54502     }else{
54503         Roo.apply(this, config);
54504     }
54505     
54506     if (this.toolbar && !this.toolbar.el && this.toolbar.xtype) {
54507         this.wrapEl = this.el.wrap();
54508         this.toolbar.container = this.el.insertSibling(false, 'before');
54509         this.toolbar = new Roo.Toolbar(this.toolbar);
54510     }
54511     
54512     // xtype created footer. - not sure if will work as we normally have to render first..
54513     if (this.footer && !this.footer.el && this.footer.xtype) {
54514         if (!this.wrapEl) {
54515             this.wrapEl = this.el.wrap();
54516         }
54517     
54518         this.footer.container = this.wrapEl.createChild();
54519          
54520         this.footer = Roo.factory(this.footer, Roo);
54521         
54522     }
54523     
54524     if(this.resizeEl){
54525         this.resizeEl = Roo.get(this.resizeEl, true);
54526     }else{
54527         this.resizeEl = this.el;
54528     }
54529     // handle view.xtype
54530     
54531  
54532     
54533     
54534     this.addEvents({
54535         /**
54536          * @event activate
54537          * Fires when this panel is activated. 
54538          * @param {Roo.ContentPanel} this
54539          */
54540         "activate" : true,
54541         /**
54542          * @event deactivate
54543          * Fires when this panel is activated. 
54544          * @param {Roo.ContentPanel} this
54545          */
54546         "deactivate" : true,
54547
54548         /**
54549          * @event resize
54550          * Fires when this panel is resized if fitToFrame is true.
54551          * @param {Roo.ContentPanel} this
54552          * @param {Number} width The width after any component adjustments
54553          * @param {Number} height The height after any component adjustments
54554          */
54555         "resize" : true,
54556         
54557          /**
54558          * @event render
54559          * Fires when this tab is created
54560          * @param {Roo.ContentPanel} this
54561          */
54562         "render" : true
54563          
54564         
54565     });
54566     
54567
54568     
54569     
54570     if(this.autoScroll){
54571         this.resizeEl.setStyle("overflow", "auto");
54572     } else {
54573         // fix randome scrolling
54574         this.el.on('scroll', function() {
54575             Roo.log('fix random scolling');
54576             this.scrollTo('top',0); 
54577         });
54578     }
54579     content = content || this.content;
54580     if(content){
54581         this.setContent(content);
54582     }
54583     if(config && config.url){
54584         this.setUrl(this.url, this.params, this.loadOnce);
54585     }
54586     
54587     
54588     
54589     Roo.ContentPanel.superclass.constructor.call(this);
54590     
54591     if (this.view && typeof(this.view.xtype) != 'undefined') {
54592         this.view.el = this.el.appendChild(document.createElement("div"));
54593         this.view = Roo.factory(this.view); 
54594         this.view.render  &&  this.view.render(false, '');  
54595     }
54596     
54597     
54598     this.fireEvent('render', this);
54599 };
54600
54601 Roo.extend(Roo.ContentPanel, Roo.util.Observable, {
54602     tabTip:'',
54603     setRegion : function(region){
54604         this.region = region;
54605         if(region){
54606            this.el.replaceClass("x-layout-inactive-content", "x-layout-active-content");
54607         }else{
54608            this.el.replaceClass("x-layout-active-content", "x-layout-inactive-content");
54609         } 
54610     },
54611     
54612     /**
54613      * Returns the toolbar for this Panel if one was configured. 
54614      * @return {Roo.Toolbar} 
54615      */
54616     getToolbar : function(){
54617         return this.toolbar;
54618     },
54619     
54620     setActiveState : function(active){
54621         this.active = active;
54622         if(!active){
54623             this.fireEvent("deactivate", this);
54624         }else{
54625             this.fireEvent("activate", this);
54626         }
54627     },
54628     /**
54629      * Updates this panel's element
54630      * @param {String} content The new content
54631      * @param {Boolean} loadScripts (optional) true to look for and process scripts
54632     */
54633     setContent : function(content, loadScripts){
54634         this.el.update(content, loadScripts);
54635     },
54636
54637     ignoreResize : function(w, h){
54638         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
54639             return true;
54640         }else{
54641             this.lastSize = {width: w, height: h};
54642             return false;
54643         }
54644     },
54645     /**
54646      * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
54647      * @return {Roo.UpdateManager} The UpdateManager
54648      */
54649     getUpdateManager : function(){
54650         return this.el.getUpdateManager();
54651     },
54652      /**
54653      * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
54654      * @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:
54655 <pre><code>
54656 panel.load({
54657     url: "your-url.php",
54658     params: {param1: "foo", param2: "bar"}, // or a URL encoded string
54659     callback: yourFunction,
54660     scope: yourObject, //(optional scope)
54661     discardUrl: false,
54662     nocache: false,
54663     text: "Loading...",
54664     timeout: 30,
54665     scripts: false
54666 });
54667 </code></pre>
54668      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
54669      * 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.
54670      * @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}
54671      * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
54672      * @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.
54673      * @return {Roo.ContentPanel} this
54674      */
54675     load : function(){
54676         var um = this.el.getUpdateManager();
54677         um.update.apply(um, arguments);
54678         return this;
54679     },
54680
54681
54682     /**
54683      * 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.
54684      * @param {String/Function} url The URL to load the content from or a function to call to get the URL
54685      * @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)
54686      * @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)
54687      * @return {Roo.UpdateManager} The UpdateManager
54688      */
54689     setUrl : function(url, params, loadOnce){
54690         if(this.refreshDelegate){
54691             this.removeListener("activate", this.refreshDelegate);
54692         }
54693         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
54694         this.on("activate", this.refreshDelegate);
54695         return this.el.getUpdateManager();
54696     },
54697     
54698     _handleRefresh : function(url, params, loadOnce){
54699         if(!loadOnce || !this.loaded){
54700             var updater = this.el.getUpdateManager();
54701             updater.update(url, params, this._setLoaded.createDelegate(this));
54702         }
54703     },
54704     
54705     _setLoaded : function(){
54706         this.loaded = true;
54707     }, 
54708     
54709     /**
54710      * Returns this panel's id
54711      * @return {String} 
54712      */
54713     getId : function(){
54714         return this.el.id;
54715     },
54716     
54717     /** 
54718      * Returns this panel's element - used by regiosn to add.
54719      * @return {Roo.Element} 
54720      */
54721     getEl : function(){
54722         return this.wrapEl || this.el;
54723     },
54724     
54725     adjustForComponents : function(width, height)
54726     {
54727         //Roo.log('adjustForComponents ');
54728         if(this.resizeEl != this.el){
54729             width -= this.el.getFrameWidth('lr');
54730             height -= this.el.getFrameWidth('tb');
54731         }
54732         if(this.toolbar){
54733             var te = this.toolbar.getEl();
54734             height -= te.getHeight();
54735             te.setWidth(width);
54736         }
54737         if(this.footer){
54738             var te = this.footer.getEl();
54739             //Roo.log("footer:" + te.getHeight());
54740             
54741             height -= te.getHeight();
54742             te.setWidth(width);
54743         }
54744         
54745         
54746         if(this.adjustments){
54747             width += this.adjustments[0];
54748             height += this.adjustments[1];
54749         }
54750         return {"width": width, "height": height};
54751     },
54752     
54753     setSize : function(width, height){
54754         if(this.fitToFrame && !this.ignoreResize(width, height)){
54755             if(this.fitContainer && this.resizeEl != this.el){
54756                 this.el.setSize(width, height);
54757             }
54758             var size = this.adjustForComponents(width, height);
54759             this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
54760             this.fireEvent('resize', this, size.width, size.height);
54761         }
54762     },
54763     
54764     /**
54765      * Returns this panel's title
54766      * @return {String} 
54767      */
54768     getTitle : function(){
54769         return this.title;
54770     },
54771     
54772     /**
54773      * Set this panel's title
54774      * @param {String} title
54775      */
54776     setTitle : function(title){
54777         this.title = title;
54778         if(this.region){
54779             this.region.updatePanelTitle(this, title);
54780         }
54781     },
54782     
54783     /**
54784      * Returns true is this panel was configured to be closable
54785      * @return {Boolean} 
54786      */
54787     isClosable : function(){
54788         return this.closable;
54789     },
54790     
54791     beforeSlide : function(){
54792         this.el.clip();
54793         this.resizeEl.clip();
54794     },
54795     
54796     afterSlide : function(){
54797         this.el.unclip();
54798         this.resizeEl.unclip();
54799     },
54800     
54801     /**
54802      *   Force a content refresh from the URL specified in the {@link #setUrl} method.
54803      *   Will fail silently if the {@link #setUrl} method has not been called.
54804      *   This does not activate the panel, just updates its content.
54805      */
54806     refresh : function(){
54807         if(this.refreshDelegate){
54808            this.loaded = false;
54809            this.refreshDelegate();
54810         }
54811     },
54812     
54813     /**
54814      * Destroys this panel
54815      */
54816     destroy : function(){
54817         this.el.removeAllListeners();
54818         var tempEl = document.createElement("span");
54819         tempEl.appendChild(this.el.dom);
54820         tempEl.innerHTML = "";
54821         this.el.remove();
54822         this.el = null;
54823     },
54824     
54825     /**
54826      * form - if the content panel contains a form - this is a reference to it.
54827      * @type {Roo.form.Form}
54828      */
54829     form : false,
54830     /**
54831      * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
54832      *    This contains a reference to it.
54833      * @type {Roo.View}
54834      */
54835     view : false,
54836     
54837       /**
54838      * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
54839      * <pre><code>
54840
54841 layout.addxtype({
54842        xtype : 'Form',
54843        items: [ .... ]
54844    }
54845 );
54846
54847 </code></pre>
54848      * @param {Object} cfg Xtype definition of item to add.
54849      */
54850     
54851     addxtype : function(cfg) {
54852         // add form..
54853         if (cfg.xtype.match(/^Form$/)) {
54854             
54855             var el;
54856             //if (this.footer) {
54857             //    el = this.footer.container.insertSibling(false, 'before');
54858             //} else {
54859                 el = this.el.createChild();
54860             //}
54861
54862             this.form = new  Roo.form.Form(cfg);
54863             
54864             
54865             if ( this.form.allItems.length) {
54866                 this.form.render(el.dom);
54867             }
54868             return this.form;
54869         }
54870         // should only have one of theses..
54871         if ([ 'View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
54872             // views.. should not be just added - used named prop 'view''
54873             
54874             cfg.el = this.el.appendChild(document.createElement("div"));
54875             // factory?
54876             
54877             var ret = new Roo.factory(cfg);
54878              
54879              ret.render && ret.render(false, ''); // render blank..
54880             this.view = ret;
54881             return ret;
54882         }
54883         return false;
54884     }
54885 });
54886
54887 /**
54888  * @class Roo.GridPanel
54889  * @extends Roo.ContentPanel
54890  * @constructor
54891  * Create a new GridPanel.
54892  * @param {Roo.grid.Grid} grid The grid for this panel
54893  * @param {String/Object} config A string to set only the panel's title, or a config object
54894  */
54895 Roo.GridPanel = function(grid, config){
54896     
54897   
54898     this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
54899         {tag: "div", cls: "x-layout-grid-wrapper x-layout-inactive-content"}, true);
54900         
54901     this.wrapper.dom.appendChild(grid.getGridEl().dom);
54902     
54903     Roo.GridPanel.superclass.constructor.call(this, this.wrapper, config);
54904     
54905     if(this.toolbar){
54906         this.toolbar.el.insertBefore(this.wrapper.dom.firstChild);
54907     }
54908     // xtype created footer. - not sure if will work as we normally have to render first..
54909     if (this.footer && !this.footer.el && this.footer.xtype) {
54910         
54911         this.footer.container = this.grid.getView().getFooterPanel(true);
54912         this.footer.dataSource = this.grid.dataSource;
54913         this.footer = Roo.factory(this.footer, Roo);
54914         
54915     }
54916     
54917     grid.monitorWindowResize = false; // turn off autosizing
54918     grid.autoHeight = false;
54919     grid.autoWidth = false;
54920     this.grid = grid;
54921     this.grid.getGridEl().replaceClass("x-layout-inactive-content", "x-layout-component-panel");
54922 };
54923
54924 Roo.extend(Roo.GridPanel, Roo.ContentPanel, {
54925     getId : function(){
54926         return this.grid.id;
54927     },
54928     
54929     /**
54930      * Returns the grid for this panel
54931      * @return {Roo.grid.Grid} 
54932      */
54933     getGrid : function(){
54934         return this.grid;    
54935     },
54936     
54937     setSize : function(width, height){
54938         if(!this.ignoreResize(width, height)){
54939             var grid = this.grid;
54940             var size = this.adjustForComponents(width, height);
54941             grid.getGridEl().setSize(size.width, size.height);
54942             grid.autoSize();
54943         }
54944     },
54945     
54946     beforeSlide : function(){
54947         this.grid.getView().scroller.clip();
54948     },
54949     
54950     afterSlide : function(){
54951         this.grid.getView().scroller.unclip();
54952     },
54953     
54954     destroy : function(){
54955         this.grid.destroy();
54956         delete this.grid;
54957         Roo.GridPanel.superclass.destroy.call(this); 
54958     }
54959 });
54960
54961
54962 /**
54963  * @class Roo.NestedLayoutPanel
54964  * @extends Roo.ContentPanel
54965  * @constructor
54966  * Create a new NestedLayoutPanel.
54967  * 
54968  * 
54969  * @param {Roo.BorderLayout} layout [required] The layout for this panel
54970  * @param {String/Object} config A string to set only the title or a config object
54971  */
54972 Roo.NestedLayoutPanel = function(layout, config)
54973 {
54974     // construct with only one argument..
54975     /* FIXME - implement nicer consturctors
54976     if (layout.layout) {
54977         config = layout;
54978         layout = config.layout;
54979         delete config.layout;
54980     }
54981     if (layout.xtype && !layout.getEl) {
54982         // then layout needs constructing..
54983         layout = Roo.factory(layout, Roo);
54984     }
54985     */
54986     
54987     
54988     Roo.NestedLayoutPanel.superclass.constructor.call(this, layout.getEl(), config);
54989     
54990     layout.monitorWindowResize = false; // turn off autosizing
54991     this.layout = layout;
54992     this.layout.getEl().addClass("x-layout-nested-layout");
54993     
54994     
54995     
54996     
54997 };
54998
54999 Roo.extend(Roo.NestedLayoutPanel, Roo.ContentPanel, {
55000
55001     setSize : function(width, height){
55002         if(!this.ignoreResize(width, height)){
55003             var size = this.adjustForComponents(width, height);
55004             var el = this.layout.getEl();
55005             el.setSize(size.width, size.height);
55006             var touch = el.dom.offsetWidth;
55007             this.layout.layout();
55008             // ie requires a double layout on the first pass
55009             if(Roo.isIE && !this.initialized){
55010                 this.initialized = true;
55011                 this.layout.layout();
55012             }
55013         }
55014     },
55015     
55016     // activate all subpanels if not currently active..
55017     
55018     setActiveState : function(active){
55019         this.active = active;
55020         if(!active){
55021             this.fireEvent("deactivate", this);
55022             return;
55023         }
55024         
55025         this.fireEvent("activate", this);
55026         // not sure if this should happen before or after..
55027         if (!this.layout) {
55028             return; // should not happen..
55029         }
55030         var reg = false;
55031         for (var r in this.layout.regions) {
55032             reg = this.layout.getRegion(r);
55033             if (reg.getActivePanel()) {
55034                 //reg.showPanel(reg.getActivePanel()); // force it to activate.. 
55035                 reg.setActivePanel(reg.getActivePanel());
55036                 continue;
55037             }
55038             if (!reg.panels.length) {
55039                 continue;
55040             }
55041             reg.showPanel(reg.getPanel(0));
55042         }
55043         
55044         
55045         
55046         
55047     },
55048     
55049     /**
55050      * Returns the nested BorderLayout for this panel
55051      * @return {Roo.BorderLayout} 
55052      */
55053     getLayout : function(){
55054         return this.layout;
55055     },
55056     
55057      /**
55058      * Adds a xtype elements to the layout of the nested panel
55059      * <pre><code>
55060
55061 panel.addxtype({
55062        xtype : 'ContentPanel',
55063        region: 'west',
55064        items: [ .... ]
55065    }
55066 );
55067
55068 panel.addxtype({
55069         xtype : 'NestedLayoutPanel',
55070         region: 'west',
55071         layout: {
55072            center: { },
55073            west: { }   
55074         },
55075         items : [ ... list of content panels or nested layout panels.. ]
55076    }
55077 );
55078 </code></pre>
55079      * @param {Object} cfg Xtype definition of item to add.
55080      */
55081     addxtype : function(cfg) {
55082         return this.layout.addxtype(cfg);
55083     
55084     }
55085 });
55086
55087 Roo.ScrollPanel = function(el, config, content){
55088     config = config || {};
55089     config.fitToFrame = true;
55090     Roo.ScrollPanel.superclass.constructor.call(this, el, config, content);
55091     
55092     this.el.dom.style.overflow = "hidden";
55093     var wrap = this.el.wrap({cls: "x-scroller x-layout-inactive-content"});
55094     this.el.removeClass("x-layout-inactive-content");
55095     this.el.on("mousewheel", this.onWheel, this);
55096
55097     var up = wrap.createChild({cls: "x-scroller-up", html: "&#160;"}, this.el.dom);
55098     var down = wrap.createChild({cls: "x-scroller-down", html: "&#160;"});
55099     up.unselectable(); down.unselectable();
55100     up.on("click", this.scrollUp, this);
55101     down.on("click", this.scrollDown, this);
55102     up.addClassOnOver("x-scroller-btn-over");
55103     down.addClassOnOver("x-scroller-btn-over");
55104     up.addClassOnClick("x-scroller-btn-click");
55105     down.addClassOnClick("x-scroller-btn-click");
55106     this.adjustments = [0, -(up.getHeight() + down.getHeight())];
55107
55108     this.resizeEl = this.el;
55109     this.el = wrap; this.up = up; this.down = down;
55110 };
55111
55112 Roo.extend(Roo.ScrollPanel, Roo.ContentPanel, {
55113     increment : 100,
55114     wheelIncrement : 5,
55115     scrollUp : function(){
55116         this.resizeEl.scroll("up", this.increment, {callback: this.afterScroll, scope: this});
55117     },
55118
55119     scrollDown : function(){
55120         this.resizeEl.scroll("down", this.increment, {callback: this.afterScroll, scope: this});
55121     },
55122
55123     afterScroll : function(){
55124         var el = this.resizeEl;
55125         var t = el.dom.scrollTop, h = el.dom.scrollHeight, ch = el.dom.clientHeight;
55126         this.up[t == 0 ? "addClass" : "removeClass"]("x-scroller-btn-disabled");
55127         this.down[h - t <= ch ? "addClass" : "removeClass"]("x-scroller-btn-disabled");
55128     },
55129
55130     setSize : function(){
55131         Roo.ScrollPanel.superclass.setSize.apply(this, arguments);
55132         this.afterScroll();
55133     },
55134
55135     onWheel : function(e){
55136         var d = e.getWheelDelta();
55137         this.resizeEl.dom.scrollTop -= (d*this.wheelIncrement);
55138         this.afterScroll();
55139         e.stopEvent();
55140     },
55141
55142     setContent : function(content, loadScripts){
55143         this.resizeEl.update(content, loadScripts);
55144     }
55145
55146 });
55147
55148
55149
55150 /**
55151  * @class Roo.TreePanel
55152  * @extends Roo.ContentPanel
55153  * Treepanel component
55154  * 
55155  * @constructor
55156  * Create a new TreePanel. - defaults to fit/scoll contents.
55157  * @param {String/Object} config A string to set only the panel's title, or a config object
55158  */
55159 Roo.TreePanel = function(config){
55160     var el = config.el;
55161     var tree = config.tree;
55162     delete config.tree; 
55163     delete config.el; // hopefull!
55164     
55165     // wrapper for IE7 strict & safari scroll issue
55166     
55167     var treeEl = el.createChild();
55168     config.resizeEl = treeEl;
55169     
55170     
55171     
55172     Roo.TreePanel.superclass.constructor.call(this, el, config);
55173  
55174  
55175     this.tree = new Roo.tree.TreePanel(treeEl , tree);
55176     //console.log(tree);
55177     this.on('activate', function()
55178     {
55179         if (this.tree.rendered) {
55180             return;
55181         }
55182         //console.log('render tree');
55183         this.tree.render();
55184     });
55185     // this should not be needed.. - it's actually the 'el' that resizes?
55186     // actuall it breaks the containerScroll - dragging nodes auto scroll at top
55187     
55188     //this.on('resize',  function (cp, w, h) {
55189     //        this.tree.innerCt.setWidth(w);
55190     //        this.tree.innerCt.setHeight(h);
55191     //        //this.tree.innerCt.setStyle('overflow-y', 'auto');
55192     //});
55193
55194         
55195     
55196 };
55197
55198 Roo.extend(Roo.TreePanel, Roo.ContentPanel, {   
55199     fitToFrame : true,
55200     autoScroll : true,
55201     /*
55202      * @cfg {Roo.tree.TreePanel} tree [required] The tree TreePanel, with config etc.
55203      */
55204     tree : false
55205
55206 });
55207
55208
55209
55210
55211
55212
55213
55214
55215
55216
55217
55218 /*
55219  * Based on:
55220  * Ext JS Library 1.1.1
55221  * Copyright(c) 2006-2007, Ext JS, LLC.
55222  *
55223  * Originally Released Under LGPL - original licence link has changed is not relivant.
55224  *
55225  * Fork - LGPL
55226  * <script type="text/javascript">
55227  */
55228  
55229
55230 /**
55231  * @class Roo.ReaderLayout
55232  * @extends Roo.BorderLayout
55233  * This is a pre-built layout that represents a classic, 5-pane application.  It consists of a header, a primary
55234  * center region containing two nested regions (a top one for a list view and one for item preview below),
55235  * and regions on either side that can be used for navigation, application commands, informational displays, etc.
55236  * The setup and configuration work exactly the same as it does for a {@link Roo.BorderLayout} - this class simply
55237  * expedites the setup of the overall layout and regions for this common application style.
55238  * Example:
55239  <pre><code>
55240 var reader = new Roo.ReaderLayout();
55241 var CP = Roo.ContentPanel;  // shortcut for adding
55242
55243 reader.beginUpdate();
55244 reader.add("north", new CP("north", "North"));
55245 reader.add("west", new CP("west", {title: "West"}));
55246 reader.add("east", new CP("east", {title: "East"}));
55247
55248 reader.regions.listView.add(new CP("listView", "List"));
55249 reader.regions.preview.add(new CP("preview", "Preview"));
55250 reader.endUpdate();
55251 </code></pre>
55252 * @constructor
55253 * Create a new ReaderLayout
55254 * @param {Object} config Configuration options
55255 * @param {String/HTMLElement/Element} container (optional) The container this layout is bound to (defaults to
55256 * document.body if omitted)
55257 */
55258 Roo.ReaderLayout = function(config, renderTo){
55259     var c = config || {size:{}};
55260     Roo.ReaderLayout.superclass.constructor.call(this, renderTo || document.body, {
55261         north: c.north !== false ? Roo.apply({
55262             split:false,
55263             initialSize: 32,
55264             titlebar: false
55265         }, c.north) : false,
55266         west: c.west !== false ? Roo.apply({
55267             split:true,
55268             initialSize: 200,
55269             minSize: 175,
55270             maxSize: 400,
55271             titlebar: true,
55272             collapsible: true,
55273             animate: true,
55274             margins:{left:5,right:0,bottom:5,top:5},
55275             cmargins:{left:5,right:5,bottom:5,top:5}
55276         }, c.west) : false,
55277         east: c.east !== false ? Roo.apply({
55278             split:true,
55279             initialSize: 200,
55280             minSize: 175,
55281             maxSize: 400,
55282             titlebar: true,
55283             collapsible: true,
55284             animate: true,
55285             margins:{left:0,right:5,bottom:5,top:5},
55286             cmargins:{left:5,right:5,bottom:5,top:5}
55287         }, c.east) : false,
55288         center: Roo.apply({
55289             tabPosition: 'top',
55290             autoScroll:false,
55291             closeOnTab: true,
55292             titlebar:false,
55293             margins:{left:c.west!==false ? 0 : 5,right:c.east!==false ? 0 : 5,bottom:5,top:2}
55294         }, c.center)
55295     });
55296
55297     this.el.addClass('x-reader');
55298
55299     this.beginUpdate();
55300
55301     var inner = new Roo.BorderLayout(Roo.get(document.body).createChild(), {
55302         south: c.preview !== false ? Roo.apply({
55303             split:true,
55304             initialSize: 200,
55305             minSize: 100,
55306             autoScroll:true,
55307             collapsible:true,
55308             titlebar: true,
55309             cmargins:{top:5,left:0, right:0, bottom:0}
55310         }, c.preview) : false,
55311         center: Roo.apply({
55312             autoScroll:false,
55313             titlebar:false,
55314             minHeight:200
55315         }, c.listView)
55316     });
55317     this.add('center', new Roo.NestedLayoutPanel(inner,
55318             Roo.apply({title: c.mainTitle || '',tabTip:''},c.innerPanelCfg)));
55319
55320     this.endUpdate();
55321
55322     this.regions.preview = inner.getRegion('south');
55323     this.regions.listView = inner.getRegion('center');
55324 };
55325
55326 Roo.extend(Roo.ReaderLayout, Roo.BorderLayout);/*
55327  * Based on:
55328  * Ext JS Library 1.1.1
55329  * Copyright(c) 2006-2007, Ext JS, LLC.
55330  *
55331  * Originally Released Under LGPL - original licence link has changed is not relivant.
55332  *
55333  * Fork - LGPL
55334  * <script type="text/javascript">
55335  */
55336  
55337 /**
55338  * @class Roo.grid.Grid
55339  * @extends Roo.util.Observable
55340  * This class represents the primary interface of a component based grid control.
55341  * <br><br>Usage:<pre><code>
55342  var grid = new Roo.grid.Grid("my-container-id", {
55343      ds: myDataStore,
55344      cm: myColModel,
55345      selModel: mySelectionModel,
55346      autoSizeColumns: true,
55347      monitorWindowResize: false,
55348      trackMouseOver: true
55349  });
55350  // set any options
55351  grid.render();
55352  * </code></pre>
55353  * <b>Common Problems:</b><br/>
55354  * - Grid does not resize properly when going smaller: Setting overflow hidden on the container
55355  * element will correct this<br/>
55356  * - If you get el.style[camel]= NaNpx or -2px or something related, be certain you have given your container element
55357  * dimensions. The grid adapts to your container's size, if your container has no size defined then the results
55358  * are unpredictable.<br/>
55359  * - Do not render the grid into an element with display:none. Try using visibility:hidden. Otherwise there is no way for the
55360  * grid to calculate dimensions/offsets.<br/>
55361   * @constructor
55362  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
55363  * The container MUST have some type of size defined for the grid to fill. The container will be
55364  * automatically set to position relative if it isn't already.
55365  * @param {Object} config A config object that sets properties on this grid.
55366  */
55367 Roo.grid.Grid = function(container, config){
55368         // initialize the container
55369         this.container = Roo.get(container);
55370         this.container.update("");
55371         this.container.setStyle("overflow", "hidden");
55372     this.container.addClass('x-grid-container');
55373
55374     this.id = this.container.id;
55375
55376     Roo.apply(this, config);
55377     // check and correct shorthanded configs
55378     if(this.ds){
55379         this.dataSource = this.ds;
55380         delete this.ds;
55381     }
55382     if(this.cm){
55383         this.colModel = this.cm;
55384         delete this.cm;
55385     }
55386     if(this.sm){
55387         this.selModel = this.sm;
55388         delete this.sm;
55389     }
55390
55391     if (this.selModel) {
55392         this.selModel = Roo.factory(this.selModel, Roo.grid);
55393         this.sm = this.selModel;
55394         this.sm.xmodule = this.xmodule || false;
55395     }
55396     if (typeof(this.colModel.config) == 'undefined') {
55397         this.colModel = new Roo.grid.ColumnModel(this.colModel);
55398         this.cm = this.colModel;
55399         this.cm.xmodule = this.xmodule || false;
55400     }
55401     if (this.dataSource) {
55402         this.dataSource= Roo.factory(this.dataSource, Roo.data);
55403         this.ds = this.dataSource;
55404         this.ds.xmodule = this.xmodule || false;
55405          
55406     }
55407     
55408     
55409     
55410     if(this.width){
55411         this.container.setWidth(this.width);
55412     }
55413
55414     if(this.height){
55415         this.container.setHeight(this.height);
55416     }
55417     /** @private */
55418         this.addEvents({
55419         // raw events
55420         /**
55421          * @event click
55422          * The raw click event for the entire grid.
55423          * @param {Roo.EventObject} e
55424          */
55425         "click" : true,
55426         /**
55427          * @event dblclick
55428          * The raw dblclick event for the entire grid.
55429          * @param {Roo.EventObject} e
55430          */
55431         "dblclick" : true,
55432         /**
55433          * @event contextmenu
55434          * The raw contextmenu event for the entire grid.
55435          * @param {Roo.EventObject} e
55436          */
55437         "contextmenu" : true,
55438         /**
55439          * @event mousedown
55440          * The raw mousedown event for the entire grid.
55441          * @param {Roo.EventObject} e
55442          */
55443         "mousedown" : true,
55444         /**
55445          * @event mouseup
55446          * The raw mouseup event for the entire grid.
55447          * @param {Roo.EventObject} e
55448          */
55449         "mouseup" : true,
55450         /**
55451          * @event mouseover
55452          * The raw mouseover event for the entire grid.
55453          * @param {Roo.EventObject} e
55454          */
55455         "mouseover" : true,
55456         /**
55457          * @event mouseout
55458          * The raw mouseout event for the entire grid.
55459          * @param {Roo.EventObject} e
55460          */
55461         "mouseout" : true,
55462         /**
55463          * @event keypress
55464          * The raw keypress event for the entire grid.
55465          * @param {Roo.EventObject} e
55466          */
55467         "keypress" : true,
55468         /**
55469          * @event keydown
55470          * The raw keydown event for the entire grid.
55471          * @param {Roo.EventObject} e
55472          */
55473         "keydown" : true,
55474
55475         // custom events
55476
55477         /**
55478          * @event cellclick
55479          * Fires when a cell is clicked
55480          * @param {Grid} this
55481          * @param {Number} rowIndex
55482          * @param {Number} columnIndex
55483          * @param {Roo.EventObject} e
55484          */
55485         "cellclick" : true,
55486         /**
55487          * @event celldblclick
55488          * Fires when a cell is double clicked
55489          * @param {Grid} this
55490          * @param {Number} rowIndex
55491          * @param {Number} columnIndex
55492          * @param {Roo.EventObject} e
55493          */
55494         "celldblclick" : true,
55495         /**
55496          * @event rowclick
55497          * Fires when a row is clicked
55498          * @param {Grid} this
55499          * @param {Number} rowIndex
55500          * @param {Roo.EventObject} e
55501          */
55502         "rowclick" : true,
55503         /**
55504          * @event rowdblclick
55505          * Fires when a row is double clicked
55506          * @param {Grid} this
55507          * @param {Number} rowIndex
55508          * @param {Roo.EventObject} e
55509          */
55510         "rowdblclick" : true,
55511         /**
55512          * @event headerclick
55513          * Fires when a header is clicked
55514          * @param {Grid} this
55515          * @param {Number} columnIndex
55516          * @param {Roo.EventObject} e
55517          */
55518         "headerclick" : true,
55519         /**
55520          * @event headerdblclick
55521          * Fires when a header cell is double clicked
55522          * @param {Grid} this
55523          * @param {Number} columnIndex
55524          * @param {Roo.EventObject} e
55525          */
55526         "headerdblclick" : true,
55527         /**
55528          * @event rowcontextmenu
55529          * Fires when a row is right clicked
55530          * @param {Grid} this
55531          * @param {Number} rowIndex
55532          * @param {Roo.EventObject} e
55533          */
55534         "rowcontextmenu" : true,
55535         /**
55536          * @event cellcontextmenu
55537          * Fires when a cell is right clicked
55538          * @param {Grid} this
55539          * @param {Number} rowIndex
55540          * @param {Number} cellIndex
55541          * @param {Roo.EventObject} e
55542          */
55543          "cellcontextmenu" : true,
55544         /**
55545          * @event headercontextmenu
55546          * Fires when a header is right clicked
55547          * @param {Grid} this
55548          * @param {Number} columnIndex
55549          * @param {Roo.EventObject} e
55550          */
55551         "headercontextmenu" : true,
55552         /**
55553          * @event bodyscroll
55554          * Fires when the body element is scrolled
55555          * @param {Number} scrollLeft
55556          * @param {Number} scrollTop
55557          */
55558         "bodyscroll" : true,
55559         /**
55560          * @event columnresize
55561          * Fires when the user resizes a column
55562          * @param {Number} columnIndex
55563          * @param {Number} newSize
55564          */
55565         "columnresize" : true,
55566         /**
55567          * @event columnmove
55568          * Fires when the user moves a column
55569          * @param {Number} oldIndex
55570          * @param {Number} newIndex
55571          */
55572         "columnmove" : true,
55573         /**
55574          * @event startdrag
55575          * Fires when row(s) start being dragged
55576          * @param {Grid} this
55577          * @param {Roo.GridDD} dd The drag drop object
55578          * @param {event} e The raw browser event
55579          */
55580         "startdrag" : true,
55581         /**
55582          * @event enddrag
55583          * Fires when a drag operation is complete
55584          * @param {Grid} this
55585          * @param {Roo.GridDD} dd The drag drop object
55586          * @param {event} e The raw browser event
55587          */
55588         "enddrag" : true,
55589         /**
55590          * @event dragdrop
55591          * Fires when dragged row(s) are dropped on a valid DD target
55592          * @param {Grid} this
55593          * @param {Roo.GridDD} dd The drag drop object
55594          * @param {String} targetId The target drag drop object
55595          * @param {event} e The raw browser event
55596          */
55597         "dragdrop" : true,
55598         /**
55599          * @event dragover
55600          * Fires while row(s) are being dragged. "targetId" is the id of the Yahoo.util.DD object the selected rows are being dragged over.
55601          * @param {Grid} this
55602          * @param {Roo.GridDD} dd The drag drop object
55603          * @param {String} targetId The target drag drop object
55604          * @param {event} e The raw browser event
55605          */
55606         "dragover" : true,
55607         /**
55608          * @event dragenter
55609          *  Fires when the dragged row(s) first cross another DD target while being dragged
55610          * @param {Grid} this
55611          * @param {Roo.GridDD} dd The drag drop object
55612          * @param {String} targetId The target drag drop object
55613          * @param {event} e The raw browser event
55614          */
55615         "dragenter" : true,
55616         /**
55617          * @event dragout
55618          * Fires when the dragged row(s) leave another DD target while being dragged
55619          * @param {Grid} this
55620          * @param {Roo.GridDD} dd The drag drop object
55621          * @param {String} targetId The target drag drop object
55622          * @param {event} e The raw browser event
55623          */
55624         "dragout" : true,
55625         /**
55626          * @event rowclass
55627          * Fires when a row is rendered, so you can change add a style to it.
55628          * @param {GridView} gridview   The grid view
55629          * @param {Object} rowcfg   contains record  rowIndex and rowClass - set rowClass to add a style.
55630          */
55631         'rowclass' : true,
55632
55633         /**
55634          * @event render
55635          * Fires when the grid is rendered
55636          * @param {Grid} grid
55637          */
55638         'render' : true
55639     });
55640
55641     Roo.grid.Grid.superclass.constructor.call(this);
55642 };
55643 Roo.extend(Roo.grid.Grid, Roo.util.Observable, {
55644     
55645     /**
55646          * @cfg {Roo.grid.AbstractSelectionModel} sm The selection Model (default = Roo.grid.RowSelectionModel)
55647          */
55648         /**
55649          * @cfg {Roo.grid.GridView} view  The view that renders the grid (default = Roo.grid.GridView)
55650          */
55651         /**
55652          * @cfg {Roo.grid.ColumnModel} cm[] The columns of the grid
55653          */
55654         /**
55655          * @cfg {Roo.grid.Store} ds The data store for the grid
55656          */
55657         /**
55658          * @cfg {Roo.Toolbar} toolbar a toolbar for buttons etc.
55659          */
55660         /**
55661      * @cfg {String} ddGroup - drag drop group.
55662      */
55663       /**
55664      * @cfg {String} dragGroup - drag group (?? not sure if needed.)
55665      */
55666
55667     /**
55668      * @cfg {Number} minColumnWidth The minimum width a column can be resized to. Default is 25.
55669      */
55670     minColumnWidth : 25,
55671
55672     /**
55673      * @cfg {Boolean} autoSizeColumns True to automatically resize the columns to fit their content
55674      * <b>on initial render.</b> It is more efficient to explicitly size the columns
55675      * through the ColumnModel's {@link Roo.grid.ColumnModel#width} config option.  Default is false.
55676      */
55677     autoSizeColumns : false,
55678
55679     /**
55680      * @cfg {Boolean} autoSizeHeaders True to measure headers with column data when auto sizing columns. Default is true.
55681      */
55682     autoSizeHeaders : true,
55683
55684     /**
55685      * @cfg {Boolean} monitorWindowResize True to autoSize the grid when the window resizes. Default is true.
55686      */
55687     monitorWindowResize : true,
55688
55689     /**
55690      * @cfg {Boolean} maxRowsToMeasure If autoSizeColumns is on, maxRowsToMeasure can be used to limit the number of
55691      * rows measured to get a columns size. Default is 0 (all rows).
55692      */
55693     maxRowsToMeasure : 0,
55694
55695     /**
55696      * @cfg {Boolean} trackMouseOver True to highlight rows when the mouse is over. Default is true.
55697      */
55698     trackMouseOver : true,
55699
55700     /**
55701     * @cfg {Boolean} enableDrag  True to enable drag of rows. Default is false. (double check if this is needed?)
55702     */
55703       /**
55704     * @cfg {Boolean} enableDrop  True to enable drop of elements. Default is false. (double check if this is needed?)
55705     */
55706     
55707     /**
55708     * @cfg {Boolean} enableDragDrop True to enable drag and drop of rows. Default is false.
55709     */
55710     enableDragDrop : false,
55711     
55712     /**
55713     * @cfg {Boolean} enableColumnMove True to enable drag and drop reorder of columns. Default is true.
55714     */
55715     enableColumnMove : true,
55716     
55717     /**
55718     * @cfg {Boolean} enableColumnHide True to enable hiding of columns with the header context menu. Default is true.
55719     */
55720     enableColumnHide : true,
55721     
55722     /**
55723     * @cfg {Boolean} enableRowHeightSync True to manually sync row heights across locked and not locked rows. Default is false.
55724     */
55725     enableRowHeightSync : false,
55726     
55727     /**
55728     * @cfg {Boolean} stripeRows True to stripe the rows.  Default is true.
55729     */
55730     stripeRows : true,
55731     
55732     /**
55733     * @cfg {Boolean} autoHeight True to fit the height of the grid container to the height of the data. Default is false.
55734     */
55735     autoHeight : false,
55736
55737     /**
55738      * @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.
55739      */
55740     autoExpandColumn : false,
55741
55742     /**
55743     * @cfg {Number} autoExpandMin The minimum width the autoExpandColumn can have (if enabled).
55744     * Default is 50.
55745     */
55746     autoExpandMin : 50,
55747
55748     /**
55749     * @cfg {Number} autoExpandMax The maximum width the autoExpandColumn can have (if enabled). Default is 1000.
55750     */
55751     autoExpandMax : 1000,
55752
55753     /**
55754     * @cfg {Object} view The {@link Roo.grid.GridView} used by the grid. This can be set before a call to render().
55755     */
55756     view : null,
55757
55758     /**
55759     * @cfg {Object} loadMask An {@link Roo.LoadMask} config or true to mask the grid while loading. Default is false.
55760     */
55761     loadMask : false,
55762     /**
55763     * @cfg {Roo.dd.DropTarget} dropTarget An {@link Roo.dd.DropTarget} config
55764     */
55765     dropTarget: false,
55766     
55767    
55768     
55769     // private
55770     rendered : false,
55771
55772     /**
55773     * @cfg {Boolean} autoWidth True to set the grid's width to the default total width of the grid's columns instead
55774     * of a fixed width. Default is false.
55775     */
55776     /**
55777     * @cfg {Number} maxHeight Sets the maximum height of the grid - ignored if autoHeight is not on.
55778     */
55779     
55780     
55781     /**
55782     * @cfg {String} ddText Configures the text is the drag proxy (defaults to "%0 selected row(s)").
55783     * %0 is replaced with the number of selected rows.
55784     */
55785     ddText : "{0} selected row{1}",
55786     
55787     
55788     /**
55789      * Called once after all setup has been completed and the grid is ready to be rendered.
55790      * @return {Roo.grid.Grid} this
55791      */
55792     render : function()
55793     {
55794         var c = this.container;
55795         // try to detect autoHeight/width mode
55796         if((!c.dom.offsetHeight || c.dom.offsetHeight < 20) || c.getStyle("height") == "auto"){
55797             this.autoHeight = true;
55798         }
55799         var view = this.getView();
55800         view.init(this);
55801
55802         c.on("click", this.onClick, this);
55803         c.on("dblclick", this.onDblClick, this);
55804         c.on("contextmenu", this.onContextMenu, this);
55805         c.on("keydown", this.onKeyDown, this);
55806         if (Roo.isTouch) {
55807             c.on("touchstart", this.onTouchStart, this);
55808         }
55809
55810         this.relayEvents(c, ["mousedown","mouseup","mouseover","mouseout","keypress"]);
55811
55812         this.getSelectionModel().init(this);
55813
55814         view.render();
55815
55816         if(this.loadMask){
55817             this.loadMask = new Roo.LoadMask(this.container,
55818                     Roo.apply({store:this.dataSource}, this.loadMask));
55819         }
55820         
55821         
55822         if (this.toolbar && this.toolbar.xtype) {
55823             this.toolbar.container = this.getView().getHeaderPanel(true);
55824             this.toolbar = new Roo.Toolbar(this.toolbar);
55825         }
55826         if (this.footer && this.footer.xtype) {
55827             this.footer.dataSource = this.getDataSource();
55828             this.footer.container = this.getView().getFooterPanel(true);
55829             this.footer = Roo.factory(this.footer, Roo);
55830         }
55831         if (this.dropTarget && this.dropTarget.xtype) {
55832             delete this.dropTarget.xtype;
55833             this.dropTarget =  new Roo.dd.DropTarget(this.getView().mainBody, this.dropTarget);
55834         }
55835         
55836         
55837         this.rendered = true;
55838         this.fireEvent('render', this);
55839         return this;
55840     },
55841
55842     /**
55843      * Reconfigures the grid to use a different Store and Column Model.
55844      * The View will be bound to the new objects and refreshed.
55845      * @param {Roo.data.Store} dataSource The new {@link Roo.data.Store} object
55846      * @param {Roo.grid.ColumnModel} The new {@link Roo.grid.ColumnModel} object
55847      */
55848     reconfigure : function(dataSource, colModel){
55849         if(this.loadMask){
55850             this.loadMask.destroy();
55851             this.loadMask = new Roo.LoadMask(this.container,
55852                     Roo.apply({store:dataSource}, this.loadMask));
55853         }
55854         this.view.bind(dataSource, colModel);
55855         this.dataSource = dataSource;
55856         this.colModel = colModel;
55857         this.view.refresh(true);
55858     },
55859     /**
55860      * addColumns
55861      * Add's a column, default at the end..
55862      
55863      * @param {int} position to add (default end)
55864      * @param {Array} of objects of column configuration see {@link Roo.grid.ColumnModel} 
55865      */
55866     addColumns : function(pos, ar)
55867     {
55868         
55869         for (var i =0;i< ar.length;i++) {
55870             var cfg = ar[i];
55871             cfg.id = typeof(cfg.id) == 'undefined' ? Roo.id() : cfg.id; // don't normally use this..
55872             this.cm.lookup[cfg.id] = cfg;
55873         }
55874         
55875         
55876         if (typeof(pos) == 'undefined' || pos >= this.cm.config.length) {
55877             pos = this.cm.config.length; //this.cm.config.push(cfg);
55878         } 
55879         pos = Math.max(0,pos);
55880         ar.unshift(0);
55881         ar.unshift(pos);
55882         this.cm.config.splice.apply(this.cm.config, ar);
55883         
55884         
55885         
55886         this.view.generateRules(this.cm);
55887         this.view.refresh(true);
55888         
55889     },
55890     
55891     
55892     
55893     
55894     // private
55895     onKeyDown : function(e){
55896         this.fireEvent("keydown", e);
55897     },
55898
55899     /**
55900      * Destroy this grid.
55901      * @param {Boolean} removeEl True to remove the element
55902      */
55903     destroy : function(removeEl, keepListeners){
55904         if(this.loadMask){
55905             this.loadMask.destroy();
55906         }
55907         var c = this.container;
55908         c.removeAllListeners();
55909         this.view.destroy();
55910         this.colModel.purgeListeners();
55911         if(!keepListeners){
55912             this.purgeListeners();
55913         }
55914         c.update("");
55915         if(removeEl === true){
55916             c.remove();
55917         }
55918     },
55919
55920     // private
55921     processEvent : function(name, e){
55922         // does this fire select???
55923         //Roo.log('grid:processEvent '  + name);
55924         
55925         if (name != 'touchstart' ) {
55926             this.fireEvent(name, e);    
55927         }
55928         
55929         var t = e.getTarget();
55930         var v = this.view;
55931         var header = v.findHeaderIndex(t);
55932         if(header !== false){
55933             var ename = name == 'touchstart' ? 'click' : name;
55934              
55935             this.fireEvent("header" + ename, this, header, e);
55936         }else{
55937             var row = v.findRowIndex(t);
55938             var cell = v.findCellIndex(t);
55939             if (name == 'touchstart') {
55940                 // first touch is always a click.
55941                 // hopefull this happens after selection is updated.?
55942                 name = false;
55943                 
55944                 if (typeof(this.selModel.getSelectedCell) != 'undefined') {
55945                     var cs = this.selModel.getSelectedCell();
55946                     if (row == cs[0] && cell == cs[1]){
55947                         name = 'dblclick';
55948                     }
55949                 }
55950                 if (typeof(this.selModel.getSelections) != 'undefined') {
55951                     var cs = this.selModel.getSelections();
55952                     var ds = this.dataSource;
55953                     if (cs.length == 1 && ds.getAt(row) == cs[0]){
55954                         name = 'dblclick';
55955                     }
55956                 }
55957                 if (!name) {
55958                     return;
55959                 }
55960             }
55961             
55962             
55963             if(row !== false){
55964                 this.fireEvent("row" + name, this, row, e);
55965                 if(cell !== false){
55966                     this.fireEvent("cell" + name, this, row, cell, e);
55967                 }
55968             }
55969         }
55970     },
55971
55972     // private
55973     onClick : function(e){
55974         this.processEvent("click", e);
55975     },
55976    // private
55977     onTouchStart : function(e){
55978         this.processEvent("touchstart", e);
55979     },
55980
55981     // private
55982     onContextMenu : function(e, t){
55983         this.processEvent("contextmenu", e);
55984     },
55985
55986     // private
55987     onDblClick : function(e){
55988         this.processEvent("dblclick", e);
55989     },
55990
55991     // private
55992     walkCells : function(row, col, step, fn, scope){
55993         var cm = this.colModel, clen = cm.getColumnCount();
55994         var ds = this.dataSource, rlen = ds.getCount(), first = true;
55995         if(step < 0){
55996             if(col < 0){
55997                 row--;
55998                 first = false;
55999             }
56000             while(row >= 0){
56001                 if(!first){
56002                     col = clen-1;
56003                 }
56004                 first = false;
56005                 while(col >= 0){
56006                     if(fn.call(scope || this, row, col, cm) === true){
56007                         return [row, col];
56008                     }
56009                     col--;
56010                 }
56011                 row--;
56012             }
56013         } else {
56014             if(col >= clen){
56015                 row++;
56016                 first = false;
56017             }
56018             while(row < rlen){
56019                 if(!first){
56020                     col = 0;
56021                 }
56022                 first = false;
56023                 while(col < clen){
56024                     if(fn.call(scope || this, row, col, cm) === true){
56025                         return [row, col];
56026                     }
56027                     col++;
56028                 }
56029                 row++;
56030             }
56031         }
56032         return null;
56033     },
56034
56035     // private
56036     getSelections : function(){
56037         return this.selModel.getSelections();
56038     },
56039
56040     /**
56041      * Causes the grid to manually recalculate its dimensions. Generally this is done automatically,
56042      * but if manual update is required this method will initiate it.
56043      */
56044     autoSize : function(){
56045         if(this.rendered){
56046             this.view.layout();
56047             if(this.view.adjustForScroll){
56048                 this.view.adjustForScroll();
56049             }
56050         }
56051     },
56052
56053     /**
56054      * Returns the grid's underlying element.
56055      * @return {Element} The element
56056      */
56057     getGridEl : function(){
56058         return this.container;
56059     },
56060
56061     // private for compatibility, overridden by editor grid
56062     stopEditing : function(){},
56063
56064     /**
56065      * Returns the grid's SelectionModel.
56066      * @return {SelectionModel}
56067      */
56068     getSelectionModel : function(){
56069         if(!this.selModel){
56070             this.selModel = new Roo.grid.RowSelectionModel();
56071         }
56072         return this.selModel;
56073     },
56074
56075     /**
56076      * Returns the grid's DataSource.
56077      * @return {DataSource}
56078      */
56079     getDataSource : function(){
56080         return this.dataSource;
56081     },
56082
56083     /**
56084      * Returns the grid's ColumnModel.
56085      * @return {ColumnModel}
56086      */
56087     getColumnModel : function(){
56088         return this.colModel;
56089     },
56090
56091     /**
56092      * Returns the grid's GridView object.
56093      * @return {GridView}
56094      */
56095     getView : function(){
56096         if(!this.view){
56097             this.view = new Roo.grid.GridView(this.viewConfig);
56098             this.relayEvents(this.view, [
56099                 "beforerowremoved", "beforerowsinserted",
56100                 "beforerefresh", "rowremoved",
56101                 "rowsinserted", "rowupdated" ,"refresh"
56102             ]);
56103         }
56104         return this.view;
56105     },
56106     /**
56107      * Called to get grid's drag proxy text, by default returns this.ddText.
56108      * Override this to put something different in the dragged text.
56109      * @return {String}
56110      */
56111     getDragDropText : function(){
56112         var count = this.selModel.getCount();
56113         return String.format(this.ddText, count, count == 1 ? '' : 's');
56114     }
56115 });
56116 /*
56117  * Based on:
56118  * Ext JS Library 1.1.1
56119  * Copyright(c) 2006-2007, Ext JS, LLC.
56120  *
56121  * Originally Released Under LGPL - original licence link has changed is not relivant.
56122  *
56123  * Fork - LGPL
56124  * <script type="text/javascript">
56125  */
56126  /**
56127  * @class Roo.grid.AbstractGridView
56128  * @extends Roo.util.Observable
56129  * @abstract
56130  * Abstract base class for grid Views
56131  * @constructor
56132  */
56133 Roo.grid.AbstractGridView = function(){
56134         this.grid = null;
56135         
56136         this.events = {
56137             "beforerowremoved" : true,
56138             "beforerowsinserted" : true,
56139             "beforerefresh" : true,
56140             "rowremoved" : true,
56141             "rowsinserted" : true,
56142             "rowupdated" : true,
56143             "refresh" : true
56144         };
56145     Roo.grid.AbstractGridView.superclass.constructor.call(this);
56146 };
56147
56148 Roo.extend(Roo.grid.AbstractGridView, Roo.util.Observable, {
56149     rowClass : "x-grid-row",
56150     cellClass : "x-grid-cell",
56151     tdClass : "x-grid-td",
56152     hdClass : "x-grid-hd",
56153     splitClass : "x-grid-hd-split",
56154     
56155     init: function(grid){
56156         this.grid = grid;
56157                 var cid = this.grid.getGridEl().id;
56158         this.colSelector = "#" + cid + " ." + this.cellClass + "-";
56159         this.tdSelector = "#" + cid + " ." + this.tdClass + "-";
56160         this.hdSelector = "#" + cid + " ." + this.hdClass + "-";
56161         this.splitSelector = "#" + cid + " ." + this.splitClass + "-";
56162         },
56163         
56164     getColumnRenderers : function(){
56165         var renderers = [];
56166         var cm = this.grid.colModel;
56167         var colCount = cm.getColumnCount();
56168         for(var i = 0; i < colCount; i++){
56169             renderers[i] = cm.getRenderer(i);
56170         }
56171         return renderers;
56172     },
56173     
56174     getColumnIds : function(){
56175         var ids = [];
56176         var cm = this.grid.colModel;
56177         var colCount = cm.getColumnCount();
56178         for(var i = 0; i < colCount; i++){
56179             ids[i] = cm.getColumnId(i);
56180         }
56181         return ids;
56182     },
56183     
56184     getDataIndexes : function(){
56185         if(!this.indexMap){
56186             this.indexMap = this.buildIndexMap();
56187         }
56188         return this.indexMap.colToData;
56189     },
56190     
56191     getColumnIndexByDataIndex : function(dataIndex){
56192         if(!this.indexMap){
56193             this.indexMap = this.buildIndexMap();
56194         }
56195         return this.indexMap.dataToCol[dataIndex];
56196     },
56197     
56198     /**
56199      * Set a css style for a column dynamically. 
56200      * @param {Number} colIndex The index of the column
56201      * @param {String} name The css property name
56202      * @param {String} value The css value
56203      */
56204     setCSSStyle : function(colIndex, name, value){
56205         var selector = "#" + this.grid.id + " .x-grid-col-" + colIndex;
56206         Roo.util.CSS.updateRule(selector, name, value);
56207     },
56208     
56209     generateRules : function(cm){
56210         var ruleBuf = [], rulesId = this.grid.id + '-cssrules';
56211         Roo.util.CSS.removeStyleSheet(rulesId);
56212         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
56213             var cid = cm.getColumnId(i);
56214             ruleBuf.push(this.colSelector, cid, " {\n", cm.config[i].css, "}\n",
56215                          this.tdSelector, cid, " {\n}\n",
56216                          this.hdSelector, cid, " {\n}\n",
56217                          this.splitSelector, cid, " {\n}\n");
56218         }
56219         return Roo.util.CSS.createStyleSheet(ruleBuf.join(""), rulesId);
56220     }
56221 });/*
56222  * Based on:
56223  * Ext JS Library 1.1.1
56224  * Copyright(c) 2006-2007, Ext JS, LLC.
56225  *
56226  * Originally Released Under LGPL - original licence link has changed is not relivant.
56227  *
56228  * Fork - LGPL
56229  * <script type="text/javascript">
56230  */
56231
56232 // private
56233 // This is a support class used internally by the Grid components
56234 Roo.grid.HeaderDragZone = function(grid, hd, hd2){
56235     this.grid = grid;
56236     this.view = grid.getView();
56237     this.ddGroup = "gridHeader" + this.grid.getGridEl().id;
56238     Roo.grid.HeaderDragZone.superclass.constructor.call(this, hd);
56239     if(hd2){
56240         this.setHandleElId(Roo.id(hd));
56241         this.setOuterHandleElId(Roo.id(hd2));
56242     }
56243     this.scroll = false;
56244 };
56245 Roo.extend(Roo.grid.HeaderDragZone, Roo.dd.DragZone, {
56246     maxDragWidth: 120,
56247     getDragData : function(e){
56248         var t = Roo.lib.Event.getTarget(e);
56249         var h = this.view.findHeaderCell(t);
56250         if(h){
56251             return {ddel: h.firstChild, header:h};
56252         }
56253         return false;
56254     },
56255
56256     onInitDrag : function(e){
56257         this.view.headersDisabled = true;
56258         var clone = this.dragData.ddel.cloneNode(true);
56259         clone.id = Roo.id();
56260         clone.style.width = Math.min(this.dragData.header.offsetWidth,this.maxDragWidth) + "px";
56261         this.proxy.update(clone);
56262         return true;
56263     },
56264
56265     afterValidDrop : function(){
56266         var v = this.view;
56267         setTimeout(function(){
56268             v.headersDisabled = false;
56269         }, 50);
56270     },
56271
56272     afterInvalidDrop : function(){
56273         var v = this.view;
56274         setTimeout(function(){
56275             v.headersDisabled = false;
56276         }, 50);
56277     }
56278 });
56279 /*
56280  * Based on:
56281  * Ext JS Library 1.1.1
56282  * Copyright(c) 2006-2007, Ext JS, LLC.
56283  *
56284  * Originally Released Under LGPL - original licence link has changed is not relivant.
56285  *
56286  * Fork - LGPL
56287  * <script type="text/javascript">
56288  */
56289 // private
56290 // This is a support class used internally by the Grid components
56291 Roo.grid.HeaderDropZone = function(grid, hd, hd2){
56292     this.grid = grid;
56293     this.view = grid.getView();
56294     // split the proxies so they don't interfere with mouse events
56295     this.proxyTop = Roo.DomHelper.append(document.body, {
56296         cls:"col-move-top", html:"&#160;"
56297     }, true);
56298     this.proxyBottom = Roo.DomHelper.append(document.body, {
56299         cls:"col-move-bottom", html:"&#160;"
56300     }, true);
56301     this.proxyTop.hide = this.proxyBottom.hide = function(){
56302         this.setLeftTop(-100,-100);
56303         this.setStyle("visibility", "hidden");
56304     };
56305     this.ddGroup = "gridHeader" + this.grid.getGridEl().id;
56306     // temporarily disabled
56307     //Roo.dd.ScrollManager.register(this.view.scroller.dom);
56308     Roo.grid.HeaderDropZone.superclass.constructor.call(this, grid.getGridEl().dom);
56309 };
56310 Roo.extend(Roo.grid.HeaderDropZone, Roo.dd.DropZone, {
56311     proxyOffsets : [-4, -9],
56312     fly: Roo.Element.fly,
56313
56314     getTargetFromEvent : function(e){
56315         var t = Roo.lib.Event.getTarget(e);
56316         var cindex = this.view.findCellIndex(t);
56317         if(cindex !== false){
56318             return this.view.getHeaderCell(cindex);
56319         }
56320         return null;
56321     },
56322
56323     nextVisible : function(h){
56324         var v = this.view, cm = this.grid.colModel;
56325         h = h.nextSibling;
56326         while(h){
56327             if(!cm.isHidden(v.getCellIndex(h))){
56328                 return h;
56329             }
56330             h = h.nextSibling;
56331         }
56332         return null;
56333     },
56334
56335     prevVisible : function(h){
56336         var v = this.view, cm = this.grid.colModel;
56337         h = h.prevSibling;
56338         while(h){
56339             if(!cm.isHidden(v.getCellIndex(h))){
56340                 return h;
56341             }
56342             h = h.prevSibling;
56343         }
56344         return null;
56345     },
56346
56347     positionIndicator : function(h, n, e){
56348         var x = Roo.lib.Event.getPageX(e);
56349         var r = Roo.lib.Dom.getRegion(n.firstChild);
56350         var px, pt, py = r.top + this.proxyOffsets[1];
56351         if((r.right - x) <= (r.right-r.left)/2){
56352             px = r.right+this.view.borderWidth;
56353             pt = "after";
56354         }else{
56355             px = r.left;
56356             pt = "before";
56357         }
56358         var oldIndex = this.view.getCellIndex(h);
56359         var newIndex = this.view.getCellIndex(n);
56360
56361         if(this.grid.colModel.isFixed(newIndex)){
56362             return false;
56363         }
56364
56365         var locked = this.grid.colModel.isLocked(newIndex);
56366
56367         if(pt == "after"){
56368             newIndex++;
56369         }
56370         if(oldIndex < newIndex){
56371             newIndex--;
56372         }
56373         if(oldIndex == newIndex && (locked == this.grid.colModel.isLocked(oldIndex))){
56374             return false;
56375         }
56376         px +=  this.proxyOffsets[0];
56377         this.proxyTop.setLeftTop(px, py);
56378         this.proxyTop.show();
56379         if(!this.bottomOffset){
56380             this.bottomOffset = this.view.mainHd.getHeight();
56381         }
56382         this.proxyBottom.setLeftTop(px, py+this.proxyTop.dom.offsetHeight+this.bottomOffset);
56383         this.proxyBottom.show();
56384         return pt;
56385     },
56386
56387     onNodeEnter : function(n, dd, e, data){
56388         if(data.header != n){
56389             this.positionIndicator(data.header, n, e);
56390         }
56391     },
56392
56393     onNodeOver : function(n, dd, e, data){
56394         var result = false;
56395         if(data.header != n){
56396             result = this.positionIndicator(data.header, n, e);
56397         }
56398         if(!result){
56399             this.proxyTop.hide();
56400             this.proxyBottom.hide();
56401         }
56402         return result ? this.dropAllowed : this.dropNotAllowed;
56403     },
56404
56405     onNodeOut : function(n, dd, e, data){
56406         this.proxyTop.hide();
56407         this.proxyBottom.hide();
56408     },
56409
56410     onNodeDrop : function(n, dd, e, data){
56411         var h = data.header;
56412         if(h != n){
56413             var cm = this.grid.colModel;
56414             var x = Roo.lib.Event.getPageX(e);
56415             var r = Roo.lib.Dom.getRegion(n.firstChild);
56416             var pt = (r.right - x) <= ((r.right-r.left)/2) ? "after" : "before";
56417             var oldIndex = this.view.getCellIndex(h);
56418             var newIndex = this.view.getCellIndex(n);
56419             var locked = cm.isLocked(newIndex);
56420             if(pt == "after"){
56421                 newIndex++;
56422             }
56423             if(oldIndex < newIndex){
56424                 newIndex--;
56425             }
56426             if(oldIndex == newIndex && (locked == cm.isLocked(oldIndex))){
56427                 return false;
56428             }
56429             cm.setLocked(oldIndex, locked, true);
56430             cm.moveColumn(oldIndex, newIndex);
56431             this.grid.fireEvent("columnmove", oldIndex, newIndex);
56432             return true;
56433         }
56434         return false;
56435     }
56436 });
56437 /*
56438  * Based on:
56439  * Ext JS Library 1.1.1
56440  * Copyright(c) 2006-2007, Ext JS, LLC.
56441  *
56442  * Originally Released Under LGPL - original licence link has changed is not relivant.
56443  *
56444  * Fork - LGPL
56445  * <script type="text/javascript">
56446  */
56447   
56448 /**
56449  * @class Roo.grid.GridView
56450  * @extends Roo.util.Observable
56451  *
56452  * @constructor
56453  * @param {Object} config
56454  */
56455 Roo.grid.GridView = function(config){
56456     Roo.grid.GridView.superclass.constructor.call(this);
56457     this.el = null;
56458
56459     Roo.apply(this, config);
56460 };
56461
56462 Roo.extend(Roo.grid.GridView, Roo.grid.AbstractGridView, {
56463
56464     unselectable :  'unselectable="on"',
56465     unselectableCls :  'x-unselectable',
56466     
56467     
56468     rowClass : "x-grid-row",
56469
56470     cellClass : "x-grid-col",
56471
56472     tdClass : "x-grid-td",
56473
56474     hdClass : "x-grid-hd",
56475
56476     splitClass : "x-grid-split",
56477
56478     sortClasses : ["sort-asc", "sort-desc"],
56479
56480     enableMoveAnim : false,
56481
56482     hlColor: "C3DAF9",
56483
56484     dh : Roo.DomHelper,
56485
56486     fly : Roo.Element.fly,
56487
56488     css : Roo.util.CSS,
56489
56490     borderWidth: 1,
56491
56492     splitOffset: 3,
56493
56494     scrollIncrement : 22,
56495
56496     cellRE: /(?:.*?)x-grid-(?:hd|cell|csplit)-(?:[\d]+)-([\d]+)(?:.*?)/,
56497
56498     findRE: /\s?(?:x-grid-hd|x-grid-col|x-grid-csplit)\s/,
56499
56500     bind : function(ds, cm){
56501         if(this.ds){
56502             this.ds.un("load", this.onLoad, this);
56503             this.ds.un("datachanged", this.onDataChange, this);
56504             this.ds.un("add", this.onAdd, this);
56505             this.ds.un("remove", this.onRemove, this);
56506             this.ds.un("update", this.onUpdate, this);
56507             this.ds.un("clear", this.onClear, this);
56508         }
56509         if(ds){
56510             ds.on("load", this.onLoad, this);
56511             ds.on("datachanged", this.onDataChange, this);
56512             ds.on("add", this.onAdd, this);
56513             ds.on("remove", this.onRemove, this);
56514             ds.on("update", this.onUpdate, this);
56515             ds.on("clear", this.onClear, this);
56516         }
56517         this.ds = ds;
56518
56519         if(this.cm){
56520             this.cm.un("widthchange", this.onColWidthChange, this);
56521             this.cm.un("headerchange", this.onHeaderChange, this);
56522             this.cm.un("hiddenchange", this.onHiddenChange, this);
56523             this.cm.un("columnmoved", this.onColumnMove, this);
56524             this.cm.un("columnlockchange", this.onColumnLock, this);
56525         }
56526         if(cm){
56527             this.generateRules(cm);
56528             cm.on("widthchange", this.onColWidthChange, this);
56529             cm.on("headerchange", this.onHeaderChange, this);
56530             cm.on("hiddenchange", this.onHiddenChange, this);
56531             cm.on("columnmoved", this.onColumnMove, this);
56532             cm.on("columnlockchange", this.onColumnLock, this);
56533         }
56534         this.cm = cm;
56535     },
56536
56537     init: function(grid){
56538         Roo.grid.GridView.superclass.init.call(this, grid);
56539
56540         this.bind(grid.dataSource, grid.colModel);
56541
56542         grid.on("headerclick", this.handleHeaderClick, this);
56543
56544         if(grid.trackMouseOver){
56545             grid.on("mouseover", this.onRowOver, this);
56546             grid.on("mouseout", this.onRowOut, this);
56547         }
56548         grid.cancelTextSelection = function(){};
56549         this.gridId = grid.id;
56550
56551         var tpls = this.templates || {};
56552
56553         if(!tpls.master){
56554             tpls.master = new Roo.Template(
56555                '<div class="x-grid" hidefocus="true">',
56556                 '<a href="#" class="x-grid-focus" tabIndex="-1"></a>',
56557                   '<div class="x-grid-topbar"></div>',
56558                   '<div class="x-grid-scroller"><div></div></div>',
56559                   '<div class="x-grid-locked">',
56560                       '<div class="x-grid-header">{lockedHeader}</div>',
56561                       '<div class="x-grid-body">{lockedBody}</div>',
56562                   "</div>",
56563                   '<div class="x-grid-viewport">',
56564                       '<div class="x-grid-header">{header}</div>',
56565                       '<div class="x-grid-body">{body}</div>',
56566                   "</div>",
56567                   '<div class="x-grid-bottombar"></div>',
56568                  
56569                   '<div class="x-grid-resize-proxy">&#160;</div>',
56570                "</div>"
56571             );
56572             tpls.master.disableformats = true;
56573         }
56574
56575         if(!tpls.header){
56576             tpls.header = new Roo.Template(
56577                '<table border="0" cellspacing="0" cellpadding="0">',
56578                '<tbody><tr class="x-grid-hd-row">{cells}</tr></tbody>',
56579                "</table>{splits}"
56580             );
56581             tpls.header.disableformats = true;
56582         }
56583         tpls.header.compile();
56584
56585         if(!tpls.hcell){
56586             tpls.hcell = new Roo.Template(
56587                 '<td class="x-grid-hd x-grid-td-{id} {cellId}"><div title="{title}" class="x-grid-hd-inner x-grid-hd-{id}">',
56588                 '<div class="x-grid-hd-text ' + this.unselectableCls +  '" ' + this.unselectable +'>{value}<img class="x-grid-sort-icon" src="', Roo.BLANK_IMAGE_URL, '" /></div>',
56589                 "</div></td>"
56590              );
56591              tpls.hcell.disableFormats = true;
56592         }
56593         tpls.hcell.compile();
56594
56595         if(!tpls.hsplit){
56596             tpls.hsplit = new Roo.Template('<div class="x-grid-split {splitId} x-grid-split-{id}" style="{style} ' +
56597                                             this.unselectableCls +  '" ' + this.unselectable +'>&#160;</div>');
56598             tpls.hsplit.disableFormats = true;
56599         }
56600         tpls.hsplit.compile();
56601
56602         if(!tpls.body){
56603             tpls.body = new Roo.Template(
56604                '<table border="0" cellspacing="0" cellpadding="0">',
56605                "<tbody>{rows}</tbody>",
56606                "</table>"
56607             );
56608             tpls.body.disableFormats = true;
56609         }
56610         tpls.body.compile();
56611
56612         if(!tpls.row){
56613             tpls.row = new Roo.Template('<tr class="x-grid-row {alt}">{cells}</tr>');
56614             tpls.row.disableFormats = true;
56615         }
56616         tpls.row.compile();
56617
56618         if(!tpls.cell){
56619             tpls.cell = new Roo.Template(
56620                 '<td class="x-grid-col x-grid-td-{id} {cellId} {css}" tabIndex="0">',
56621                 '<div class="x-grid-col-{id} x-grid-cell-inner"><div class="x-grid-cell-text ' +
56622                     this.unselectableCls +  '" ' + this.unselectable +'" {attr}>{value}</div></div>',
56623                 "</td>"
56624             );
56625             tpls.cell.disableFormats = true;
56626         }
56627         tpls.cell.compile();
56628
56629         this.templates = tpls;
56630     },
56631
56632     // remap these for backwards compat
56633     onColWidthChange : function(){
56634         this.updateColumns.apply(this, arguments);
56635     },
56636     onHeaderChange : function(){
56637         this.updateHeaders.apply(this, arguments);
56638     }, 
56639     onHiddenChange : function(){
56640         this.handleHiddenChange.apply(this, arguments);
56641     },
56642     onColumnMove : function(){
56643         this.handleColumnMove.apply(this, arguments);
56644     },
56645     onColumnLock : function(){
56646         this.handleLockChange.apply(this, arguments);
56647     },
56648
56649     onDataChange : function(){
56650         this.refresh();
56651         this.updateHeaderSortState();
56652     },
56653
56654     onClear : function(){
56655         this.refresh();
56656     },
56657
56658     onUpdate : function(ds, record){
56659         this.refreshRow(record);
56660     },
56661
56662     refreshRow : function(record){
56663         var ds = this.ds, index;
56664         if(typeof record == 'number'){
56665             index = record;
56666             record = ds.getAt(index);
56667         }else{
56668             index = ds.indexOf(record);
56669         }
56670         this.insertRows(ds, index, index, true);
56671         this.onRemove(ds, record, index+1, true);
56672         this.syncRowHeights(index, index);
56673         this.layout();
56674         this.fireEvent("rowupdated", this, index, record);
56675     },
56676
56677     onAdd : function(ds, records, index){
56678         this.insertRows(ds, index, index + (records.length-1));
56679     },
56680
56681     onRemove : function(ds, record, index, isUpdate){
56682         if(isUpdate !== true){
56683             this.fireEvent("beforerowremoved", this, index, record);
56684         }
56685         var bt = this.getBodyTable(), lt = this.getLockedTable();
56686         if(bt.rows[index]){
56687             bt.firstChild.removeChild(bt.rows[index]);
56688         }
56689         if(lt.rows[index]){
56690             lt.firstChild.removeChild(lt.rows[index]);
56691         }
56692         if(isUpdate !== true){
56693             this.stripeRows(index);
56694             this.syncRowHeights(index, index);
56695             this.layout();
56696             this.fireEvent("rowremoved", this, index, record);
56697         }
56698     },
56699
56700     onLoad : function(){
56701         this.scrollToTop();
56702     },
56703
56704     /**
56705      * Scrolls the grid to the top
56706      */
56707     scrollToTop : function(){
56708         if(this.scroller){
56709             this.scroller.dom.scrollTop = 0;
56710             this.syncScroll();
56711         }
56712     },
56713
56714     /**
56715      * Gets a panel in the header of the grid that can be used for toolbars etc.
56716      * After modifying the contents of this panel a call to grid.autoSize() may be
56717      * required to register any changes in size.
56718      * @param {Boolean} doShow By default the header is hidden. Pass true to show the panel
56719      * @return Roo.Element
56720      */
56721     getHeaderPanel : function(doShow){
56722         if(doShow){
56723             this.headerPanel.show();
56724         }
56725         return this.headerPanel;
56726     },
56727
56728     /**
56729      * Gets a panel in the footer of the grid that can be used for toolbars etc.
56730      * After modifying the contents of this panel a call to grid.autoSize() may be
56731      * required to register any changes in size.
56732      * @param {Boolean} doShow By default the footer is hidden. Pass true to show the panel
56733      * @return Roo.Element
56734      */
56735     getFooterPanel : function(doShow){
56736         if(doShow){
56737             this.footerPanel.show();
56738         }
56739         return this.footerPanel;
56740     },
56741
56742     initElements : function(){
56743         var E = Roo.Element;
56744         var el = this.grid.getGridEl().dom.firstChild;
56745         var cs = el.childNodes;
56746
56747         this.el = new E(el);
56748         
56749          this.focusEl = new E(el.firstChild);
56750         this.focusEl.swallowEvent("click", true);
56751         
56752         this.headerPanel = new E(cs[1]);
56753         this.headerPanel.enableDisplayMode("block");
56754
56755         this.scroller = new E(cs[2]);
56756         this.scrollSizer = new E(this.scroller.dom.firstChild);
56757
56758         this.lockedWrap = new E(cs[3]);
56759         this.lockedHd = new E(this.lockedWrap.dom.firstChild);
56760         this.lockedBody = new E(this.lockedWrap.dom.childNodes[1]);
56761
56762         this.mainWrap = new E(cs[4]);
56763         this.mainHd = new E(this.mainWrap.dom.firstChild);
56764         this.mainBody = new E(this.mainWrap.dom.childNodes[1]);
56765
56766         this.footerPanel = new E(cs[5]);
56767         this.footerPanel.enableDisplayMode("block");
56768
56769         this.resizeProxy = new E(cs[6]);
56770
56771         this.headerSelector = String.format(
56772            '#{0} td.x-grid-hd, #{1} td.x-grid-hd',
56773            this.lockedHd.id, this.mainHd.id
56774         );
56775
56776         this.splitterSelector = String.format(
56777            '#{0} div.x-grid-split, #{1} div.x-grid-split',
56778            this.idToCssName(this.lockedHd.id), this.idToCssName(this.mainHd.id)
56779         );
56780     },
56781     idToCssName : function(s)
56782     {
56783         return s.replace(/[^a-z0-9]+/ig, '-');
56784     },
56785
56786     getHeaderCell : function(index){
56787         return Roo.DomQuery.select(this.headerSelector)[index];
56788     },
56789
56790     getHeaderCellMeasure : function(index){
56791         return this.getHeaderCell(index).firstChild;
56792     },
56793
56794     getHeaderCellText : function(index){
56795         return this.getHeaderCell(index).firstChild.firstChild;
56796     },
56797
56798     getLockedTable : function(){
56799         return this.lockedBody.dom.firstChild;
56800     },
56801
56802     getBodyTable : function(){
56803         return this.mainBody.dom.firstChild;
56804     },
56805
56806     getLockedRow : function(index){
56807         return this.getLockedTable().rows[index];
56808     },
56809
56810     getRow : function(index){
56811         return this.getBodyTable().rows[index];
56812     },
56813
56814     getRowComposite : function(index){
56815         if(!this.rowEl){
56816             this.rowEl = new Roo.CompositeElementLite();
56817         }
56818         var els = [], lrow, mrow;
56819         if(lrow = this.getLockedRow(index)){
56820             els.push(lrow);
56821         }
56822         if(mrow = this.getRow(index)){
56823             els.push(mrow);
56824         }
56825         this.rowEl.elements = els;
56826         return this.rowEl;
56827     },
56828     /**
56829      * Gets the 'td' of the cell
56830      * 
56831      * @param {Integer} rowIndex row to select
56832      * @param {Integer} colIndex column to select
56833      * 
56834      * @return {Object} 
56835      */
56836     getCell : function(rowIndex, colIndex){
56837         var locked = this.cm.getLockedCount();
56838         var source;
56839         if(colIndex < locked){
56840             source = this.lockedBody.dom.firstChild;
56841         }else{
56842             source = this.mainBody.dom.firstChild;
56843             colIndex -= locked;
56844         }
56845         return source.rows[rowIndex].childNodes[colIndex];
56846     },
56847
56848     getCellText : function(rowIndex, colIndex){
56849         return this.getCell(rowIndex, colIndex).firstChild.firstChild;
56850     },
56851
56852     getCellBox : function(cell){
56853         var b = this.fly(cell).getBox();
56854         if(Roo.isOpera){ // opera fails to report the Y
56855             b.y = cell.offsetTop + this.mainBody.getY();
56856         }
56857         return b;
56858     },
56859
56860     getCellIndex : function(cell){
56861         var id = String(cell.className).match(this.cellRE);
56862         if(id){
56863             return parseInt(id[1], 10);
56864         }
56865         return 0;
56866     },
56867
56868     findHeaderIndex : function(n){
56869         var r = Roo.fly(n).findParent("td." + this.hdClass, 6);
56870         return r ? this.getCellIndex(r) : false;
56871     },
56872
56873     findHeaderCell : function(n){
56874         var r = Roo.fly(n).findParent("td." + this.hdClass, 6);
56875         return r ? r : false;
56876     },
56877
56878     findRowIndex : function(n){
56879         if(!n){
56880             return false;
56881         }
56882         var r = Roo.fly(n).findParent("tr." + this.rowClass, 6);
56883         return r ? r.rowIndex : false;
56884     },
56885
56886     findCellIndex : function(node){
56887         var stop = this.el.dom;
56888         while(node && node != stop){
56889             if(this.findRE.test(node.className)){
56890                 return this.getCellIndex(node);
56891             }
56892             node = node.parentNode;
56893         }
56894         return false;
56895     },
56896
56897     getColumnId : function(index){
56898         return this.cm.getColumnId(index);
56899     },
56900
56901     getSplitters : function()
56902     {
56903         if(this.splitterSelector){
56904            return Roo.DomQuery.select(this.splitterSelector);
56905         }else{
56906             return null;
56907       }
56908     },
56909
56910     getSplitter : function(index){
56911         return this.getSplitters()[index];
56912     },
56913
56914     onRowOver : function(e, t){
56915         var row;
56916         if((row = this.findRowIndex(t)) !== false){
56917             this.getRowComposite(row).addClass("x-grid-row-over");
56918         }
56919     },
56920
56921     onRowOut : function(e, t){
56922         var row;
56923         if((row = this.findRowIndex(t)) !== false && row !== this.findRowIndex(e.getRelatedTarget())){
56924             this.getRowComposite(row).removeClass("x-grid-row-over");
56925         }
56926     },
56927
56928     renderHeaders : function(){
56929         var cm = this.cm;
56930         var ct = this.templates.hcell, ht = this.templates.header, st = this.templates.hsplit;
56931         var cb = [], lb = [], sb = [], lsb = [], p = {};
56932         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
56933             p.cellId = "x-grid-hd-0-" + i;
56934             p.splitId = "x-grid-csplit-0-" + i;
56935             p.id = cm.getColumnId(i);
56936             p.value = cm.getColumnHeader(i) || "";
56937             p.title = cm.getColumnTooltip(i) || (''+p.value).match(/\</)  ? '' :  p.value  || "";
56938             p.style = (this.grid.enableColumnResize === false || !cm.isResizable(i) || cm.isFixed(i)) ? 'cursor:default' : '';
56939             if(!cm.isLocked(i)){
56940                 cb[cb.length] = ct.apply(p);
56941                 sb[sb.length] = st.apply(p);
56942             }else{
56943                 lb[lb.length] = ct.apply(p);
56944                 lsb[lsb.length] = st.apply(p);
56945             }
56946         }
56947         return [ht.apply({cells: lb.join(""), splits:lsb.join("")}),
56948                 ht.apply({cells: cb.join(""), splits:sb.join("")})];
56949     },
56950
56951     updateHeaders : function(){
56952         var html = this.renderHeaders();
56953         this.lockedHd.update(html[0]);
56954         this.mainHd.update(html[1]);
56955     },
56956
56957     /**
56958      * Focuses the specified row.
56959      * @param {Number} row The row index
56960      */
56961     focusRow : function(row)
56962     {
56963         //Roo.log('GridView.focusRow');
56964         var x = this.scroller.dom.scrollLeft;
56965         this.focusCell(row, 0, false);
56966         this.scroller.dom.scrollLeft = x;
56967     },
56968
56969     /**
56970      * Focuses the specified cell.
56971      * @param {Number} row The row index
56972      * @param {Number} col The column index
56973      * @param {Boolean} hscroll false to disable horizontal scrolling
56974      */
56975     focusCell : function(row, col, hscroll)
56976     {
56977         //Roo.log('GridView.focusCell');
56978         var el = this.ensureVisible(row, col, hscroll);
56979         this.focusEl.alignTo(el, "tl-tl");
56980         if(Roo.isGecko){
56981             this.focusEl.focus();
56982         }else{
56983             this.focusEl.focus.defer(1, this.focusEl);
56984         }
56985     },
56986
56987     /**
56988      * Scrolls the specified cell into view
56989      * @param {Number} row The row index
56990      * @param {Number} col The column index
56991      * @param {Boolean} hscroll false to disable horizontal scrolling
56992      */
56993     ensureVisible : function(row, col, hscroll)
56994     {
56995         //Roo.log('GridView.ensureVisible,' + row + ',' + col);
56996         //return null; //disable for testing.
56997         if(typeof row != "number"){
56998             row = row.rowIndex;
56999         }
57000         if(row < 0 && row >= this.ds.getCount()){
57001             return  null;
57002         }
57003         col = (col !== undefined ? col : 0);
57004         var cm = this.grid.colModel;
57005         while(cm.isHidden(col)){
57006             col++;
57007         }
57008
57009         var el = this.getCell(row, col);
57010         if(!el){
57011             return null;
57012         }
57013         var c = this.scroller.dom;
57014
57015         var ctop = parseInt(el.offsetTop, 10);
57016         var cleft = parseInt(el.offsetLeft, 10);
57017         var cbot = ctop + el.offsetHeight;
57018         var cright = cleft + el.offsetWidth;
57019         
57020         var ch = c.clientHeight - this.mainHd.dom.offsetHeight;
57021         var stop = parseInt(c.scrollTop, 10);
57022         var sleft = parseInt(c.scrollLeft, 10);
57023         var sbot = stop + ch;
57024         var sright = sleft + c.clientWidth;
57025         /*
57026         Roo.log('GridView.ensureVisible:' +
57027                 ' ctop:' + ctop +
57028                 ' c.clientHeight:' + c.clientHeight +
57029                 ' this.mainHd.dom.offsetHeight:' + this.mainHd.dom.offsetHeight +
57030                 ' stop:' + stop +
57031                 ' cbot:' + cbot +
57032                 ' sbot:' + sbot +
57033                 ' ch:' + ch  
57034                 );
57035         */
57036         if(ctop < stop){
57037             c.scrollTop = ctop;
57038             //Roo.log("set scrolltop to ctop DISABLE?");
57039         }else if(cbot > sbot){
57040             //Roo.log("set scrolltop to cbot-ch");
57041             c.scrollTop = cbot-ch;
57042         }
57043         
57044         if(hscroll !== false){
57045             if(cleft < sleft){
57046                 c.scrollLeft = cleft;
57047             }else if(cright > sright){
57048                 c.scrollLeft = cright-c.clientWidth;
57049             }
57050         }
57051          
57052         return el;
57053     },
57054
57055     updateColumns : function(){
57056         this.grid.stopEditing();
57057         var cm = this.grid.colModel, colIds = this.getColumnIds();
57058         //var totalWidth = cm.getTotalWidth();
57059         var pos = 0;
57060         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
57061             //if(cm.isHidden(i)) continue;
57062             var w = cm.getColumnWidth(i);
57063             this.css.updateRule(this.colSelector+this.idToCssName(colIds[i]), "width", (w - this.borderWidth) + "px");
57064             this.css.updateRule(this.hdSelector+this.idToCssName(colIds[i]), "width", (w - this.borderWidth) + "px");
57065         }
57066         this.updateSplitters();
57067     },
57068
57069     generateRules : function(cm){
57070         var ruleBuf = [], rulesId = this.idToCssName(this.grid.id)+ '-cssrules';
57071         Roo.util.CSS.removeStyleSheet(rulesId);
57072         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
57073             var cid = cm.getColumnId(i);
57074             var align = '';
57075             if(cm.config[i].align){
57076                 align = 'text-align:'+cm.config[i].align+';';
57077             }
57078             var hidden = '';
57079             if(cm.isHidden(i)){
57080                 hidden = 'display:none;';
57081             }
57082             var width = "width:" + (cm.getColumnWidth(i) - this.borderWidth) + "px;";
57083             ruleBuf.push(
57084                     this.colSelector, cid, " {\n", cm.config[i].css, align, width, "\n}\n",
57085                     this.hdSelector, cid, " {\n", align, width, "}\n",
57086                     this.tdSelector, cid, " {\n",hidden,"\n}\n",
57087                     this.splitSelector, cid, " {\n", hidden , "\n}\n");
57088         }
57089         return Roo.util.CSS.createStyleSheet(ruleBuf.join(""), rulesId);
57090     },
57091
57092     updateSplitters : function(){
57093         var cm = this.cm, s = this.getSplitters();
57094         if(s){ // splitters not created yet
57095             var pos = 0, locked = true;
57096             for(var i = 0, len = cm.getColumnCount(); i < len; i++){
57097                 if(cm.isHidden(i)) {
57098                     continue;
57099                 }
57100                 var w = cm.getColumnWidth(i); // make sure it's a number
57101                 if(!cm.isLocked(i) && locked){
57102                     pos = 0;
57103                     locked = false;
57104                 }
57105                 pos += w;
57106                 s[i].style.left = (pos-this.splitOffset) + "px";
57107             }
57108         }
57109     },
57110
57111     handleHiddenChange : function(colModel, colIndex, hidden){
57112         if(hidden){
57113             this.hideColumn(colIndex);
57114         }else{
57115             this.unhideColumn(colIndex);
57116         }
57117     },
57118
57119     hideColumn : function(colIndex){
57120         var cid = this.getColumnId(colIndex);
57121         this.css.updateRule(this.tdSelector+this.idToCssName(cid), "display", "none");
57122         this.css.updateRule(this.splitSelector+this.idToCssName(cid), "display", "none");
57123         if(Roo.isSafari){
57124             this.updateHeaders();
57125         }
57126         this.updateSplitters();
57127         this.layout();
57128     },
57129
57130     unhideColumn : function(colIndex){
57131         var cid = this.getColumnId(colIndex);
57132         this.css.updateRule(this.tdSelector+this.idToCssName(cid), "display", "");
57133         this.css.updateRule(this.splitSelector+this.idToCssName(cid), "display", "");
57134
57135         if(Roo.isSafari){
57136             this.updateHeaders();
57137         }
57138         this.updateSplitters();
57139         this.layout();
57140     },
57141
57142     insertRows : function(dm, firstRow, lastRow, isUpdate){
57143         if(firstRow == 0 && lastRow == dm.getCount()-1){
57144             this.refresh();
57145         }else{
57146             if(!isUpdate){
57147                 this.fireEvent("beforerowsinserted", this, firstRow, lastRow);
57148             }
57149             var s = this.getScrollState();
57150             var markup = this.renderRows(firstRow, lastRow);
57151             this.bufferRows(markup[0], this.getLockedTable(), firstRow);
57152             this.bufferRows(markup[1], this.getBodyTable(), firstRow);
57153             this.restoreScroll(s);
57154             if(!isUpdate){
57155                 this.fireEvent("rowsinserted", this, firstRow, lastRow);
57156                 this.syncRowHeights(firstRow, lastRow);
57157                 this.stripeRows(firstRow);
57158                 this.layout();
57159             }
57160         }
57161     },
57162
57163     bufferRows : function(markup, target, index){
57164         var before = null, trows = target.rows, tbody = target.tBodies[0];
57165         if(index < trows.length){
57166             before = trows[index];
57167         }
57168         var b = document.createElement("div");
57169         b.innerHTML = "<table><tbody>"+markup+"</tbody></table>";
57170         var rows = b.firstChild.rows;
57171         for(var i = 0, len = rows.length; i < len; i++){
57172             if(before){
57173                 tbody.insertBefore(rows[0], before);
57174             }else{
57175                 tbody.appendChild(rows[0]);
57176             }
57177         }
57178         b.innerHTML = "";
57179         b = null;
57180     },
57181
57182     deleteRows : function(dm, firstRow, lastRow){
57183         if(dm.getRowCount()<1){
57184             this.fireEvent("beforerefresh", this);
57185             this.mainBody.update("");
57186             this.lockedBody.update("");
57187             this.fireEvent("refresh", this);
57188         }else{
57189             this.fireEvent("beforerowsdeleted", this, firstRow, lastRow);
57190             var bt = this.getBodyTable();
57191             var tbody = bt.firstChild;
57192             var rows = bt.rows;
57193             for(var rowIndex = firstRow; rowIndex <= lastRow; rowIndex++){
57194                 tbody.removeChild(rows[firstRow]);
57195             }
57196             this.stripeRows(firstRow);
57197             this.fireEvent("rowsdeleted", this, firstRow, lastRow);
57198         }
57199     },
57200
57201     updateRows : function(dataSource, firstRow, lastRow){
57202         var s = this.getScrollState();
57203         this.refresh();
57204         this.restoreScroll(s);
57205     },
57206
57207     handleSort : function(dataSource, sortColumnIndex, sortDir, noRefresh){
57208         if(!noRefresh){
57209            this.refresh();
57210         }
57211         this.updateHeaderSortState();
57212     },
57213
57214     getScrollState : function(){
57215         
57216         var sb = this.scroller.dom;
57217         return {left: sb.scrollLeft, top: sb.scrollTop};
57218     },
57219
57220     stripeRows : function(startRow){
57221         if(!this.grid.stripeRows || this.ds.getCount() < 1){
57222             return;
57223         }
57224         startRow = startRow || 0;
57225         var rows = this.getBodyTable().rows;
57226         var lrows = this.getLockedTable().rows;
57227         var cls = ' x-grid-row-alt ';
57228         for(var i = startRow, len = rows.length; i < len; i++){
57229             var row = rows[i], lrow = lrows[i];
57230             var isAlt = ((i+1) % 2 == 0);
57231             var hasAlt = (' '+row.className + ' ').indexOf(cls) != -1;
57232             if(isAlt == hasAlt){
57233                 continue;
57234             }
57235             if(isAlt){
57236                 row.className += " x-grid-row-alt";
57237             }else{
57238                 row.className = row.className.replace("x-grid-row-alt", "");
57239             }
57240             if(lrow){
57241                 lrow.className = row.className;
57242             }
57243         }
57244     },
57245
57246     restoreScroll : function(state){
57247         //Roo.log('GridView.restoreScroll');
57248         var sb = this.scroller.dom;
57249         sb.scrollLeft = state.left;
57250         sb.scrollTop = state.top;
57251         this.syncScroll();
57252     },
57253
57254     syncScroll : function(){
57255         //Roo.log('GridView.syncScroll');
57256         var sb = this.scroller.dom;
57257         var sh = this.mainHd.dom;
57258         var bs = this.mainBody.dom;
57259         var lv = this.lockedBody.dom;
57260         sh.scrollLeft = bs.scrollLeft = sb.scrollLeft;
57261         lv.scrollTop = bs.scrollTop = sb.scrollTop;
57262     },
57263
57264     handleScroll : function(e){
57265         this.syncScroll();
57266         var sb = this.scroller.dom;
57267         this.grid.fireEvent("bodyscroll", sb.scrollLeft, sb.scrollTop);
57268         e.stopEvent();
57269     },
57270
57271     handleWheel : function(e){
57272         var d = e.getWheelDelta();
57273         this.scroller.dom.scrollTop -= d*22;
57274         // set this here to prevent jumpy scrolling on large tables
57275         this.lockedBody.dom.scrollTop = this.mainBody.dom.scrollTop = this.scroller.dom.scrollTop;
57276         e.stopEvent();
57277     },
57278
57279     renderRows : function(startRow, endRow){
57280         // pull in all the crap needed to render rows
57281         var g = this.grid, cm = g.colModel, ds = g.dataSource, stripe = g.stripeRows;
57282         var colCount = cm.getColumnCount();
57283
57284         if(ds.getCount() < 1){
57285             return ["", ""];
57286         }
57287
57288         // build a map for all the columns
57289         var cs = [];
57290         for(var i = 0; i < colCount; i++){
57291             var name = cm.getDataIndex(i);
57292             cs[i] = {
57293                 name : typeof name == 'undefined' ? ds.fields.get(i).name : name,
57294                 renderer : cm.getRenderer(i),
57295                 id : cm.getColumnId(i),
57296                 locked : cm.isLocked(i),
57297                 has_editor : cm.isCellEditable(i)
57298             };
57299         }
57300
57301         startRow = startRow || 0;
57302         endRow = typeof endRow == "undefined"? ds.getCount()-1 : endRow;
57303
57304         // records to render
57305         var rs = ds.getRange(startRow, endRow);
57306
57307         return this.doRender(cs, rs, ds, startRow, colCount, stripe);
57308     },
57309
57310     // As much as I hate to duplicate code, this was branched because FireFox really hates
57311     // [].join("") on strings. The performance difference was substantial enough to
57312     // branch this function
57313     doRender : Roo.isGecko ?
57314             function(cs, rs, ds, startRow, colCount, stripe){
57315                 var ts = this.templates, ct = ts.cell, rt = ts.row;
57316                 // buffers
57317                 var buf = "", lbuf = "", cb, lcb, c, p = {}, rp = {}, r, rowIndex;
57318                 
57319                 var hasListener = this.grid.hasListener('rowclass');
57320                 var rowcfg = {};
57321                 for(var j = 0, len = rs.length; j < len; j++){
57322                     r = rs[j]; cb = ""; lcb = ""; rowIndex = (j+startRow);
57323                     for(var i = 0; i < colCount; i++){
57324                         c = cs[i];
57325                         p.cellId = "x-grid-cell-" + rowIndex + "-" + i;
57326                         p.id = c.id;
57327                         p.css = p.attr = "";
57328                         p.value = c.renderer(r.data[c.name], p, r, rowIndex, i, ds);
57329                         if(p.value == undefined || p.value === "") {
57330                             p.value = "&#160;";
57331                         }
57332                         if(c.has_editor){
57333                             p.css += ' x-grid-editable-cell';
57334                         }
57335                         if(c.dirty && typeof r.modified[c.name] !== 'undefined'){
57336                             p.css +=  ' x-grid-dirty-cell';
57337                         }
57338                         var markup = ct.apply(p);
57339                         if(!c.locked){
57340                             cb+= markup;
57341                         }else{
57342                             lcb+= markup;
57343                         }
57344                     }
57345                     var alt = [];
57346                     if(stripe && ((rowIndex+1) % 2 == 0)){
57347                         alt.push("x-grid-row-alt")
57348                     }
57349                     if(r.dirty){
57350                         alt.push(  " x-grid-dirty-row");
57351                     }
57352                     rp.cells = lcb;
57353                     if(this.getRowClass){
57354                         alt.push(this.getRowClass(r, rowIndex));
57355                     }
57356                     if (hasListener) {
57357                         rowcfg = {
57358                              
57359                             record: r,
57360                             rowIndex : rowIndex,
57361                             rowClass : ''
57362                         };
57363                         this.grid.fireEvent('rowclass', this, rowcfg);
57364                         alt.push(rowcfg.rowClass);
57365                     }
57366                     rp.alt = alt.join(" ");
57367                     lbuf+= rt.apply(rp);
57368                     rp.cells = cb;
57369                     buf+=  rt.apply(rp);
57370                 }
57371                 return [lbuf, buf];
57372             } :
57373             function(cs, rs, ds, startRow, colCount, stripe){
57374                 var ts = this.templates, ct = ts.cell, rt = ts.row;
57375                 // buffers
57376                 var buf = [], lbuf = [], cb, lcb, c, p = {}, rp = {}, r, rowIndex;
57377                 var hasListener = this.grid.hasListener('rowclass');
57378  
57379                 var rowcfg = {};
57380                 for(var j = 0, len = rs.length; j < len; j++){
57381                     r = rs[j]; cb = []; lcb = []; rowIndex = (j+startRow);
57382                     for(var i = 0; i < colCount; i++){
57383                         c = cs[i];
57384                         p.cellId = "x-grid-cell-" + rowIndex + "-" + i;
57385                         p.id = c.id;
57386                         p.css = p.attr = "";
57387                         p.value = c.renderer(r.data[c.name], p, r, rowIndex, i, ds);
57388                         if(p.value == undefined || p.value === "") {
57389                             p.value = "&#160;";
57390                         }
57391                         //Roo.log(c);
57392                          if(c.has_editor){
57393                             p.css += ' x-grid-editable-cell';
57394                         }
57395                         if(r.dirty && typeof r.modified[c.name] !== 'undefined'){
57396                             p.css += ' x-grid-dirty-cell' 
57397                         }
57398                         
57399                         var markup = ct.apply(p);
57400                         if(!c.locked){
57401                             cb[cb.length] = markup;
57402                         }else{
57403                             lcb[lcb.length] = markup;
57404                         }
57405                     }
57406                     var alt = [];
57407                     if(stripe && ((rowIndex+1) % 2 == 0)){
57408                         alt.push( "x-grid-row-alt");
57409                     }
57410                     if(r.dirty){
57411                         alt.push(" x-grid-dirty-row");
57412                     }
57413                     rp.cells = lcb;
57414                     if(this.getRowClass){
57415                         alt.push( this.getRowClass(r, rowIndex));
57416                     }
57417                     if (hasListener) {
57418                         rowcfg = {
57419                              
57420                             record: r,
57421                             rowIndex : rowIndex,
57422                             rowClass : ''
57423                         };
57424                         this.grid.fireEvent('rowclass', this, rowcfg);
57425                         alt.push(rowcfg.rowClass);
57426                     }
57427                     
57428                     rp.alt = alt.join(" ");
57429                     rp.cells = lcb.join("");
57430                     lbuf[lbuf.length] = rt.apply(rp);
57431                     rp.cells = cb.join("");
57432                     buf[buf.length] =  rt.apply(rp);
57433                 }
57434                 return [lbuf.join(""), buf.join("")];
57435             },
57436
57437     renderBody : function(){
57438         var markup = this.renderRows();
57439         var bt = this.templates.body;
57440         return [bt.apply({rows: markup[0]}), bt.apply({rows: markup[1]})];
57441     },
57442
57443     /**
57444      * Refreshes the grid
57445      * @param {Boolean} headersToo
57446      */
57447     refresh : function(headersToo){
57448         this.fireEvent("beforerefresh", this);
57449         this.grid.stopEditing();
57450         var result = this.renderBody();
57451         this.lockedBody.update(result[0]);
57452         this.mainBody.update(result[1]);
57453         if(headersToo === true){
57454             this.updateHeaders();
57455             this.updateColumns();
57456             this.updateSplitters();
57457             this.updateHeaderSortState();
57458         }
57459         this.syncRowHeights();
57460         this.layout();
57461         this.fireEvent("refresh", this);
57462     },
57463
57464     handleColumnMove : function(cm, oldIndex, newIndex){
57465         this.indexMap = null;
57466         var s = this.getScrollState();
57467         this.refresh(true);
57468         this.restoreScroll(s);
57469         this.afterMove(newIndex);
57470     },
57471
57472     afterMove : function(colIndex){
57473         if(this.enableMoveAnim && Roo.enableFx){
57474             this.fly(this.getHeaderCell(colIndex).firstChild).highlight(this.hlColor);
57475         }
57476         // if multisort - fix sortOrder, and reload..
57477         if (this.grid.dataSource.multiSort) {
57478             // the we can call sort again..
57479             var dm = this.grid.dataSource;
57480             var cm = this.grid.colModel;
57481             var so = [];
57482             for(var i = 0; i < cm.config.length; i++ ) {
57483                 
57484                 if ((typeof(dm.sortToggle[cm.config[i].dataIndex]) == 'undefined')) {
57485                     continue; // dont' bother, it's not in sort list or being set.
57486                 }
57487                 
57488                 so.push(cm.config[i].dataIndex);
57489             };
57490             dm.sortOrder = so;
57491             dm.load(dm.lastOptions);
57492             
57493             
57494         }
57495         
57496     },
57497
57498     updateCell : function(dm, rowIndex, dataIndex){
57499         var colIndex = this.getColumnIndexByDataIndex(dataIndex);
57500         if(typeof colIndex == "undefined"){ // not present in grid
57501             return;
57502         }
57503         var cm = this.grid.colModel;
57504         var cell = this.getCell(rowIndex, colIndex);
57505         var cellText = this.getCellText(rowIndex, colIndex);
57506
57507         var p = {
57508             cellId : "x-grid-cell-" + rowIndex + "-" + colIndex,
57509             id : cm.getColumnId(colIndex),
57510             css: colIndex == cm.getColumnCount()-1 ? "x-grid-col-last" : ""
57511         };
57512         var renderer = cm.getRenderer(colIndex);
57513         var val = renderer(dm.getValueAt(rowIndex, dataIndex), p, rowIndex, colIndex, dm);
57514         if(typeof val == "undefined" || val === "") {
57515             val = "&#160;";
57516         }
57517         cellText.innerHTML = val;
57518         cell.className = this.cellClass + " " + this.idToCssName(p.cellId) + " " + p.css;
57519         this.syncRowHeights(rowIndex, rowIndex);
57520     },
57521
57522     calcColumnWidth : function(colIndex, maxRowsToMeasure){
57523         var maxWidth = 0;
57524         if(this.grid.autoSizeHeaders){
57525             var h = this.getHeaderCellMeasure(colIndex);
57526             maxWidth = Math.max(maxWidth, h.scrollWidth);
57527         }
57528         var tb, index;
57529         if(this.cm.isLocked(colIndex)){
57530             tb = this.getLockedTable();
57531             index = colIndex;
57532         }else{
57533             tb = this.getBodyTable();
57534             index = colIndex - this.cm.getLockedCount();
57535         }
57536         if(tb && tb.rows){
57537             var rows = tb.rows;
57538             var stopIndex = Math.min(maxRowsToMeasure || rows.length, rows.length);
57539             for(var i = 0; i < stopIndex; i++){
57540                 var cell = rows[i].childNodes[index].firstChild;
57541                 maxWidth = Math.max(maxWidth, cell.scrollWidth);
57542             }
57543         }
57544         return maxWidth + /*margin for error in IE*/ 5;
57545     },
57546     /**
57547      * Autofit a column to its content.
57548      * @param {Number} colIndex
57549      * @param {Boolean} forceMinSize true to force the column to go smaller if possible
57550      */
57551      autoSizeColumn : function(colIndex, forceMinSize, suppressEvent){
57552          if(this.cm.isHidden(colIndex)){
57553              return; // can't calc a hidden column
57554          }
57555         if(forceMinSize){
57556             var cid = this.cm.getColumnId(colIndex);
57557             this.css.updateRule(this.colSelector +this.idToCssName( cid), "width", this.grid.minColumnWidth + "px");
57558            if(this.grid.autoSizeHeaders){
57559                this.css.updateRule(this.hdSelector + this.idToCssName(cid), "width", this.grid.minColumnWidth + "px");
57560            }
57561         }
57562         var newWidth = this.calcColumnWidth(colIndex);
57563         this.cm.setColumnWidth(colIndex,
57564             Math.max(this.grid.minColumnWidth, newWidth), suppressEvent);
57565         if(!suppressEvent){
57566             this.grid.fireEvent("columnresize", colIndex, newWidth);
57567         }
57568     },
57569
57570     /**
57571      * Autofits all columns to their content and then expands to fit any extra space in the grid
57572      */
57573      autoSizeColumns : function(){
57574         var cm = this.grid.colModel;
57575         var colCount = cm.getColumnCount();
57576         for(var i = 0; i < colCount; i++){
57577             this.autoSizeColumn(i, true, true);
57578         }
57579         if(cm.getTotalWidth() < this.scroller.dom.clientWidth){
57580             this.fitColumns();
57581         }else{
57582             this.updateColumns();
57583             this.layout();
57584         }
57585     },
57586
57587     /**
57588      * Autofits all columns to the grid's width proportionate with their current size
57589      * @param {Boolean} reserveScrollSpace Reserve space for a scrollbar
57590      */
57591     fitColumns : function(reserveScrollSpace){
57592         var cm = this.grid.colModel;
57593         var colCount = cm.getColumnCount();
57594         var cols = [];
57595         var width = 0;
57596         var i, w;
57597         for (i = 0; i < colCount; i++){
57598             if(!cm.isHidden(i) && !cm.isFixed(i)){
57599                 w = cm.getColumnWidth(i);
57600                 cols.push(i);
57601                 cols.push(w);
57602                 width += w;
57603             }
57604         }
57605         var avail = Math.min(this.scroller.dom.clientWidth, this.el.getWidth());
57606         if(reserveScrollSpace){
57607             avail -= 17;
57608         }
57609         var frac = (avail - cm.getTotalWidth())/width;
57610         while (cols.length){
57611             w = cols.pop();
57612             i = cols.pop();
57613             cm.setColumnWidth(i, Math.floor(w + w*frac), true);
57614         }
57615         this.updateColumns();
57616         this.layout();
57617     },
57618
57619     onRowSelect : function(rowIndex){
57620         var row = this.getRowComposite(rowIndex);
57621         row.addClass("x-grid-row-selected");
57622     },
57623
57624     onRowDeselect : function(rowIndex){
57625         var row = this.getRowComposite(rowIndex);
57626         row.removeClass("x-grid-row-selected");
57627     },
57628
57629     onCellSelect : function(row, col){
57630         var cell = this.getCell(row, col);
57631         if(cell){
57632             Roo.fly(cell).addClass("x-grid-cell-selected");
57633         }
57634     },
57635
57636     onCellDeselect : function(row, col){
57637         var cell = this.getCell(row, col);
57638         if(cell){
57639             Roo.fly(cell).removeClass("x-grid-cell-selected");
57640         }
57641     },
57642
57643     updateHeaderSortState : function(){
57644         
57645         // sort state can be single { field: xxx, direction : yyy}
57646         // or   { xxx=>ASC , yyy : DESC ..... }
57647         
57648         var mstate = {};
57649         if (!this.ds.multiSort) { 
57650             var state = this.ds.getSortState();
57651             if(!state){
57652                 return;
57653             }
57654             mstate[state.field] = state.direction;
57655             // FIXME... - this is not used here.. but might be elsewhere..
57656             this.sortState = state;
57657             
57658         } else {
57659             mstate = this.ds.sortToggle;
57660         }
57661         //remove existing sort classes..
57662         
57663         var sc = this.sortClasses;
57664         var hds = this.el.select(this.headerSelector).removeClass(sc);
57665         
57666         for(var f in mstate) {
57667         
57668             var sortColumn = this.cm.findColumnIndex(f);
57669             
57670             if(sortColumn != -1){
57671                 var sortDir = mstate[f];        
57672                 hds.item(sortColumn).addClass(sc[sortDir == "DESC" ? 1 : 0]);
57673             }
57674         }
57675         
57676          
57677         
57678     },
57679
57680
57681     handleHeaderClick : function(g, index,e){
57682         
57683         Roo.log("header click");
57684         
57685         if (Roo.isTouch) {
57686             // touch events on header are handled by context
57687             this.handleHdCtx(g,index,e);
57688             return;
57689         }
57690         
57691         
57692         if(this.headersDisabled){
57693             return;
57694         }
57695         var dm = g.dataSource, cm = g.colModel;
57696         if(!cm.isSortable(index)){
57697             return;
57698         }
57699         g.stopEditing();
57700         
57701         if (dm.multiSort) {
57702             // update the sortOrder
57703             var so = [];
57704             for(var i = 0; i < cm.config.length; i++ ) {
57705                 
57706                 if ((typeof(dm.sortToggle[cm.config[i].dataIndex]) == 'undefined') && (index != i)) {
57707                     continue; // dont' bother, it's not in sort list or being set.
57708                 }
57709                 
57710                 so.push(cm.config[i].dataIndex);
57711             };
57712             dm.sortOrder = so;
57713         }
57714         
57715         
57716         dm.sort(cm.getDataIndex(index));
57717     },
57718
57719
57720     destroy : function(){
57721         if(this.colMenu){
57722             this.colMenu.removeAll();
57723             Roo.menu.MenuMgr.unregister(this.colMenu);
57724             this.colMenu.getEl().remove();
57725             delete this.colMenu;
57726         }
57727         if(this.hmenu){
57728             this.hmenu.removeAll();
57729             Roo.menu.MenuMgr.unregister(this.hmenu);
57730             this.hmenu.getEl().remove();
57731             delete this.hmenu;
57732         }
57733         if(this.grid.enableColumnMove){
57734             var dds = Roo.dd.DDM.ids['gridHeader' + this.grid.getGridEl().id];
57735             if(dds){
57736                 for(var dd in dds){
57737                     if(!dds[dd].config.isTarget && dds[dd].dragElId){
57738                         var elid = dds[dd].dragElId;
57739                         dds[dd].unreg();
57740                         Roo.get(elid).remove();
57741                     } else if(dds[dd].config.isTarget){
57742                         dds[dd].proxyTop.remove();
57743                         dds[dd].proxyBottom.remove();
57744                         dds[dd].unreg();
57745                     }
57746                     if(Roo.dd.DDM.locationCache[dd]){
57747                         delete Roo.dd.DDM.locationCache[dd];
57748                     }
57749                 }
57750                 delete Roo.dd.DDM.ids['gridHeader' + this.grid.getGridEl().id];
57751             }
57752         }
57753         Roo.util.CSS.removeStyleSheet(this.idToCssName(this.grid.id) + '-cssrules');
57754         this.bind(null, null);
57755         Roo.EventManager.removeResizeListener(this.onWindowResize, this);
57756     },
57757
57758     handleLockChange : function(){
57759         this.refresh(true);
57760     },
57761
57762     onDenyColumnLock : function(){
57763
57764     },
57765
57766     onDenyColumnHide : function(){
57767
57768     },
57769
57770     handleHdMenuClick : function(item){
57771         var index = this.hdCtxIndex;
57772         var cm = this.cm, ds = this.ds;
57773         switch(item.id){
57774             case "asc":
57775                 ds.sort(cm.getDataIndex(index), "ASC");
57776                 break;
57777             case "desc":
57778                 ds.sort(cm.getDataIndex(index), "DESC");
57779                 break;
57780             case "lock":
57781                 var lc = cm.getLockedCount();
57782                 if(cm.getColumnCount(true) <= lc+1){
57783                     this.onDenyColumnLock();
57784                     return;
57785                 }
57786                 if(lc != index){
57787                     cm.setLocked(index, true, true);
57788                     cm.moveColumn(index, lc);
57789                     this.grid.fireEvent("columnmove", index, lc);
57790                 }else{
57791                     cm.setLocked(index, true);
57792                 }
57793             break;
57794             case "unlock":
57795                 var lc = cm.getLockedCount();
57796                 if((lc-1) != index){
57797                     cm.setLocked(index, false, true);
57798                     cm.moveColumn(index, lc-1);
57799                     this.grid.fireEvent("columnmove", index, lc-1);
57800                 }else{
57801                     cm.setLocked(index, false);
57802                 }
57803             break;
57804             case 'wider': // used to expand cols on touch..
57805             case 'narrow':
57806                 var cw = cm.getColumnWidth(index);
57807                 cw += (item.id == 'wider' ? 1 : -1) * 50;
57808                 cw = Math.max(0, cw);
57809                 cw = Math.min(cw,4000);
57810                 cm.setColumnWidth(index, cw);
57811                 break;
57812                 
57813             default:
57814                 index = cm.getIndexById(item.id.substr(4));
57815                 if(index != -1){
57816                     if(item.checked && cm.getColumnCount(true) <= 1){
57817                         this.onDenyColumnHide();
57818                         return false;
57819                     }
57820                     cm.setHidden(index, item.checked);
57821                 }
57822         }
57823         return true;
57824     },
57825
57826     beforeColMenuShow : function(){
57827         var cm = this.cm,  colCount = cm.getColumnCount();
57828         this.colMenu.removeAll();
57829         for(var i = 0; i < colCount; i++){
57830             this.colMenu.add(new Roo.menu.CheckItem({
57831                 id: "col-"+cm.getColumnId(i),
57832                 text: cm.getColumnHeader(i),
57833                 checked: !cm.isHidden(i),
57834                 hideOnClick:false
57835             }));
57836         }
57837     },
57838
57839     handleHdCtx : function(g, index, e){
57840         e.stopEvent();
57841         var hd = this.getHeaderCell(index);
57842         this.hdCtxIndex = index;
57843         var ms = this.hmenu.items, cm = this.cm;
57844         ms.get("asc").setDisabled(!cm.isSortable(index));
57845         ms.get("desc").setDisabled(!cm.isSortable(index));
57846         if(this.grid.enableColLock !== false){
57847             ms.get("lock").setDisabled(cm.isLocked(index));
57848             ms.get("unlock").setDisabled(!cm.isLocked(index));
57849         }
57850         this.hmenu.show(hd, "tl-bl");
57851     },
57852
57853     handleHdOver : function(e){
57854         var hd = this.findHeaderCell(e.getTarget());
57855         if(hd && !this.headersDisabled){
57856             if(this.grid.colModel.isSortable(this.getCellIndex(hd))){
57857                this.fly(hd).addClass("x-grid-hd-over");
57858             }
57859         }
57860     },
57861
57862     handleHdOut : function(e){
57863         var hd = this.findHeaderCell(e.getTarget());
57864         if(hd){
57865             this.fly(hd).removeClass("x-grid-hd-over");
57866         }
57867     },
57868
57869     handleSplitDblClick : function(e, t){
57870         var i = this.getCellIndex(t);
57871         if(this.grid.enableColumnResize !== false && this.cm.isResizable(i) && !this.cm.isFixed(i)){
57872             this.autoSizeColumn(i, true);
57873             this.layout();
57874         }
57875     },
57876
57877     render : function(){
57878
57879         var cm = this.cm;
57880         var colCount = cm.getColumnCount();
57881
57882         if(this.grid.monitorWindowResize === true){
57883             Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
57884         }
57885         var header = this.renderHeaders();
57886         var body = this.templates.body.apply({rows:""});
57887         var html = this.templates.master.apply({
57888             lockedBody: body,
57889             body: body,
57890             lockedHeader: header[0],
57891             header: header[1]
57892         });
57893
57894         //this.updateColumns();
57895
57896         this.grid.getGridEl().dom.innerHTML = html;
57897
57898         this.initElements();
57899         
57900         // a kludge to fix the random scolling effect in webkit
57901         this.el.on("scroll", function() {
57902             this.el.dom.scrollTop=0; // hopefully not recursive..
57903         },this);
57904
57905         this.scroller.on("scroll", this.handleScroll, this);
57906         this.lockedBody.on("mousewheel", this.handleWheel, this);
57907         this.mainBody.on("mousewheel", this.handleWheel, this);
57908
57909         this.mainHd.on("mouseover", this.handleHdOver, this);
57910         this.mainHd.on("mouseout", this.handleHdOut, this);
57911         this.mainHd.on("dblclick", this.handleSplitDblClick, this,
57912                 {delegate: "."+this.splitClass});
57913
57914         this.lockedHd.on("mouseover", this.handleHdOver, this);
57915         this.lockedHd.on("mouseout", this.handleHdOut, this);
57916         this.lockedHd.on("dblclick", this.handleSplitDblClick, this,
57917                 {delegate: "."+this.splitClass});
57918
57919         if(this.grid.enableColumnResize !== false && Roo.grid.SplitDragZone){
57920             new Roo.grid.SplitDragZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
57921         }
57922
57923         this.updateSplitters();
57924
57925         if(this.grid.enableColumnMove && Roo.grid.HeaderDragZone){
57926             new Roo.grid.HeaderDragZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
57927             new Roo.grid.HeaderDropZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
57928         }
57929
57930         if(this.grid.enableCtxMenu !== false && Roo.menu.Menu){
57931             this.hmenu = new Roo.menu.Menu({id: this.grid.id + "-hctx"});
57932             this.hmenu.add(
57933                 {id:"asc", text: this.sortAscText, cls: "xg-hmenu-sort-asc"},
57934                 {id:"desc", text: this.sortDescText, cls: "xg-hmenu-sort-desc"}
57935             );
57936             if(this.grid.enableColLock !== false){
57937                 this.hmenu.add('-',
57938                     {id:"lock", text: this.lockText, cls: "xg-hmenu-lock"},
57939                     {id:"unlock", text: this.unlockText, cls: "xg-hmenu-unlock"}
57940                 );
57941             }
57942             if (Roo.isTouch) {
57943                  this.hmenu.add('-',
57944                     {id:"wider", text: this.columnsWiderText},
57945                     {id:"narrow", text: this.columnsNarrowText }
57946                 );
57947                 
57948                  
57949             }
57950             
57951             if(this.grid.enableColumnHide !== false){
57952
57953                 this.colMenu = new Roo.menu.Menu({id:this.grid.id + "-hcols-menu"});
57954                 this.colMenu.on("beforeshow", this.beforeColMenuShow, this);
57955                 this.colMenu.on("itemclick", this.handleHdMenuClick, this);
57956
57957                 this.hmenu.add('-',
57958                     {id:"columns", text: this.columnsText, menu: this.colMenu}
57959                 );
57960             }
57961             this.hmenu.on("itemclick", this.handleHdMenuClick, this);
57962
57963             this.grid.on("headercontextmenu", this.handleHdCtx, this);
57964         }
57965
57966         if((this.grid.enableDragDrop || this.grid.enableDrag) && Roo.grid.GridDragZone){
57967             this.dd = new Roo.grid.GridDragZone(this.grid, {
57968                 ddGroup : this.grid.ddGroup || 'GridDD'
57969             });
57970             
57971         }
57972
57973         /*
57974         for(var i = 0; i < colCount; i++){
57975             if(cm.isHidden(i)){
57976                 this.hideColumn(i);
57977             }
57978             if(cm.config[i].align){
57979                 this.css.updateRule(this.colSelector + i, "textAlign", cm.config[i].align);
57980                 this.css.updateRule(this.hdSelector + i, "textAlign", cm.config[i].align);
57981             }
57982         }*/
57983         
57984         this.updateHeaderSortState();
57985
57986         this.beforeInitialResize();
57987         this.layout(true);
57988
57989         // two part rendering gives faster view to the user
57990         this.renderPhase2.defer(1, this);
57991     },
57992
57993     renderPhase2 : function(){
57994         // render the rows now
57995         this.refresh();
57996         if(this.grid.autoSizeColumns){
57997             this.autoSizeColumns();
57998         }
57999     },
58000
58001     beforeInitialResize : function(){
58002
58003     },
58004
58005     onColumnSplitterMoved : function(i, w){
58006         this.userResized = true;
58007         var cm = this.grid.colModel;
58008         cm.setColumnWidth(i, w, true);
58009         var cid = cm.getColumnId(i);
58010         this.css.updateRule(this.colSelector + this.idToCssName(cid), "width", (w-this.borderWidth) + "px");
58011         this.css.updateRule(this.hdSelector + this.idToCssName(cid), "width", (w-this.borderWidth) + "px");
58012         this.updateSplitters();
58013         this.layout();
58014         this.grid.fireEvent("columnresize", i, w);
58015     },
58016
58017     syncRowHeights : function(startIndex, endIndex){
58018         if(this.grid.enableRowHeightSync === true && this.cm.getLockedCount() > 0){
58019             startIndex = startIndex || 0;
58020             var mrows = this.getBodyTable().rows;
58021             var lrows = this.getLockedTable().rows;
58022             var len = mrows.length-1;
58023             endIndex = Math.min(endIndex || len, len);
58024             for(var i = startIndex; i <= endIndex; i++){
58025                 var m = mrows[i], l = lrows[i];
58026                 var h = Math.max(m.offsetHeight, l.offsetHeight);
58027                 m.style.height = l.style.height = h + "px";
58028             }
58029         }
58030     },
58031
58032     layout : function(initialRender, is2ndPass)
58033     {
58034         var g = this.grid;
58035         var auto = g.autoHeight;
58036         var scrollOffset = 16;
58037         var c = g.getGridEl(), cm = this.cm,
58038                 expandCol = g.autoExpandColumn,
58039                 gv = this;
58040         //c.beginMeasure();
58041
58042         if(!c.dom.offsetWidth){ // display:none?
58043             if(initialRender){
58044                 this.lockedWrap.show();
58045                 this.mainWrap.show();
58046             }
58047             return;
58048         }
58049
58050         var hasLock = this.cm.isLocked(0);
58051
58052         var tbh = this.headerPanel.getHeight();
58053         var bbh = this.footerPanel.getHeight();
58054
58055         if(auto){
58056             var ch = this.getBodyTable().offsetHeight + tbh + bbh + this.mainHd.getHeight();
58057             var newHeight = ch + c.getBorderWidth("tb");
58058             if(g.maxHeight){
58059                 newHeight = Math.min(g.maxHeight, newHeight);
58060             }
58061             c.setHeight(newHeight);
58062         }
58063
58064         if(g.autoWidth){
58065             c.setWidth(cm.getTotalWidth()+c.getBorderWidth('lr'));
58066         }
58067
58068         var s = this.scroller;
58069
58070         var csize = c.getSize(true);
58071
58072         this.el.setSize(csize.width, csize.height);
58073
58074         this.headerPanel.setWidth(csize.width);
58075         this.footerPanel.setWidth(csize.width);
58076
58077         var hdHeight = this.mainHd.getHeight();
58078         var vw = csize.width;
58079         var vh = csize.height - (tbh + bbh);
58080
58081         s.setSize(vw, vh);
58082
58083         var bt = this.getBodyTable();
58084         
58085         if(cm.getLockedCount() == cm.config.length){
58086             bt = this.getLockedTable();
58087         }
58088         
58089         var ltWidth = hasLock ?
58090                       Math.max(this.getLockedTable().offsetWidth, this.lockedHd.dom.firstChild.offsetWidth) : 0;
58091
58092         var scrollHeight = bt.offsetHeight;
58093         var scrollWidth = ltWidth + bt.offsetWidth;
58094         var vscroll = false, hscroll = false;
58095
58096         this.scrollSizer.setSize(scrollWidth, scrollHeight+hdHeight);
58097
58098         var lw = this.lockedWrap, mw = this.mainWrap;
58099         var lb = this.lockedBody, mb = this.mainBody;
58100
58101         setTimeout(function(){
58102             var t = s.dom.offsetTop;
58103             var w = s.dom.clientWidth,
58104                 h = s.dom.clientHeight;
58105
58106             lw.setTop(t);
58107             lw.setSize(ltWidth, h);
58108
58109             mw.setLeftTop(ltWidth, t);
58110             mw.setSize(w-ltWidth, h);
58111
58112             lb.setHeight(h-hdHeight);
58113             mb.setHeight(h-hdHeight);
58114
58115             if(is2ndPass !== true && !gv.userResized && expandCol){
58116                 // high speed resize without full column calculation
58117                 
58118                 var ci = cm.getIndexById(expandCol);
58119                 if (ci < 0) {
58120                     ci = cm.findColumnIndex(expandCol);
58121                 }
58122                 ci = Math.max(0, ci); // make sure it's got at least the first col.
58123                 var expandId = cm.getColumnId(ci);
58124                 var  tw = cm.getTotalWidth(false);
58125                 var currentWidth = cm.getColumnWidth(ci);
58126                 var cw = Math.min(Math.max(((w-tw)+currentWidth-2)-/*scrollbar*/(w <= s.dom.offsetWidth ? 0 : 18), g.autoExpandMin), g.autoExpandMax);
58127                 if(currentWidth != cw){
58128                     cm.setColumnWidth(ci, cw, true);
58129                     gv.css.updateRule(gv.colSelector+gv.idToCssName(expandId), "width", (cw - gv.borderWidth) + "px");
58130                     gv.css.updateRule(gv.hdSelector+gv.idToCssName(expandId), "width", (cw - gv.borderWidth) + "px");
58131                     gv.updateSplitters();
58132                     gv.layout(false, true);
58133                 }
58134             }
58135
58136             if(initialRender){
58137                 lw.show();
58138                 mw.show();
58139             }
58140             //c.endMeasure();
58141         }, 10);
58142     },
58143
58144     onWindowResize : function(){
58145         if(!this.grid.monitorWindowResize || this.grid.autoHeight){
58146             return;
58147         }
58148         this.layout();
58149     },
58150
58151     appendFooter : function(parentEl){
58152         return null;
58153     },
58154
58155     sortAscText : "Sort Ascending",
58156     sortDescText : "Sort Descending",
58157     lockText : "Lock Column",
58158     unlockText : "Unlock Column",
58159     columnsText : "Columns",
58160  
58161     columnsWiderText : "Wider",
58162     columnsNarrowText : "Thinner"
58163 });
58164
58165
58166 Roo.grid.GridView.ColumnDragZone = function(grid, hd){
58167     Roo.grid.GridView.ColumnDragZone.superclass.constructor.call(this, grid, hd, null);
58168     this.proxy.el.addClass('x-grid3-col-dd');
58169 };
58170
58171 Roo.extend(Roo.grid.GridView.ColumnDragZone, Roo.grid.HeaderDragZone, {
58172     handleMouseDown : function(e){
58173
58174     },
58175
58176     callHandleMouseDown : function(e){
58177         Roo.grid.GridView.ColumnDragZone.superclass.handleMouseDown.call(this, e);
58178     }
58179 });
58180 /*
58181  * Based on:
58182  * Ext JS Library 1.1.1
58183  * Copyright(c) 2006-2007, Ext JS, LLC.
58184  *
58185  * Originally Released Under LGPL - original licence link has changed is not relivant.
58186  *
58187  * Fork - LGPL
58188  * <script type="text/javascript">
58189  */
58190  /**
58191  * @extends Roo.dd.DDProxy
58192  * @class Roo.grid.SplitDragZone
58193  * Support for Column Header resizing
58194  * @constructor
58195  * @param {Object} config
58196  */
58197 // private
58198 // This is a support class used internally by the Grid components
58199 Roo.grid.SplitDragZone = function(grid, hd, hd2){
58200     this.grid = grid;
58201     this.view = grid.getView();
58202     this.proxy = this.view.resizeProxy;
58203     Roo.grid.SplitDragZone.superclass.constructor.call(
58204         this,
58205         hd, // ID
58206         "gridSplitters" + this.grid.getGridEl().id, // SGROUP
58207         {  // CONFIG
58208             dragElId : Roo.id(this.proxy.dom),
58209             resizeFrame:false
58210         }
58211     );
58212     
58213     this.setHandleElId(Roo.id(hd));
58214     if (hd2 !== false) {
58215         this.setOuterHandleElId(Roo.id(hd2));
58216     }
58217     
58218     this.scroll = false;
58219 };
58220 Roo.extend(Roo.grid.SplitDragZone, Roo.dd.DDProxy, {
58221     fly: Roo.Element.fly,
58222
58223     b4StartDrag : function(x, y){
58224         this.view.headersDisabled = true;
58225         var h = this.view.mainWrap ? this.view.mainWrap.getHeight() : (
58226                     this.view.headEl.getHeight() + this.view.bodyEl.getHeight()
58227         );
58228         this.proxy.setHeight(h);
58229         
58230         // for old system colWidth really stored the actual width?
58231         // in bootstrap we tried using xs/ms/etc.. to do % sizing?
58232         // which in reality did not work.. - it worked only for fixed sizes
58233         // for resizable we need to use actual sizes.
58234         var w = this.cm.getColumnWidth(this.cellIndex);
58235         if (!this.view.mainWrap) {
58236             // bootstrap.
58237             w = this.view.getHeaderIndex(this.cellIndex).getWidth();
58238         }
58239         
58240         
58241         
58242         // this was w-this.grid.minColumnWidth;
58243         // doesnt really make sense? - w = thie curren width or the rendered one?
58244         var minw = Math.max(w-this.grid.minColumnWidth, 0);
58245         this.resetConstraints();
58246         this.setXConstraint(minw, 1000);
58247         this.setYConstraint(0, 0);
58248         this.minX = x - minw;
58249         this.maxX = x + 1000;
58250         this.startPos = x;
58251         if (!this.view.mainWrap) { // this is Bootstrap code..
58252             this.getDragEl().style.display='block';
58253         }
58254         
58255         Roo.dd.DDProxy.prototype.b4StartDrag.call(this, x, y);
58256     },
58257
58258
58259     handleMouseDown : function(e){
58260         ev = Roo.EventObject.setEvent(e);
58261         var t = this.fly(ev.getTarget());
58262         if(t.hasClass("x-grid-split")){
58263             this.cellIndex = this.view.getCellIndex(t.dom);
58264             this.split = t.dom;
58265             this.cm = this.grid.colModel;
58266             if(this.cm.isResizable(this.cellIndex) && !this.cm.isFixed(this.cellIndex)){
58267                 Roo.grid.SplitDragZone.superclass.handleMouseDown.apply(this, arguments);
58268             }
58269         }
58270     },
58271
58272     endDrag : function(e){
58273         this.view.headersDisabled = false;
58274         var endX = Math.max(this.minX, Roo.lib.Event.getPageX(e));
58275         var diff = endX - this.startPos;
58276         // 
58277         var w = this.cm.getColumnWidth(this.cellIndex);
58278         if (!this.view.mainWrap) {
58279             w = 0;
58280         }
58281         this.view.onColumnSplitterMoved(this.cellIndex, w+diff);
58282     },
58283
58284     autoOffset : function(){
58285         this.setDelta(0,0);
58286     }
58287 });/*
58288  * Based on:
58289  * Ext JS Library 1.1.1
58290  * Copyright(c) 2006-2007, Ext JS, LLC.
58291  *
58292  * Originally Released Under LGPL - original licence link has changed is not relivant.
58293  *
58294  * Fork - LGPL
58295  * <script type="text/javascript">
58296  */
58297  
58298 // private
58299 // This is a support class used internally by the Grid components
58300 Roo.grid.GridDragZone = function(grid, config){
58301     this.view = grid.getView();
58302     Roo.grid.GridDragZone.superclass.constructor.call(this, this.view.mainBody.dom, config);
58303     if(this.view.lockedBody){
58304         this.setHandleElId(Roo.id(this.view.mainBody.dom));
58305         this.setOuterHandleElId(Roo.id(this.view.lockedBody.dom));
58306     }
58307     this.scroll = false;
58308     this.grid = grid;
58309     this.ddel = document.createElement('div');
58310     this.ddel.className = 'x-grid-dd-wrap';
58311 };
58312
58313 Roo.extend(Roo.grid.GridDragZone, Roo.dd.DragZone, {
58314     ddGroup : "GridDD",
58315
58316     getDragData : function(e){
58317         var t = Roo.lib.Event.getTarget(e);
58318         var rowIndex = this.view.findRowIndex(t);
58319         var sm = this.grid.selModel;
58320             
58321         //Roo.log(rowIndex);
58322         
58323         if (sm.getSelectedCell) {
58324             // cell selection..
58325             if (!sm.getSelectedCell()) {
58326                 return false;
58327             }
58328             if (rowIndex != sm.getSelectedCell()[0]) {
58329                 return false;
58330             }
58331         
58332         }
58333         if (sm.getSelections && sm.getSelections().length < 1) {
58334             return false;
58335         }
58336         
58337         
58338         // before it used to all dragging of unseleted... - now we dont do that.
58339         if(rowIndex !== false){
58340             
58341             // if editorgrid.. 
58342             
58343             
58344             //Roo.log([ sm.getSelectedCell() ? sm.getSelectedCell()[0] : 'NO' , rowIndex ]);
58345                
58346             //if(!sm.isSelected(rowIndex) || e.hasModifier()){
58347               //  
58348             //}
58349             if (e.hasModifier()){
58350                 sm.handleMouseDown(e, t); // non modifier buttons are handled by row select.
58351             }
58352             
58353             Roo.log("getDragData");
58354             
58355             return {
58356                 grid: this.grid,
58357                 ddel: this.ddel,
58358                 rowIndex: rowIndex,
58359                 selections: sm.getSelections ? sm.getSelections() : (
58360                     sm.getSelectedCell() ? [ this.grid.ds.getAt(sm.getSelectedCell()[0]) ] : [])
58361             };
58362         }
58363         return false;
58364     },
58365     
58366     
58367     onInitDrag : function(e){
58368         var data = this.dragData;
58369         this.ddel.innerHTML = this.grid.getDragDropText();
58370         this.proxy.update(this.ddel);
58371         // fire start drag?
58372     },
58373
58374     afterRepair : function(){
58375         this.dragging = false;
58376     },
58377
58378     getRepairXY : function(e, data){
58379         return false;
58380     },
58381
58382     onEndDrag : function(data, e){
58383         // fire end drag?
58384     },
58385
58386     onValidDrop : function(dd, e, id){
58387         // fire drag drop?
58388         this.hideProxy();
58389     },
58390
58391     beforeInvalidDrop : function(e, id){
58392
58393     }
58394 });/*
58395  * Based on:
58396  * Ext JS Library 1.1.1
58397  * Copyright(c) 2006-2007, Ext JS, LLC.
58398  *
58399  * Originally Released Under LGPL - original licence link has changed is not relivant.
58400  *
58401  * Fork - LGPL
58402  * <script type="text/javascript">
58403  */
58404  
58405
58406 /**
58407  * @class Roo.grid.ColumnModel
58408  * @extends Roo.util.Observable
58409  * This is the default implementation of a ColumnModel used by the Grid. It defines
58410  * the columns in the grid.
58411  * <br>Usage:<br>
58412  <pre><code>
58413  var colModel = new Roo.grid.ColumnModel([
58414         {header: "Ticker", width: 60, sortable: true, locked: true},
58415         {header: "Company Name", width: 150, sortable: true},
58416         {header: "Market Cap.", width: 100, sortable: true},
58417         {header: "$ Sales", width: 100, sortable: true, renderer: money},
58418         {header: "Employees", width: 100, sortable: true, resizable: false}
58419  ]);
58420  </code></pre>
58421  * <p>
58422  
58423  * The config options listed for this class are options which may appear in each
58424  * individual column definition.
58425  * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
58426  * @constructor
58427  * @param {Object} config An Array of column config objects. See this class's
58428  * config objects for details.
58429 */
58430 Roo.grid.ColumnModel = function(config){
58431         /**
58432      * The config passed into the constructor
58433      */
58434     this.config = []; //config;
58435     this.lookup = {};
58436
58437     // if no id, create one
58438     // if the column does not have a dataIndex mapping,
58439     // map it to the order it is in the config
58440     for(var i = 0, len = config.length; i < len; i++){
58441         this.addColumn(config[i]);
58442         
58443     }
58444
58445     /**
58446      * The width of columns which have no width specified (defaults to 100)
58447      * @type Number
58448      */
58449     this.defaultWidth = 100;
58450
58451     /**
58452      * Default sortable of columns which have no sortable specified (defaults to false)
58453      * @type Boolean
58454      */
58455     this.defaultSortable = false;
58456
58457     this.addEvents({
58458         /**
58459              * @event widthchange
58460              * Fires when the width of a column changes.
58461              * @param {ColumnModel} this
58462              * @param {Number} columnIndex The column index
58463              * @param {Number} newWidth The new width
58464              */
58465             "widthchange": true,
58466         /**
58467              * @event headerchange
58468              * Fires when the text of a header changes.
58469              * @param {ColumnModel} this
58470              * @param {Number} columnIndex The column index
58471              * @param {Number} newText The new header text
58472              */
58473             "headerchange": true,
58474         /**
58475              * @event hiddenchange
58476              * Fires when a column is hidden or "unhidden".
58477              * @param {ColumnModel} this
58478              * @param {Number} columnIndex The column index
58479              * @param {Boolean} hidden true if hidden, false otherwise
58480              */
58481             "hiddenchange": true,
58482             /**
58483          * @event columnmoved
58484          * Fires when a column is moved.
58485          * @param {ColumnModel} this
58486          * @param {Number} oldIndex
58487          * @param {Number} newIndex
58488          */
58489         "columnmoved" : true,
58490         /**
58491          * @event columlockchange
58492          * Fires when a column's locked state is changed
58493          * @param {ColumnModel} this
58494          * @param {Number} colIndex
58495          * @param {Boolean} locked true if locked
58496          */
58497         "columnlockchange" : true
58498     });
58499     Roo.grid.ColumnModel.superclass.constructor.call(this);
58500 };
58501 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
58502     /**
58503      * @cfg {String} header The header text to display in the Grid view.
58504      */
58505         /**
58506      * @cfg {String} xsHeader Header at Bootsrap Extra Small width (default for all)
58507      */
58508         /**
58509      * @cfg {String} smHeader Header at Bootsrap Small width
58510      */
58511         /**
58512      * @cfg {String} mdHeader Header at Bootsrap Medium width
58513      */
58514         /**
58515      * @cfg {String} lgHeader Header at Bootsrap Large width
58516      */
58517         /**
58518      * @cfg {String} xlHeader Header at Bootsrap extra Large width
58519      */
58520     /**
58521      * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
58522      * {@link Roo.data.Record} definition from which to draw the column's value. If not
58523      * specified, the column's index is used as an index into the Record's data Array.
58524      */
58525     /**
58526      * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
58527      * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
58528      */
58529     /**
58530      * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
58531      * Defaults to the value of the {@link #defaultSortable} property.
58532      * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
58533      */
58534     /**
58535      * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid.  Defaults to false.
58536      */
58537     /**
58538      * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed.  Defaults to false.
58539      */
58540     /**
58541      * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
58542      */
58543     /**
58544      * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
58545      */
58546     /**
58547      * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
58548      * given the cell's data value. See {@link #setRenderer}. If not specified, the
58549      * default renderer returns the escaped data value. If an object is returned (bootstrap only)
58550      * then it is treated as a Roo Component object instance, and it is rendered after the initial row is rendered
58551      */
58552        /**
58553      * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor 
58554      */
58555     /**
58556      * @cfg {String} align (Optional) Set the CSS text-align property of the column.  Defaults to undefined.
58557      */
58558     /**
58559      * @cfg {String} valign (Optional) Set the CSS vertical-align property of the column (eg. middle, top, bottom etc).  Defaults to undefined.
58560      */
58561     /**
58562      * @cfg {String} cursor (Optional)
58563      */
58564     /**
58565      * @cfg {String} tooltip (Optional)
58566      */
58567     /**
58568      * @cfg {Number} xs (Optional) can be '0' for hidden at this size (number less than 12)
58569      */
58570     /**
58571      * @cfg {Number} sm (Optional) can be '0' for hidden at this size (number less than 12)
58572      */
58573     /**
58574      * @cfg {Number} md (Optional) can be '0' for hidden at this size (number less than 12)
58575      */
58576     /**
58577      * @cfg {Number} lg (Optional) can be '0' for hidden at this size (number less than 12)
58578      */
58579         /**
58580      * @cfg {Number} xl (Optional) can be '0' for hidden at this size (number less than 12)
58581      */
58582     /**
58583      * Returns the id of the column at the specified index.
58584      * @param {Number} index The column index
58585      * @return {String} the id
58586      */
58587     getColumnId : function(index){
58588         return this.config[index].id;
58589     },
58590
58591     /**
58592      * Returns the column for a specified id.
58593      * @param {String} id The column id
58594      * @return {Object} the column
58595      */
58596     getColumnById : function(id){
58597         return this.lookup[id];
58598     },
58599
58600     
58601     /**
58602      * Returns the column Object for a specified dataIndex.
58603      * @param {String} dataIndex The column dataIndex
58604      * @return {Object|Boolean} the column or false if not found
58605      */
58606     getColumnByDataIndex: function(dataIndex){
58607         var index = this.findColumnIndex(dataIndex);
58608         return index > -1 ? this.config[index] : false;
58609     },
58610     
58611     /**
58612      * Returns the index for a specified column id.
58613      * @param {String} id The column id
58614      * @return {Number} the index, or -1 if not found
58615      */
58616     getIndexById : function(id){
58617         for(var i = 0, len = this.config.length; i < len; i++){
58618             if(this.config[i].id == id){
58619                 return i;
58620             }
58621         }
58622         return -1;
58623     },
58624     
58625     /**
58626      * Returns the index for a specified column dataIndex.
58627      * @param {String} dataIndex The column dataIndex
58628      * @return {Number} the index, or -1 if not found
58629      */
58630     
58631     findColumnIndex : function(dataIndex){
58632         for(var i = 0, len = this.config.length; i < len; i++){
58633             if(this.config[i].dataIndex == dataIndex){
58634                 return i;
58635             }
58636         }
58637         return -1;
58638     },
58639     
58640     
58641     moveColumn : function(oldIndex, newIndex){
58642         var c = this.config[oldIndex];
58643         this.config.splice(oldIndex, 1);
58644         this.config.splice(newIndex, 0, c);
58645         this.dataMap = null;
58646         this.fireEvent("columnmoved", this, oldIndex, newIndex);
58647     },
58648
58649     isLocked : function(colIndex){
58650         return this.config[colIndex].locked === true;
58651     },
58652
58653     setLocked : function(colIndex, value, suppressEvent){
58654         if(this.isLocked(colIndex) == value){
58655             return;
58656         }
58657         this.config[colIndex].locked = value;
58658         if(!suppressEvent){
58659             this.fireEvent("columnlockchange", this, colIndex, value);
58660         }
58661     },
58662
58663     getTotalLockedWidth : function(){
58664         var totalWidth = 0;
58665         for(var i = 0; i < this.config.length; i++){
58666             if(this.isLocked(i) && !this.isHidden(i)){
58667                 this.totalWidth += this.getColumnWidth(i);
58668             }
58669         }
58670         return totalWidth;
58671     },
58672
58673     getLockedCount : function(){
58674         for(var i = 0, len = this.config.length; i < len; i++){
58675             if(!this.isLocked(i)){
58676                 return i;
58677             }
58678         }
58679         
58680         return this.config.length;
58681     },
58682
58683     /**
58684      * Returns the number of columns.
58685      * @return {Number}
58686      */
58687     getColumnCount : function(visibleOnly){
58688         if(visibleOnly === true){
58689             var c = 0;
58690             for(var i = 0, len = this.config.length; i < len; i++){
58691                 if(!this.isHidden(i)){
58692                     c++;
58693                 }
58694             }
58695             return c;
58696         }
58697         return this.config.length;
58698     },
58699
58700     /**
58701      * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
58702      * @param {Function} fn
58703      * @param {Object} scope (optional)
58704      * @return {Array} result
58705      */
58706     getColumnsBy : function(fn, scope){
58707         var r = [];
58708         for(var i = 0, len = this.config.length; i < len; i++){
58709             var c = this.config[i];
58710             if(fn.call(scope||this, c, i) === true){
58711                 r[r.length] = c;
58712             }
58713         }
58714         return r;
58715     },
58716
58717     /**
58718      * Returns true if the specified column is sortable.
58719      * @param {Number} col The column index
58720      * @return {Boolean}
58721      */
58722     isSortable : function(col){
58723         if(typeof this.config[col].sortable == "undefined"){
58724             return this.defaultSortable;
58725         }
58726         return this.config[col].sortable;
58727     },
58728
58729     /**
58730      * Returns the rendering (formatting) function defined for the column.
58731      * @param {Number} col The column index.
58732      * @return {Function} The function used to render the cell. See {@link #setRenderer}.
58733      */
58734     getRenderer : function(col){
58735         if(!this.config[col].renderer){
58736             return Roo.grid.ColumnModel.defaultRenderer;
58737         }
58738         return this.config[col].renderer;
58739     },
58740
58741     /**
58742      * Sets the rendering (formatting) function for a column.
58743      * @param {Number} col The column index
58744      * @param {Function} fn The function to use to process the cell's raw data
58745      * to return HTML markup for the grid view. The render function is called with
58746      * the following parameters:<ul>
58747      * <li>Data value.</li>
58748      * <li>Cell metadata. An object in which you may set the following attributes:<ul>
58749      * <li>css A CSS style string to apply to the table cell.</li>
58750      * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
58751      * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
58752      * <li>Row index</li>
58753      * <li>Column index</li>
58754      * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
58755      */
58756     setRenderer : function(col, fn){
58757         this.config[col].renderer = fn;
58758     },
58759
58760     /**
58761      * Returns the width for the specified column.
58762      * @param {Number} col The column index
58763      * @param (optional) {String} gridSize bootstrap width size.
58764      * @return {Number}
58765      */
58766     getColumnWidth : function(col, gridSize)
58767         {
58768                 var cfg = this.config[col];
58769                 
58770                 if (typeof(gridSize) == 'undefined') {
58771                         return cfg.width * 1 || this.defaultWidth;
58772                 }
58773                 if (gridSize === false) { // if we set it..
58774                         return cfg.width || false;
58775                 }
58776                 var sizes = ['xl', 'lg', 'md', 'sm', 'xs'];
58777                 
58778                 for(var i = sizes.indexOf(gridSize); i < sizes.length; i++) {
58779                         if (typeof(cfg[ sizes[i] ] ) == 'undefined') {
58780                                 continue;
58781                         }
58782                         return cfg[ sizes[i] ];
58783                 }
58784                 return 1;
58785                 
58786     },
58787
58788     /**
58789      * Sets the width for a column.
58790      * @param {Number} col The column index
58791      * @param {Number} width The new width
58792      */
58793     setColumnWidth : function(col, width, suppressEvent){
58794         this.config[col].width = width;
58795         this.totalWidth = null;
58796         if(!suppressEvent){
58797              this.fireEvent("widthchange", this, col, width);
58798         }
58799     },
58800
58801     /**
58802      * Returns the total width of all columns.
58803      * @param {Boolean} includeHidden True to include hidden column widths
58804      * @return {Number}
58805      */
58806     getTotalWidth : function(includeHidden){
58807         if(!this.totalWidth){
58808             this.totalWidth = 0;
58809             for(var i = 0, len = this.config.length; i < len; i++){
58810                 if(includeHidden || !this.isHidden(i)){
58811                     this.totalWidth += this.getColumnWidth(i);
58812                 }
58813             }
58814         }
58815         return this.totalWidth;
58816     },
58817
58818     /**
58819      * Returns the header for the specified column.
58820      * @param {Number} col The column index
58821      * @return {String}
58822      */
58823     getColumnHeader : function(col){
58824         return this.config[col].header;
58825     },
58826
58827     /**
58828      * Sets the header for a column.
58829      * @param {Number} col The column index
58830      * @param {String} header The new header
58831      */
58832     setColumnHeader : function(col, header){
58833         this.config[col].header = header;
58834         this.fireEvent("headerchange", this, col, header);
58835     },
58836
58837     /**
58838      * Returns the tooltip for the specified column.
58839      * @param {Number} col The column index
58840      * @return {String}
58841      */
58842     getColumnTooltip : function(col){
58843             return this.config[col].tooltip;
58844     },
58845     /**
58846      * Sets the tooltip for a column.
58847      * @param {Number} col The column index
58848      * @param {String} tooltip The new tooltip
58849      */
58850     setColumnTooltip : function(col, tooltip){
58851             this.config[col].tooltip = tooltip;
58852     },
58853
58854     /**
58855      * Returns the dataIndex for the specified column.
58856      * @param {Number} col The column index
58857      * @return {Number}
58858      */
58859     getDataIndex : function(col){
58860         return this.config[col].dataIndex;
58861     },
58862
58863     /**
58864      * Sets the dataIndex for a column.
58865      * @param {Number} col The column index
58866      * @param {Number} dataIndex The new dataIndex
58867      */
58868     setDataIndex : function(col, dataIndex){
58869         this.config[col].dataIndex = dataIndex;
58870     },
58871
58872     
58873     
58874     /**
58875      * Returns true if the cell is editable.
58876      * @param {Number} colIndex The column index
58877      * @param {Number} rowIndex The row index - this is nto actually used..?
58878      * @return {Boolean}
58879      */
58880     isCellEditable : function(colIndex, rowIndex){
58881         return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
58882     },
58883
58884     /**
58885      * Returns the editor defined for the cell/column.
58886      * return false or null to disable editing.
58887      * @param {Number} colIndex The column index
58888      * @param {Number} rowIndex The row index
58889      * @return {Object}
58890      */
58891     getCellEditor : function(colIndex, rowIndex){
58892         return this.config[colIndex].editor;
58893     },
58894
58895     /**
58896      * Sets if a column is editable.
58897      * @param {Number} col The column index
58898      * @param {Boolean} editable True if the column is editable
58899      */
58900     setEditable : function(col, editable){
58901         this.config[col].editable = editable;
58902     },
58903
58904
58905     /**
58906      * Returns true if the column is hidden.
58907      * @param {Number} colIndex The column index
58908      * @return {Boolean}
58909      */
58910     isHidden : function(colIndex){
58911         return this.config[colIndex].hidden;
58912     },
58913
58914
58915     /**
58916      * Returns true if the column width cannot be changed
58917      */
58918     isFixed : function(colIndex){
58919         return this.config[colIndex].fixed;
58920     },
58921
58922     /**
58923      * Returns true if the column can be resized
58924      * @return {Boolean}
58925      */
58926     isResizable : function(colIndex){
58927         return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
58928     },
58929     /**
58930      * Sets if a column is hidden.
58931      * @param {Number} colIndex The column index
58932      * @param {Boolean} hidden True if the column is hidden
58933      */
58934     setHidden : function(colIndex, hidden){
58935         this.config[colIndex].hidden = hidden;
58936         this.totalWidth = null;
58937         this.fireEvent("hiddenchange", this, colIndex, hidden);
58938     },
58939
58940     /**
58941      * Sets the editor for a column.
58942      * @param {Number} col The column index
58943      * @param {Object} editor The editor object
58944      */
58945     setEditor : function(col, editor){
58946         this.config[col].editor = editor;
58947     },
58948     /**
58949      * Add a column (experimental...) - defaults to adding to the end..
58950      * @param {Object} config 
58951     */
58952     addColumn : function(c)
58953     {
58954     
58955         var i = this.config.length;
58956         this.config[i] = c;
58957         
58958         if(typeof c.dataIndex == "undefined"){
58959             c.dataIndex = i;
58960         }
58961         if(typeof c.renderer == "string"){
58962             c.renderer = Roo.util.Format[c.renderer];
58963         }
58964         if(typeof c.id == "undefined"){
58965             c.id = Roo.id();
58966         }
58967         if(c.editor && c.editor.xtype){
58968             c.editor  = Roo.factory(c.editor, Roo.grid);
58969         }
58970         if(c.editor && c.editor.isFormField){
58971             c.editor = new Roo.grid.GridEditor(c.editor);
58972         }
58973         this.lookup[c.id] = c;
58974     }
58975     
58976 });
58977
58978 Roo.grid.ColumnModel.defaultRenderer = function(value)
58979 {
58980     if(typeof value == "object") {
58981         return value;
58982     }
58983         if(typeof value == "string" && value.length < 1){
58984             return "&#160;";
58985         }
58986     
58987         return String.format("{0}", value);
58988 };
58989
58990 // Alias for backwards compatibility
58991 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
58992 /*
58993  * Based on:
58994  * Ext JS Library 1.1.1
58995  * Copyright(c) 2006-2007, Ext JS, LLC.
58996  *
58997  * Originally Released Under LGPL - original licence link has changed is not relivant.
58998  *
58999  * Fork - LGPL
59000  * <script type="text/javascript">
59001  */
59002
59003 /**
59004  * @class Roo.grid.AbstractSelectionModel
59005  * @extends Roo.util.Observable
59006  * @abstract
59007  * Abstract base class for grid SelectionModels.  It provides the interface that should be
59008  * implemented by descendant classes.  This class should not be directly instantiated.
59009  * @constructor
59010  */
59011 Roo.grid.AbstractSelectionModel = function(){
59012     this.locked = false;
59013     Roo.grid.AbstractSelectionModel.superclass.constructor.call(this);
59014 };
59015
59016 Roo.extend(Roo.grid.AbstractSelectionModel, Roo.util.Observable,  {
59017     /** @ignore Called by the grid automatically. Do not call directly. */
59018     init : function(grid){
59019         this.grid = grid;
59020         this.initEvents();
59021     },
59022
59023     /**
59024      * Locks the selections.
59025      */
59026     lock : function(){
59027         this.locked = true;
59028     },
59029
59030     /**
59031      * Unlocks the selections.
59032      */
59033     unlock : function(){
59034         this.locked = false;
59035     },
59036
59037     /**
59038      * Returns true if the selections are locked.
59039      * @return {Boolean}
59040      */
59041     isLocked : function(){
59042         return this.locked;
59043     }
59044 });/*
59045  * Based on:
59046  * Ext JS Library 1.1.1
59047  * Copyright(c) 2006-2007, Ext JS, LLC.
59048  *
59049  * Originally Released Under LGPL - original licence link has changed is not relivant.
59050  *
59051  * Fork - LGPL
59052  * <script type="text/javascript">
59053  */
59054 /**
59055  * @extends Roo.grid.AbstractSelectionModel
59056  * @class Roo.grid.RowSelectionModel
59057  * The default SelectionModel used by {@link Roo.grid.Grid}.
59058  * It supports multiple selections and keyboard selection/navigation. 
59059  * @constructor
59060  * @param {Object} config
59061  */
59062 Roo.grid.RowSelectionModel = function(config){
59063     Roo.apply(this, config);
59064     this.selections = new Roo.util.MixedCollection(false, function(o){
59065         return o.id;
59066     });
59067
59068     this.last = false;
59069     this.lastActive = false;
59070
59071     this.addEvents({
59072         /**
59073         * @event selectionchange
59074         * Fires when the selection changes
59075         * @param {SelectionModel} this
59076         */
59077        "selectionchange" : true,
59078        /**
59079         * @event afterselectionchange
59080         * Fires after the selection changes (eg. by key press or clicking)
59081         * @param {SelectionModel} this
59082         */
59083        "afterselectionchange" : true,
59084        /**
59085         * @event beforerowselect
59086         * Fires when a row is selected being selected, return false to cancel.
59087         * @param {SelectionModel} this
59088         * @param {Number} rowIndex The selected index
59089         * @param {Boolean} keepExisting False if other selections will be cleared
59090         */
59091        "beforerowselect" : true,
59092        /**
59093         * @event rowselect
59094         * Fires when a row is selected.
59095         * @param {SelectionModel} this
59096         * @param {Number} rowIndex The selected index
59097         * @param {Roo.data.Record} r The record
59098         */
59099        "rowselect" : true,
59100        /**
59101         * @event rowdeselect
59102         * Fires when a row is deselected.
59103         * @param {SelectionModel} this
59104         * @param {Number} rowIndex The selected index
59105         */
59106         "rowdeselect" : true
59107     });
59108     Roo.grid.RowSelectionModel.superclass.constructor.call(this);
59109     this.locked = false;
59110 };
59111
59112 Roo.extend(Roo.grid.RowSelectionModel, Roo.grid.AbstractSelectionModel,  {
59113     /**
59114      * @cfg {Boolean} singleSelect
59115      * True to allow selection of only one row at a time (defaults to false)
59116      */
59117     singleSelect : false,
59118
59119     // private
59120     initEvents : function(){
59121
59122         if(!this.grid.enableDragDrop && !this.grid.enableDrag){
59123             this.grid.on("mousedown", this.handleMouseDown, this);
59124         }else{ // allow click to work like normal
59125             this.grid.on("rowclick", this.handleDragableRowClick, this);
59126         }
59127         // bootstrap does not have a view..
59128         var view = this.grid.view ? this.grid.view : this.grid;
59129         this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
59130             "up" : function(e){
59131                 if(!e.shiftKey){
59132                     this.selectPrevious(e.shiftKey);
59133                 }else if(this.last !== false && this.lastActive !== false){
59134                     var last = this.last;
59135                     this.selectRange(this.last,  this.lastActive-1);
59136                     view.focusRow(this.lastActive);
59137                     if(last !== false){
59138                         this.last = last;
59139                     }
59140                 }else{
59141                     this.selectFirstRow();
59142                 }
59143                 this.fireEvent("afterselectionchange", this);
59144             },
59145             "down" : function(e){
59146                 if(!e.shiftKey){
59147                     this.selectNext(e.shiftKey);
59148                 }else if(this.last !== false && this.lastActive !== false){
59149                     var last = this.last;
59150                     this.selectRange(this.last,  this.lastActive+1);
59151                     view.focusRow(this.lastActive);
59152                     if(last !== false){
59153                         this.last = last;
59154                     }
59155                 }else{
59156                     this.selectFirstRow();
59157                 }
59158                 this.fireEvent("afterselectionchange", this);
59159             },
59160             scope: this
59161         });
59162
59163          
59164         view.on("refresh", this.onRefresh, this);
59165         view.on("rowupdated", this.onRowUpdated, this);
59166         view.on("rowremoved", this.onRemove, this);
59167     },
59168
59169     // private
59170     onRefresh : function(){
59171         var ds = this.grid.ds, i, v = this.grid.view;
59172         var s = this.selections;
59173         s.each(function(r){
59174             if((i = ds.indexOfId(r.id)) != -1){
59175                 v.onRowSelect(i);
59176                 s.add(ds.getAt(i)); // updating the selection relate data
59177             }else{
59178                 s.remove(r);
59179             }
59180         });
59181     },
59182
59183     // private
59184     onRemove : function(v, index, r){
59185         this.selections.remove(r);
59186     },
59187
59188     // private
59189     onRowUpdated : function(v, index, r){
59190         if(this.isSelected(r)){
59191             v.onRowSelect(index);
59192         }
59193     },
59194
59195     /**
59196      * Select records.
59197      * @param {Array} records The records to select
59198      * @param {Boolean} keepExisting (optional) True to keep existing selections
59199      */
59200     selectRecords : function(records, keepExisting){
59201         if(!keepExisting){
59202             this.clearSelections();
59203         }
59204         var ds = this.grid.ds;
59205         for(var i = 0, len = records.length; i < len; i++){
59206             this.selectRow(ds.indexOf(records[i]), true);
59207         }
59208     },
59209
59210     /**
59211      * Gets the number of selected rows.
59212      * @return {Number}
59213      */
59214     getCount : function(){
59215         return this.selections.length;
59216     },
59217
59218     /**
59219      * Selects the first row in the grid.
59220      */
59221     selectFirstRow : function(){
59222         this.selectRow(0);
59223     },
59224
59225     /**
59226      * Select the last row.
59227      * @param {Boolean} keepExisting (optional) True to keep existing selections
59228      */
59229     selectLastRow : function(keepExisting){
59230         this.selectRow(this.grid.ds.getCount() - 1, keepExisting);
59231     },
59232
59233     /**
59234      * Selects the row immediately following the last selected row.
59235      * @param {Boolean} keepExisting (optional) True to keep existing selections
59236      */
59237     selectNext : function(keepExisting){
59238         if(this.last !== false && (this.last+1) < this.grid.ds.getCount()){
59239             this.selectRow(this.last+1, keepExisting);
59240             var view = this.grid.view ? this.grid.view : this.grid;
59241             view.focusRow(this.last);
59242         }
59243     },
59244
59245     /**
59246      * Selects the row that precedes the last selected row.
59247      * @param {Boolean} keepExisting (optional) True to keep existing selections
59248      */
59249     selectPrevious : function(keepExisting){
59250         if(this.last){
59251             this.selectRow(this.last-1, keepExisting);
59252             var view = this.grid.view ? this.grid.view : this.grid;
59253             view.focusRow(this.last);
59254         }
59255     },
59256
59257     /**
59258      * Returns the selected records
59259      * @return {Array} Array of selected records
59260      */
59261     getSelections : function(){
59262         return [].concat(this.selections.items);
59263     },
59264
59265     /**
59266      * Returns the first selected record.
59267      * @return {Record}
59268      */
59269     getSelected : function(){
59270         return this.selections.itemAt(0);
59271     },
59272
59273
59274     /**
59275      * Clears all selections.
59276      */
59277     clearSelections : function(fast){
59278         if(this.locked) {
59279             return;
59280         }
59281         if(fast !== true){
59282             var ds = this.grid.ds;
59283             var s = this.selections;
59284             s.each(function(r){
59285                 this.deselectRow(ds.indexOfId(r.id));
59286             }, this);
59287             s.clear();
59288         }else{
59289             this.selections.clear();
59290         }
59291         this.last = false;
59292     },
59293
59294
59295     /**
59296      * Selects all rows.
59297      */
59298     selectAll : function(){
59299         if(this.locked) {
59300             return;
59301         }
59302         this.selections.clear();
59303         for(var i = 0, len = this.grid.ds.getCount(); i < len; i++){
59304             this.selectRow(i, true);
59305         }
59306     },
59307
59308     /**
59309      * Returns True if there is a selection.
59310      * @return {Boolean}
59311      */
59312     hasSelection : function(){
59313         return this.selections.length > 0;
59314     },
59315
59316     /**
59317      * Returns True if the specified row is selected.
59318      * @param {Number/Record} record The record or index of the record to check
59319      * @return {Boolean}
59320      */
59321     isSelected : function(index){
59322         var r = typeof index == "number" ? this.grid.ds.getAt(index) : index;
59323         return (r && this.selections.key(r.id) ? true : false);
59324     },
59325
59326     /**
59327      * Returns True if the specified record id is selected.
59328      * @param {String} id The id of record to check
59329      * @return {Boolean}
59330      */
59331     isIdSelected : function(id){
59332         return (this.selections.key(id) ? true : false);
59333     },
59334
59335     // private
59336     handleMouseDown : function(e, t)
59337     {
59338         var view = this.grid.view ? this.grid.view : this.grid;
59339         var rowIndex;
59340         if(this.isLocked() || (rowIndex = view.findRowIndex(t)) === false){
59341             return;
59342         };
59343         if(e.shiftKey && this.last !== false){
59344             var last = this.last;
59345             this.selectRange(last, rowIndex, e.ctrlKey);
59346             this.last = last; // reset the last
59347             view.focusRow(rowIndex);
59348         }else{
59349             var isSelected = this.isSelected(rowIndex);
59350             if(e.button !== 0 && isSelected){
59351                 view.focusRow(rowIndex);
59352             }else if(e.ctrlKey && isSelected){
59353                 this.deselectRow(rowIndex);
59354             }else if(!isSelected){
59355                 this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
59356                 view.focusRow(rowIndex);
59357             }
59358         }
59359         this.fireEvent("afterselectionchange", this);
59360     },
59361     // private
59362     handleDragableRowClick :  function(grid, rowIndex, e) 
59363     {
59364         if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
59365             this.selectRow(rowIndex, false);
59366             var view = this.grid.view ? this.grid.view : this.grid;
59367             view.focusRow(rowIndex);
59368              this.fireEvent("afterselectionchange", this);
59369         }
59370     },
59371     
59372     /**
59373      * Selects multiple rows.
59374      * @param {Array} rows Array of the indexes of the row to select
59375      * @param {Boolean} keepExisting (optional) True to keep existing selections
59376      */
59377     selectRows : function(rows, keepExisting){
59378         if(!keepExisting){
59379             this.clearSelections();
59380         }
59381         for(var i = 0, len = rows.length; i < len; i++){
59382             this.selectRow(rows[i], true);
59383         }
59384     },
59385
59386     /**
59387      * Selects a range of rows. All rows in between startRow and endRow are also selected.
59388      * @param {Number} startRow The index of the first row in the range
59389      * @param {Number} endRow The index of the last row in the range
59390      * @param {Boolean} keepExisting (optional) True to retain existing selections
59391      */
59392     selectRange : function(startRow, endRow, keepExisting){
59393         if(this.locked) {
59394             return;
59395         }
59396         if(!keepExisting){
59397             this.clearSelections();
59398         }
59399         if(startRow <= endRow){
59400             for(var i = startRow; i <= endRow; i++){
59401                 this.selectRow(i, true);
59402             }
59403         }else{
59404             for(var i = startRow; i >= endRow; i--){
59405                 this.selectRow(i, true);
59406             }
59407         }
59408     },
59409
59410     /**
59411      * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
59412      * @param {Number} startRow The index of the first row in the range
59413      * @param {Number} endRow The index of the last row in the range
59414      */
59415     deselectRange : function(startRow, endRow, preventViewNotify){
59416         if(this.locked) {
59417             return;
59418         }
59419         for(var i = startRow; i <= endRow; i++){
59420             this.deselectRow(i, preventViewNotify);
59421         }
59422     },
59423
59424     /**
59425      * Selects a row.
59426      * @param {Number} row The index of the row to select
59427      * @param {Boolean} keepExisting (optional) True to keep existing selections
59428      */
59429     selectRow : function(index, keepExisting, preventViewNotify){
59430         if(this.locked || (index < 0 || index >= this.grid.ds.getCount())) {
59431             return;
59432         }
59433         if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
59434             if(!keepExisting || this.singleSelect){
59435                 this.clearSelections();
59436             }
59437             var r = this.grid.ds.getAt(index);
59438             this.selections.add(r);
59439             this.last = this.lastActive = index;
59440             if(!preventViewNotify){
59441                 var view = this.grid.view ? this.grid.view : this.grid;
59442                 view.onRowSelect(index);
59443             }
59444             this.fireEvent("rowselect", this, index, r);
59445             this.fireEvent("selectionchange", this);
59446         }
59447     },
59448
59449     /**
59450      * Deselects a row.
59451      * @param {Number} row The index of the row to deselect
59452      */
59453     deselectRow : function(index, preventViewNotify){
59454         if(this.locked) {
59455             return;
59456         }
59457         if(this.last == index){
59458             this.last = false;
59459         }
59460         if(this.lastActive == index){
59461             this.lastActive = false;
59462         }
59463         var r = this.grid.ds.getAt(index);
59464         this.selections.remove(r);
59465         if(!preventViewNotify){
59466             var view = this.grid.view ? this.grid.view : this.grid;
59467             view.onRowDeselect(index);
59468         }
59469         this.fireEvent("rowdeselect", this, index);
59470         this.fireEvent("selectionchange", this);
59471     },
59472
59473     // private
59474     restoreLast : function(){
59475         if(this._last){
59476             this.last = this._last;
59477         }
59478     },
59479
59480     // private
59481     acceptsNav : function(row, col, cm){
59482         return !cm.isHidden(col) && cm.isCellEditable(col, row);
59483     },
59484
59485     // private
59486     onEditorKey : function(field, e){
59487         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
59488         if(k == e.TAB){
59489             e.stopEvent();
59490             ed.completeEdit();
59491             if(e.shiftKey){
59492                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
59493             }else{
59494                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
59495             }
59496         }else if(k == e.ENTER && !e.ctrlKey){
59497             e.stopEvent();
59498             ed.completeEdit();
59499             if(e.shiftKey){
59500                 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
59501             }else{
59502                 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
59503             }
59504         }else if(k == e.ESC){
59505             ed.cancelEdit();
59506         }
59507         if(newCell){
59508             g.startEditing(newCell[0], newCell[1]);
59509         }
59510     }
59511 });/*
59512  * Based on:
59513  * Ext JS Library 1.1.1
59514  * Copyright(c) 2006-2007, Ext JS, LLC.
59515  *
59516  * Originally Released Under LGPL - original licence link has changed is not relivant.
59517  *
59518  * Fork - LGPL
59519  * <script type="text/javascript">
59520  */
59521 /**
59522  * @class Roo.grid.CellSelectionModel
59523  * @extends Roo.grid.AbstractSelectionModel
59524  * This class provides the basic implementation for cell selection in a grid.
59525  * @constructor
59526  * @param {Object} config The object containing the configuration of this model.
59527  * @cfg {Boolean} enter_is_tab Enter behaves the same as tab. (eg. goes to next cell) default: false
59528  */
59529 Roo.grid.CellSelectionModel = function(config){
59530     Roo.apply(this, config);
59531
59532     this.selection = null;
59533
59534     this.addEvents({
59535         /**
59536              * @event beforerowselect
59537              * Fires before a cell is selected.
59538              * @param {SelectionModel} this
59539              * @param {Number} rowIndex The selected row index
59540              * @param {Number} colIndex The selected cell index
59541              */
59542             "beforecellselect" : true,
59543         /**
59544              * @event cellselect
59545              * Fires when a cell is selected.
59546              * @param {SelectionModel} this
59547              * @param {Number} rowIndex The selected row index
59548              * @param {Number} colIndex The selected cell index
59549              */
59550             "cellselect" : true,
59551         /**
59552              * @event selectionchange
59553              * Fires when the active selection changes.
59554              * @param {SelectionModel} this
59555              * @param {Object} selection null for no selection or an object (o) with two properties
59556                 <ul>
59557                 <li>o.record: the record object for the row the selection is in</li>
59558                 <li>o.cell: An array of [rowIndex, columnIndex]</li>
59559                 </ul>
59560              */
59561             "selectionchange" : true,
59562         /**
59563              * @event tabend
59564              * Fires when the tab (or enter) was pressed on the last editable cell
59565              * You can use this to trigger add new row.
59566              * @param {SelectionModel} this
59567              */
59568             "tabend" : true,
59569          /**
59570              * @event beforeeditnext
59571              * Fires before the next editable sell is made active
59572              * You can use this to skip to another cell or fire the tabend
59573              *    if you set cell to false
59574              * @param {Object} eventdata object : { cell : [ row, col ] } 
59575              */
59576             "beforeeditnext" : true
59577     });
59578     Roo.grid.CellSelectionModel.superclass.constructor.call(this);
59579 };
59580
59581 Roo.extend(Roo.grid.CellSelectionModel, Roo.grid.AbstractSelectionModel,  {
59582     
59583     enter_is_tab: false,
59584
59585     /** @ignore */
59586     initEvents : function(){
59587         this.grid.on("mousedown", this.handleMouseDown, this);
59588         this.grid.getGridEl().on(Roo.isIE ? "keydown" : "keypress", this.handleKeyDown, this);
59589         var view = this.grid.view;
59590         view.on("refresh", this.onViewChange, this);
59591         view.on("rowupdated", this.onRowUpdated, this);
59592         view.on("beforerowremoved", this.clearSelections, this);
59593         view.on("beforerowsinserted", this.clearSelections, this);
59594         if(this.grid.isEditor){
59595             this.grid.on("beforeedit", this.beforeEdit,  this);
59596         }
59597     },
59598
59599         //private
59600     beforeEdit : function(e){
59601         this.select(e.row, e.column, false, true, e.record);
59602     },
59603
59604         //private
59605     onRowUpdated : function(v, index, r){
59606         if(this.selection && this.selection.record == r){
59607             v.onCellSelect(index, this.selection.cell[1]);
59608         }
59609     },
59610
59611         //private
59612     onViewChange : function(){
59613         this.clearSelections(true);
59614     },
59615
59616         /**
59617          * Returns the currently selected cell,.
59618          * @return {Array} The selected cell (row, column) or null if none selected.
59619          */
59620     getSelectedCell : function(){
59621         return this.selection ? this.selection.cell : null;
59622     },
59623
59624     /**
59625      * Clears all selections.
59626      * @param {Boolean} true to prevent the gridview from being notified about the change.
59627      */
59628     clearSelections : function(preventNotify){
59629         var s = this.selection;
59630         if(s){
59631             if(preventNotify !== true){
59632                 this.grid.view.onCellDeselect(s.cell[0], s.cell[1]);
59633             }
59634             this.selection = null;
59635             this.fireEvent("selectionchange", this, null);
59636         }
59637     },
59638
59639     /**
59640      * Returns true if there is a selection.
59641      * @return {Boolean}
59642      */
59643     hasSelection : function(){
59644         return this.selection ? true : false;
59645     },
59646
59647     /** @ignore */
59648     handleMouseDown : function(e, t){
59649         var v = this.grid.getView();
59650         if(this.isLocked()){
59651             return;
59652         };
59653         var row = v.findRowIndex(t);
59654         var cell = v.findCellIndex(t);
59655         if(row !== false && cell !== false){
59656             this.select(row, cell);
59657         }
59658     },
59659
59660     /**
59661      * Selects a cell.
59662      * @param {Number} rowIndex
59663      * @param {Number} collIndex
59664      */
59665     select : function(rowIndex, colIndex, preventViewNotify, preventFocus, /*internal*/ r){
59666         if(this.fireEvent("beforecellselect", this, rowIndex, colIndex) !== false){
59667             this.clearSelections();
59668             r = r || this.grid.dataSource.getAt(rowIndex);
59669             this.selection = {
59670                 record : r,
59671                 cell : [rowIndex, colIndex]
59672             };
59673             if(!preventViewNotify){
59674                 var v = this.grid.getView();
59675                 v.onCellSelect(rowIndex, colIndex);
59676                 if(preventFocus !== true){
59677                     v.focusCell(rowIndex, colIndex);
59678                 }
59679             }
59680             this.fireEvent("cellselect", this, rowIndex, colIndex);
59681             this.fireEvent("selectionchange", this, this.selection);
59682         }
59683     },
59684
59685         //private
59686     isSelectable : function(rowIndex, colIndex, cm){
59687         return !cm.isHidden(colIndex);
59688     },
59689
59690     /** @ignore */
59691     handleKeyDown : function(e){
59692         //Roo.log('Cell Sel Model handleKeyDown');
59693         if(!e.isNavKeyPress()){
59694             return;
59695         }
59696         var g = this.grid, s = this.selection;
59697         if(!s){
59698             e.stopEvent();
59699             var cell = g.walkCells(0, 0, 1, this.isSelectable,  this);
59700             if(cell){
59701                 this.select(cell[0], cell[1]);
59702             }
59703             return;
59704         }
59705         var sm = this;
59706         var walk = function(row, col, step){
59707             return g.walkCells(row, col, step, sm.isSelectable,  sm);
59708         };
59709         var k = e.getKey(), r = s.cell[0], c = s.cell[1];
59710         var newCell;
59711
59712       
59713
59714         switch(k){
59715             case e.TAB:
59716                 // handled by onEditorKey
59717                 if (g.isEditor && g.editing) {
59718                     return;
59719                 }
59720                 if(e.shiftKey) {
59721                     newCell = walk(r, c-1, -1);
59722                 } else {
59723                     newCell = walk(r, c+1, 1);
59724                 }
59725                 break;
59726             
59727             case e.DOWN:
59728                newCell = walk(r+1, c, 1);
59729                 break;
59730             
59731             case e.UP:
59732                 newCell = walk(r-1, c, -1);
59733                 break;
59734             
59735             case e.RIGHT:
59736                 newCell = walk(r, c+1, 1);
59737                 break;
59738             
59739             case e.LEFT:
59740                 newCell = walk(r, c-1, -1);
59741                 break;
59742             
59743             case e.ENTER:
59744                 
59745                 if(g.isEditor && !g.editing){
59746                    g.startEditing(r, c);
59747                    e.stopEvent();
59748                    return;
59749                 }
59750                 
59751                 
59752              break;
59753         };
59754         if(newCell){
59755             this.select(newCell[0], newCell[1]);
59756             e.stopEvent();
59757             
59758         }
59759     },
59760
59761     acceptsNav : function(row, col, cm){
59762         return !cm.isHidden(col) && cm.isCellEditable(col, row);
59763     },
59764     /**
59765      * Selects a cell.
59766      * @param {Number} field (not used) - as it's normally used as a listener
59767      * @param {Number} e - event - fake it by using
59768      *
59769      * var e = Roo.EventObjectImpl.prototype;
59770      * e.keyCode = e.TAB
59771      *
59772      * 
59773      */
59774     onEditorKey : function(field, e){
59775         
59776         var k = e.getKey(),
59777             newCell,
59778             g = this.grid,
59779             ed = g.activeEditor,
59780             forward = false;
59781         ///Roo.log('onEditorKey' + k);
59782         
59783         
59784         if (this.enter_is_tab && k == e.ENTER) {
59785             k = e.TAB;
59786         }
59787         
59788         if(k == e.TAB){
59789             if(e.shiftKey){
59790                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
59791             }else{
59792                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
59793                 forward = true;
59794             }
59795             
59796             e.stopEvent();
59797             
59798         } else if(k == e.ENTER &&  !e.ctrlKey){
59799             ed.completeEdit();
59800             e.stopEvent();
59801             newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
59802         
59803                 } else if(k == e.ESC){
59804             ed.cancelEdit();
59805         }
59806                 
59807         if (newCell) {
59808             var ecall = { cell : newCell, forward : forward };
59809             this.fireEvent('beforeeditnext', ecall );
59810             newCell = ecall.cell;
59811                         forward = ecall.forward;
59812         }
59813                 
59814         if(newCell){
59815             //Roo.log('next cell after edit');
59816             g.startEditing.defer(100, g, [newCell[0], newCell[1]]);
59817         } else if (forward) {
59818             // tabbed past last
59819             this.fireEvent.defer(100, this, ['tabend',this]);
59820         }
59821     }
59822 });/*
59823  * Based on:
59824  * Ext JS Library 1.1.1
59825  * Copyright(c) 2006-2007, Ext JS, LLC.
59826  *
59827  * Originally Released Under LGPL - original licence link has changed is not relivant.
59828  *
59829  * Fork - LGPL
59830  * <script type="text/javascript">
59831  */
59832  
59833 /**
59834  * @class Roo.grid.EditorGrid
59835  * @extends Roo.grid.Grid
59836  * Class for creating and editable grid.
59837  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered - 
59838  * The container MUST have some type of size defined for the grid to fill. The container will be 
59839  * automatically set to position relative if it isn't already.
59840  * @param {Object} dataSource The data model to bind to
59841  * @param {Object} colModel The column model with info about this grid's columns
59842  */
59843 Roo.grid.EditorGrid = function(container, config){
59844     Roo.grid.EditorGrid.superclass.constructor.call(this, container, config);
59845     this.getGridEl().addClass("xedit-grid");
59846
59847     if(!this.selModel){
59848         this.selModel = new Roo.grid.CellSelectionModel();
59849     }
59850
59851     this.activeEditor = null;
59852
59853         this.addEvents({
59854             /**
59855              * @event beforeedit
59856              * Fires before cell editing is triggered. The edit event object has the following properties <br />
59857              * <ul style="padding:5px;padding-left:16px;">
59858              * <li>grid - This grid</li>
59859              * <li>record - The record being edited</li>
59860              * <li>field - The field name being edited</li>
59861              * <li>value - The value for the field being edited.</li>
59862              * <li>row - The grid row index</li>
59863              * <li>column - The grid column index</li>
59864              * <li>cancel - Set this to true to cancel the edit or return false from your handler.</li>
59865              * </ul>
59866              * @param {Object} e An edit event (see above for description)
59867              */
59868             "beforeedit" : true,
59869             /**
59870              * @event afteredit
59871              * Fires after a cell is edited. <br />
59872              * <ul style="padding:5px;padding-left:16px;">
59873              * <li>grid - This grid</li>
59874              * <li>record - The record being edited</li>
59875              * <li>field - The field name being edited</li>
59876              * <li>value - The value being set</li>
59877              * <li>originalValue - The original value for the field, before the edit.</li>
59878              * <li>row - The grid row index</li>
59879              * <li>column - The grid column index</li>
59880              * </ul>
59881              * @param {Object} e An edit event (see above for description)
59882              */
59883             "afteredit" : true,
59884             /**
59885              * @event validateedit
59886              * Fires after a cell is edited, but before the value is set in the record. 
59887          * You can use this to modify the value being set in the field, Return false
59888              * to cancel the change. The edit event object has the following properties <br />
59889              * <ul style="padding:5px;padding-left:16px;">
59890          * <li>editor - This editor</li>
59891              * <li>grid - This grid</li>
59892              * <li>record - The record being edited</li>
59893              * <li>field - The field name being edited</li>
59894              * <li>value - The value being set</li>
59895              * <li>originalValue - The original value for the field, before the edit.</li>
59896              * <li>row - The grid row index</li>
59897              * <li>column - The grid column index</li>
59898              * <li>cancel - Set this to true to cancel the edit or return false from your handler.</li>
59899              * </ul>
59900              * @param {Object} e An edit event (see above for description)
59901              */
59902             "validateedit" : true
59903         });
59904     this.on("bodyscroll", this.stopEditing,  this);
59905     this.on(this.clicksToEdit == 1 ? "cellclick" : "celldblclick", this.onCellDblClick,  this);
59906 };
59907
59908 Roo.extend(Roo.grid.EditorGrid, Roo.grid.Grid, {
59909     /**
59910      * @cfg {Number} clicksToEdit
59911      * The number of clicks on a cell required to display the cell's editor (defaults to 2)
59912      */
59913     clicksToEdit: 2,
59914
59915     // private
59916     isEditor : true,
59917     // private
59918     trackMouseOver: false, // causes very odd FF errors
59919
59920     onCellDblClick : function(g, row, col){
59921         this.startEditing(row, col);
59922     },
59923
59924     onEditComplete : function(ed, value, startValue){
59925         this.editing = false;
59926         this.activeEditor = null;
59927         ed.un("specialkey", this.selModel.onEditorKey, this.selModel);
59928         var r = ed.record;
59929         var field = this.colModel.getDataIndex(ed.col);
59930         var e = {
59931             grid: this,
59932             record: r,
59933             field: field,
59934             originalValue: startValue,
59935             value: value,
59936             row: ed.row,
59937             column: ed.col,
59938             cancel:false,
59939             editor: ed
59940         };
59941         var cell = Roo.get(this.view.getCell(ed.row,ed.col));
59942         cell.show();
59943           
59944         if(String(value) !== String(startValue)){
59945             
59946             if(this.fireEvent("validateedit", e) !== false && !e.cancel){
59947                 r.set(field, e.value);
59948                 // if we are dealing with a combo box..
59949                 // then we also set the 'name' colum to be the displayField
59950                 if (ed.field.displayField && ed.field.name) {
59951                     r.set(ed.field.name, ed.field.el.dom.value);
59952                 }
59953                 
59954                 delete e.cancel; //?? why!!!
59955                 this.fireEvent("afteredit", e);
59956             }
59957         } else {
59958             this.fireEvent("afteredit", e); // always fire it!
59959         }
59960         this.view.focusCell(ed.row, ed.col);
59961     },
59962
59963     /**
59964      * Starts editing the specified for the specified row/column
59965      * @param {Number} rowIndex
59966      * @param {Number} colIndex
59967      */
59968     startEditing : function(row, col){
59969         this.stopEditing();
59970         if(this.colModel.isCellEditable(col, row)){
59971             this.view.ensureVisible(row, col, true);
59972           
59973             var r = this.dataSource.getAt(row);
59974             var field = this.colModel.getDataIndex(col);
59975             var cell = Roo.get(this.view.getCell(row,col));
59976             var e = {
59977                 grid: this,
59978                 record: r,
59979                 field: field,
59980                 value: r.data[field],
59981                 row: row,
59982                 column: col,
59983                 cancel:false 
59984             };
59985             if(this.fireEvent("beforeedit", e) !== false && !e.cancel){
59986                 this.editing = true;
59987                 var ed = this.colModel.getCellEditor(col, row);
59988                 
59989                 if (!ed) {
59990                     return;
59991                 }
59992                 if(!ed.rendered){
59993                     ed.render(ed.parentEl || document.body);
59994                 }
59995                 ed.field.reset();
59996                
59997                 cell.hide();
59998                 
59999                 (function(){ // complex but required for focus issues in safari, ie and opera
60000                     ed.row = row;
60001                     ed.col = col;
60002                     ed.record = r;
60003                     ed.on("complete",   this.onEditComplete,        this,       {single: true});
60004                     ed.on("specialkey", this.selModel.onEditorKey,  this.selModel);
60005                     this.activeEditor = ed;
60006                     var v = r.data[field];
60007                     ed.startEdit(this.view.getCell(row, col), v);
60008                     // combo's with 'displayField and name set
60009                     if (ed.field.displayField && ed.field.name) {
60010                         ed.field.el.dom.value = r.data[ed.field.name];
60011                     }
60012                     
60013                     
60014                 }).defer(50, this);
60015             }
60016         }
60017     },
60018         
60019     /**
60020      * Stops any active editing
60021      */
60022     stopEditing : function(){
60023         if(this.activeEditor){
60024             this.activeEditor.completeEdit();
60025         }
60026         this.activeEditor = null;
60027     },
60028         
60029          /**
60030      * Called to get grid's drag proxy text, by default returns this.ddText.
60031      * @return {String}
60032      */
60033     getDragDropText : function(){
60034         var count = this.selModel.getSelectedCell() ? 1 : 0;
60035         return String.format(this.ddText, count, count == 1 ? '' : 's');
60036     }
60037         
60038 });/*
60039  * Based on:
60040  * Ext JS Library 1.1.1
60041  * Copyright(c) 2006-2007, Ext JS, LLC.
60042  *
60043  * Originally Released Under LGPL - original licence link has changed is not relivant.
60044  *
60045  * Fork - LGPL
60046  * <script type="text/javascript">
60047  */
60048
60049 // private - not really -- you end up using it !
60050 // This is a support class used internally by the Grid components
60051
60052 /**
60053  * @class Roo.grid.GridEditor
60054  * @extends Roo.Editor
60055  * Class for creating and editable grid elements.
60056  * @param {Object} config any settings (must include field)
60057  */
60058 Roo.grid.GridEditor = function(field, config){
60059     if (!config && field.field) {
60060         config = field;
60061         field = Roo.factory(config.field, Roo.form);
60062     }
60063     Roo.grid.GridEditor.superclass.constructor.call(this, field, config);
60064     field.monitorTab = false;
60065 };
60066
60067 Roo.extend(Roo.grid.GridEditor, Roo.Editor, {
60068     
60069     /**
60070      * @cfg {Roo.form.Field} field Field to wrap (or xtyped)
60071      */
60072     
60073     alignment: "tl-tl",
60074     autoSize: "width",
60075     hideEl : false,
60076     cls: "x-small-editor x-grid-editor",
60077     shim:false,
60078     shadow:"frame"
60079 });/*
60080  * Based on:
60081  * Ext JS Library 1.1.1
60082  * Copyright(c) 2006-2007, Ext JS, LLC.
60083  *
60084  * Originally Released Under LGPL - original licence link has changed is not relivant.
60085  *
60086  * Fork - LGPL
60087  * <script type="text/javascript">
60088  */
60089   
60090
60091   
60092 Roo.grid.PropertyRecord = Roo.data.Record.create([
60093     {name:'name',type:'string'},  'value'
60094 ]);
60095
60096
60097 Roo.grid.PropertyStore = function(grid, source){
60098     this.grid = grid;
60099     this.store = new Roo.data.Store({
60100         recordType : Roo.grid.PropertyRecord
60101     });
60102     this.store.on('update', this.onUpdate,  this);
60103     if(source){
60104         this.setSource(source);
60105     }
60106     Roo.grid.PropertyStore.superclass.constructor.call(this);
60107 };
60108
60109
60110
60111 Roo.extend(Roo.grid.PropertyStore, Roo.util.Observable, {
60112     setSource : function(o){
60113         this.source = o;
60114         this.store.removeAll();
60115         var data = [];
60116         for(var k in o){
60117             if(this.isEditableValue(o[k])){
60118                 data.push(new Roo.grid.PropertyRecord({name: k, value: o[k]}, k));
60119             }
60120         }
60121         this.store.loadRecords({records: data}, {}, true);
60122     },
60123
60124     onUpdate : function(ds, record, type){
60125         if(type == Roo.data.Record.EDIT){
60126             var v = record.data['value'];
60127             var oldValue = record.modified['value'];
60128             if(this.grid.fireEvent('beforepropertychange', this.source, record.id, v, oldValue) !== false){
60129                 this.source[record.id] = v;
60130                 record.commit();
60131                 this.grid.fireEvent('propertychange', this.source, record.id, v, oldValue);
60132             }else{
60133                 record.reject();
60134             }
60135         }
60136     },
60137
60138     getProperty : function(row){
60139        return this.store.getAt(row);
60140     },
60141
60142     isEditableValue: function(val){
60143         if(val && val instanceof Date){
60144             return true;
60145         }else if(typeof val == 'object' || typeof val == 'function'){
60146             return false;
60147         }
60148         return true;
60149     },
60150
60151     setValue : function(prop, value){
60152         this.source[prop] = value;
60153         this.store.getById(prop).set('value', value);
60154     },
60155
60156     getSource : function(){
60157         return this.source;
60158     }
60159 });
60160
60161 Roo.grid.PropertyColumnModel = function(grid, store){
60162     this.grid = grid;
60163     var g = Roo.grid;
60164     g.PropertyColumnModel.superclass.constructor.call(this, [
60165         {header: this.nameText, sortable: true, dataIndex:'name', id: 'name'},
60166         {header: this.valueText, resizable:false, dataIndex: 'value', id: 'value'}
60167     ]);
60168     this.store = store;
60169     this.bselect = Roo.DomHelper.append(document.body, {
60170         tag: 'select', style:'display:none', cls: 'x-grid-editor', children: [
60171             {tag: 'option', value: 'true', html: 'true'},
60172             {tag: 'option', value: 'false', html: 'false'}
60173         ]
60174     });
60175     Roo.id(this.bselect);
60176     var f = Roo.form;
60177     this.editors = {
60178         'date' : new g.GridEditor(new f.DateField({selectOnFocus:true})),
60179         'string' : new g.GridEditor(new f.TextField({selectOnFocus:true})),
60180         'number' : new g.GridEditor(new f.NumberField({selectOnFocus:true, style:'text-align:left;'})),
60181         'int' : new g.GridEditor(new f.NumberField({selectOnFocus:true, allowDecimals:false, style:'text-align:left;'})),
60182         'boolean' : new g.GridEditor(new f.Field({el:this.bselect,selectOnFocus:true}))
60183     };
60184     this.renderCellDelegate = this.renderCell.createDelegate(this);
60185     this.renderPropDelegate = this.renderProp.createDelegate(this);
60186 };
60187
60188 Roo.extend(Roo.grid.PropertyColumnModel, Roo.grid.ColumnModel, {
60189     
60190     
60191     nameText : 'Name',
60192     valueText : 'Value',
60193     
60194     dateFormat : 'm/j/Y',
60195     
60196     
60197     renderDate : function(dateVal){
60198         return dateVal.dateFormat(this.dateFormat);
60199     },
60200
60201     renderBool : function(bVal){
60202         return bVal ? 'true' : 'false';
60203     },
60204
60205     isCellEditable : function(colIndex, rowIndex){
60206         return colIndex == 1;
60207     },
60208
60209     getRenderer : function(col){
60210         return col == 1 ?
60211             this.renderCellDelegate : this.renderPropDelegate;
60212     },
60213
60214     renderProp : function(v){
60215         return this.getPropertyName(v);
60216     },
60217
60218     renderCell : function(val){
60219         var rv = val;
60220         if(val instanceof Date){
60221             rv = this.renderDate(val);
60222         }else if(typeof val == 'boolean'){
60223             rv = this.renderBool(val);
60224         }
60225         return Roo.util.Format.htmlEncode(rv);
60226     },
60227
60228     getPropertyName : function(name){
60229         var pn = this.grid.propertyNames;
60230         return pn && pn[name] ? pn[name] : name;
60231     },
60232
60233     getCellEditor : function(colIndex, rowIndex){
60234         var p = this.store.getProperty(rowIndex);
60235         var n = p.data['name'], val = p.data['value'];
60236         
60237         if(typeof(this.grid.customEditors[n]) == 'string'){
60238             return this.editors[this.grid.customEditors[n]];
60239         }
60240         if(typeof(this.grid.customEditors[n]) != 'undefined'){
60241             return this.grid.customEditors[n];
60242         }
60243         if(val instanceof Date){
60244             return this.editors['date'];
60245         }else if(typeof val == 'number'){
60246             return this.editors['number'];
60247         }else if(typeof val == 'boolean'){
60248             return this.editors['boolean'];
60249         }else{
60250             return this.editors['string'];
60251         }
60252     }
60253 });
60254
60255 /**
60256  * @class Roo.grid.PropertyGrid
60257  * @extends Roo.grid.EditorGrid
60258  * This class represents the  interface of a component based property grid control.
60259  * <br><br>Usage:<pre><code>
60260  var grid = new Roo.grid.PropertyGrid("my-container-id", {
60261       
60262  });
60263  // set any options
60264  grid.render();
60265  * </code></pre>
60266   
60267  * @constructor
60268  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
60269  * The container MUST have some type of size defined for the grid to fill. The container will be
60270  * automatically set to position relative if it isn't already.
60271  * @param {Object} config A config object that sets properties on this grid.
60272  */
60273 Roo.grid.PropertyGrid = function(container, config){
60274     config = config || {};
60275     var store = new Roo.grid.PropertyStore(this);
60276     this.store = store;
60277     var cm = new Roo.grid.PropertyColumnModel(this, store);
60278     store.store.sort('name', 'ASC');
60279     Roo.grid.PropertyGrid.superclass.constructor.call(this, container, Roo.apply({
60280         ds: store.store,
60281         cm: cm,
60282         enableColLock:false,
60283         enableColumnMove:false,
60284         stripeRows:false,
60285         trackMouseOver: false,
60286         clicksToEdit:1
60287     }, config));
60288     this.getGridEl().addClass('x-props-grid');
60289     this.lastEditRow = null;
60290     this.on('columnresize', this.onColumnResize, this);
60291     this.addEvents({
60292          /**
60293              * @event beforepropertychange
60294              * Fires before a property changes (return false to stop?)
60295              * @param {Roo.grid.PropertyGrid} grid property grid? (check could be store)
60296              * @param {String} id Record Id
60297              * @param {String} newval New Value
60298          * @param {String} oldval Old Value
60299              */
60300         "beforepropertychange": true,
60301         /**
60302              * @event propertychange
60303              * Fires after a property changes
60304              * @param {Roo.grid.PropertyGrid} grid property grid? (check could be store)
60305              * @param {String} id Record Id
60306              * @param {String} newval New Value
60307          * @param {String} oldval Old Value
60308              */
60309         "propertychange": true
60310     });
60311     this.customEditors = this.customEditors || {};
60312 };
60313 Roo.extend(Roo.grid.PropertyGrid, Roo.grid.EditorGrid, {
60314     
60315      /**
60316      * @cfg {Object} customEditors map of colnames=> custom editors.
60317      * the custom editor can be one of the standard ones (date|string|number|int|boolean), or a
60318      * grid editor eg. Roo.grid.GridEditor(new Roo.form.TextArea({selectOnFocus:true})),
60319      * false disables editing of the field.
60320          */
60321     
60322       /**
60323      * @cfg {Object} propertyNames map of property Names to their displayed value
60324          */
60325     
60326     render : function(){
60327         Roo.grid.PropertyGrid.superclass.render.call(this);
60328         this.autoSize.defer(100, this);
60329     },
60330
60331     autoSize : function(){
60332         Roo.grid.PropertyGrid.superclass.autoSize.call(this);
60333         if(this.view){
60334             this.view.fitColumns();
60335         }
60336     },
60337
60338     onColumnResize : function(){
60339         this.colModel.setColumnWidth(1, this.container.getWidth(true)-this.colModel.getColumnWidth(0));
60340         this.autoSize();
60341     },
60342     /**
60343      * Sets the data for the Grid
60344      * accepts a Key => Value object of all the elements avaiable.
60345      * @param {Object} data  to appear in grid.
60346      */
60347     setSource : function(source){
60348         this.store.setSource(source);
60349         //this.autoSize();
60350     },
60351     /**
60352      * Gets all the data from the grid.
60353      * @return {Object} data  data stored in grid
60354      */
60355     getSource : function(){
60356         return this.store.getSource();
60357     }
60358 });/*
60359   
60360  * Licence LGPL
60361  
60362  */
60363  
60364 /**
60365  * @class Roo.grid.Calendar
60366  * @extends Roo.grid.Grid
60367  * This class extends the Grid to provide a calendar widget
60368  * <br><br>Usage:<pre><code>
60369  var grid = new Roo.grid.Calendar("my-container-id", {
60370      ds: myDataStore,
60371      cm: myColModel,
60372      selModel: mySelectionModel,
60373      autoSizeColumns: true,
60374      monitorWindowResize: false,
60375      trackMouseOver: true
60376      eventstore : real data store..
60377  });
60378  // set any options
60379  grid.render();
60380   
60381   * @constructor
60382  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
60383  * The container MUST have some type of size defined for the grid to fill. The container will be
60384  * automatically set to position relative if it isn't already.
60385  * @param {Object} config A config object that sets properties on this grid.
60386  */
60387 Roo.grid.Calendar = function(container, config){
60388         // initialize the container
60389         this.container = Roo.get(container);
60390         this.container.update("");
60391         this.container.setStyle("overflow", "hidden");
60392     this.container.addClass('x-grid-container');
60393
60394     this.id = this.container.id;
60395
60396     Roo.apply(this, config);
60397     // check and correct shorthanded configs
60398     
60399     var rows = [];
60400     var d =1;
60401     for (var r = 0;r < 6;r++) {
60402         
60403         rows[r]=[];
60404         for (var c =0;c < 7;c++) {
60405             rows[r][c]= '';
60406         }
60407     }
60408     if (this.eventStore) {
60409         this.eventStore= Roo.factory(this.eventStore, Roo.data);
60410         this.eventStore.on('load',this.onLoad, this);
60411         this.eventStore.on('beforeload',this.clearEvents, this);
60412          
60413     }
60414     
60415     this.dataSource = new Roo.data.Store({
60416             proxy: new Roo.data.MemoryProxy(rows),
60417             reader: new Roo.data.ArrayReader({}, [
60418                    'weekday0', 'weekday1', 'weekday2', 'weekday3', 'weekday4', 'weekday5', 'weekday6' ])
60419     });
60420
60421     this.dataSource.load();
60422     this.ds = this.dataSource;
60423     this.ds.xmodule = this.xmodule || false;
60424     
60425     
60426     var cellRender = function(v,x,r)
60427     {
60428         return String.format(
60429             '<div class="fc-day  fc-widget-content"><div>' +
60430                 '<div class="fc-event-container"></div>' +
60431                 '<div class="fc-day-number">{0}</div>'+
60432                 
60433                 '<div class="fc-day-content"><div style="position:relative"></div></div>' +
60434             '</div></div>', v);
60435     
60436     }
60437     
60438     
60439     this.colModel = new Roo.grid.ColumnModel( [
60440         {
60441             xtype: 'ColumnModel',
60442             xns: Roo.grid,
60443             dataIndex : 'weekday0',
60444             header : 'Sunday',
60445             renderer : cellRender
60446         },
60447         {
60448             xtype: 'ColumnModel',
60449             xns: Roo.grid,
60450             dataIndex : 'weekday1',
60451             header : 'Monday',
60452             renderer : cellRender
60453         },
60454         {
60455             xtype: 'ColumnModel',
60456             xns: Roo.grid,
60457             dataIndex : 'weekday2',
60458             header : 'Tuesday',
60459             renderer : cellRender
60460         },
60461         {
60462             xtype: 'ColumnModel',
60463             xns: Roo.grid,
60464             dataIndex : 'weekday3',
60465             header : 'Wednesday',
60466             renderer : cellRender
60467         },
60468         {
60469             xtype: 'ColumnModel',
60470             xns: Roo.grid,
60471             dataIndex : 'weekday4',
60472             header : 'Thursday',
60473             renderer : cellRender
60474         },
60475         {
60476             xtype: 'ColumnModel',
60477             xns: Roo.grid,
60478             dataIndex : 'weekday5',
60479             header : 'Friday',
60480             renderer : cellRender
60481         },
60482         {
60483             xtype: 'ColumnModel',
60484             xns: Roo.grid,
60485             dataIndex : 'weekday6',
60486             header : 'Saturday',
60487             renderer : cellRender
60488         }
60489     ]);
60490     this.cm = this.colModel;
60491     this.cm.xmodule = this.xmodule || false;
60492  
60493         
60494           
60495     //this.selModel = new Roo.grid.CellSelectionModel();
60496     //this.sm = this.selModel;
60497     //this.selModel.init(this);
60498     
60499     
60500     if(this.width){
60501         this.container.setWidth(this.width);
60502     }
60503
60504     if(this.height){
60505         this.container.setHeight(this.height);
60506     }
60507     /** @private */
60508         this.addEvents({
60509         // raw events
60510         /**
60511          * @event click
60512          * The raw click event for the entire grid.
60513          * @param {Roo.EventObject} e
60514          */
60515         "click" : true,
60516         /**
60517          * @event dblclick
60518          * The raw dblclick event for the entire grid.
60519          * @param {Roo.EventObject} e
60520          */
60521         "dblclick" : true,
60522         /**
60523          * @event contextmenu
60524          * The raw contextmenu event for the entire grid.
60525          * @param {Roo.EventObject} e
60526          */
60527         "contextmenu" : true,
60528         /**
60529          * @event mousedown
60530          * The raw mousedown event for the entire grid.
60531          * @param {Roo.EventObject} e
60532          */
60533         "mousedown" : true,
60534         /**
60535          * @event mouseup
60536          * The raw mouseup event for the entire grid.
60537          * @param {Roo.EventObject} e
60538          */
60539         "mouseup" : true,
60540         /**
60541          * @event mouseover
60542          * The raw mouseover event for the entire grid.
60543          * @param {Roo.EventObject} e
60544          */
60545         "mouseover" : true,
60546         /**
60547          * @event mouseout
60548          * The raw mouseout event for the entire grid.
60549          * @param {Roo.EventObject} e
60550          */
60551         "mouseout" : true,
60552         /**
60553          * @event keypress
60554          * The raw keypress event for the entire grid.
60555          * @param {Roo.EventObject} e
60556          */
60557         "keypress" : true,
60558         /**
60559          * @event keydown
60560          * The raw keydown event for the entire grid.
60561          * @param {Roo.EventObject} e
60562          */
60563         "keydown" : true,
60564
60565         // custom events
60566
60567         /**
60568          * @event cellclick
60569          * Fires when a cell is clicked
60570          * @param {Grid} this
60571          * @param {Number} rowIndex
60572          * @param {Number} columnIndex
60573          * @param {Roo.EventObject} e
60574          */
60575         "cellclick" : true,
60576         /**
60577          * @event celldblclick
60578          * Fires when a cell is double clicked
60579          * @param {Grid} this
60580          * @param {Number} rowIndex
60581          * @param {Number} columnIndex
60582          * @param {Roo.EventObject} e
60583          */
60584         "celldblclick" : true,
60585         /**
60586          * @event rowclick
60587          * Fires when a row is clicked
60588          * @param {Grid} this
60589          * @param {Number} rowIndex
60590          * @param {Roo.EventObject} e
60591          */
60592         "rowclick" : true,
60593         /**
60594          * @event rowdblclick
60595          * Fires when a row is double clicked
60596          * @param {Grid} this
60597          * @param {Number} rowIndex
60598          * @param {Roo.EventObject} e
60599          */
60600         "rowdblclick" : true,
60601         /**
60602          * @event headerclick
60603          * Fires when a header is clicked
60604          * @param {Grid} this
60605          * @param {Number} columnIndex
60606          * @param {Roo.EventObject} e
60607          */
60608         "headerclick" : true,
60609         /**
60610          * @event headerdblclick
60611          * Fires when a header cell is double clicked
60612          * @param {Grid} this
60613          * @param {Number} columnIndex
60614          * @param {Roo.EventObject} e
60615          */
60616         "headerdblclick" : true,
60617         /**
60618          * @event rowcontextmenu
60619          * Fires when a row is right clicked
60620          * @param {Grid} this
60621          * @param {Number} rowIndex
60622          * @param {Roo.EventObject} e
60623          */
60624         "rowcontextmenu" : true,
60625         /**
60626          * @event cellcontextmenu
60627          * Fires when a cell is right clicked
60628          * @param {Grid} this
60629          * @param {Number} rowIndex
60630          * @param {Number} cellIndex
60631          * @param {Roo.EventObject} e
60632          */
60633          "cellcontextmenu" : true,
60634         /**
60635          * @event headercontextmenu
60636          * Fires when a header is right clicked
60637          * @param {Grid} this
60638          * @param {Number} columnIndex
60639          * @param {Roo.EventObject} e
60640          */
60641         "headercontextmenu" : true,
60642         /**
60643          * @event bodyscroll
60644          * Fires when the body element is scrolled
60645          * @param {Number} scrollLeft
60646          * @param {Number} scrollTop
60647          */
60648         "bodyscroll" : true,
60649         /**
60650          * @event columnresize
60651          * Fires when the user resizes a column
60652          * @param {Number} columnIndex
60653          * @param {Number} newSize
60654          */
60655         "columnresize" : true,
60656         /**
60657          * @event columnmove
60658          * Fires when the user moves a column
60659          * @param {Number} oldIndex
60660          * @param {Number} newIndex
60661          */
60662         "columnmove" : true,
60663         /**
60664          * @event startdrag
60665          * Fires when row(s) start being dragged
60666          * @param {Grid} this
60667          * @param {Roo.GridDD} dd The drag drop object
60668          * @param {event} e The raw browser event
60669          */
60670         "startdrag" : true,
60671         /**
60672          * @event enddrag
60673          * Fires when a drag operation is complete
60674          * @param {Grid} this
60675          * @param {Roo.GridDD} dd The drag drop object
60676          * @param {event} e The raw browser event
60677          */
60678         "enddrag" : true,
60679         /**
60680          * @event dragdrop
60681          * Fires when dragged row(s) are dropped on a valid DD target
60682          * @param {Grid} this
60683          * @param {Roo.GridDD} dd The drag drop object
60684          * @param {String} targetId The target drag drop object
60685          * @param {event} e The raw browser event
60686          */
60687         "dragdrop" : true,
60688         /**
60689          * @event dragover
60690          * Fires while row(s) are being dragged. "targetId" is the id of the Yahoo.util.DD object the selected rows are being dragged over.
60691          * @param {Grid} this
60692          * @param {Roo.GridDD} dd The drag drop object
60693          * @param {String} targetId The target drag drop object
60694          * @param {event} e The raw browser event
60695          */
60696         "dragover" : true,
60697         /**
60698          * @event dragenter
60699          *  Fires when the dragged row(s) first cross another DD target while being dragged
60700          * @param {Grid} this
60701          * @param {Roo.GridDD} dd The drag drop object
60702          * @param {String} targetId The target drag drop object
60703          * @param {event} e The raw browser event
60704          */
60705         "dragenter" : true,
60706         /**
60707          * @event dragout
60708          * Fires when the dragged row(s) leave another DD target while being dragged
60709          * @param {Grid} this
60710          * @param {Roo.GridDD} dd The drag drop object
60711          * @param {String} targetId The target drag drop object
60712          * @param {event} e The raw browser event
60713          */
60714         "dragout" : true,
60715         /**
60716          * @event rowclass
60717          * Fires when a row is rendered, so you can change add a style to it.
60718          * @param {GridView} gridview   The grid view
60719          * @param {Object} rowcfg   contains record  rowIndex and rowClass - set rowClass to add a style.
60720          */
60721         'rowclass' : true,
60722
60723         /**
60724          * @event render
60725          * Fires when the grid is rendered
60726          * @param {Grid} grid
60727          */
60728         'render' : true,
60729             /**
60730              * @event select
60731              * Fires when a date is selected
60732              * @param {DatePicker} this
60733              * @param {Date} date The selected date
60734              */
60735         'select': true,
60736         /**
60737              * @event monthchange
60738              * Fires when the displayed month changes 
60739              * @param {DatePicker} this
60740              * @param {Date} date The selected month
60741              */
60742         'monthchange': true,
60743         /**
60744              * @event evententer
60745              * Fires when mouse over an event
60746              * @param {Calendar} this
60747              * @param {event} Event
60748              */
60749         'evententer': true,
60750         /**
60751              * @event eventleave
60752              * Fires when the mouse leaves an
60753              * @param {Calendar} this
60754              * @param {event}
60755              */
60756         'eventleave': true,
60757         /**
60758              * @event eventclick
60759              * Fires when the mouse click an
60760              * @param {Calendar} this
60761              * @param {event}
60762              */
60763         'eventclick': true,
60764         /**
60765              * @event eventrender
60766              * Fires before each cell is rendered, so you can modify the contents, like cls / title / qtip
60767              * @param {Calendar} this
60768              * @param {data} data to be modified
60769              */
60770         'eventrender': true
60771         
60772     });
60773
60774     Roo.grid.Grid.superclass.constructor.call(this);
60775     this.on('render', function() {
60776         this.view.el.addClass('x-grid-cal'); 
60777         
60778         (function() { this.setDate(new Date()); }).defer(100,this); //default today..
60779
60780     },this);
60781     
60782     if (!Roo.grid.Calendar.style) {
60783         Roo.grid.Calendar.style = Roo.util.CSS.createStyleSheet({
60784             
60785             
60786             '.x-grid-cal .x-grid-col' :  {
60787                 height: 'auto !important',
60788                 'vertical-align': 'top'
60789             },
60790             '.x-grid-cal  .fc-event-hori' : {
60791                 height: '14px'
60792             }
60793              
60794             
60795         }, Roo.id());
60796     }
60797
60798     
60799     
60800 };
60801 Roo.extend(Roo.grid.Calendar, Roo.grid.Grid, {
60802     /**
60803      * @cfg {Store} eventStore The store that loads events.
60804      */
60805     eventStore : 25,
60806
60807      
60808     activeDate : false,
60809     startDay : 0,
60810     autoWidth : true,
60811     monitorWindowResize : false,
60812
60813     
60814     resizeColumns : function() {
60815         var col = (this.view.el.getWidth() / 7) - 3;
60816         // loop through cols, and setWidth
60817         for(var i =0 ; i < 7 ; i++){
60818             this.cm.setColumnWidth(i, col);
60819         }
60820     },
60821      setDate :function(date) {
60822         
60823         Roo.log('setDate?');
60824         
60825         this.resizeColumns();
60826         var vd = this.activeDate;
60827         this.activeDate = date;
60828 //        if(vd && this.el){
60829 //            var t = date.getTime();
60830 //            if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
60831 //                Roo.log('using add remove');
60832 //                
60833 //                this.fireEvent('monthchange', this, date);
60834 //                
60835 //                this.cells.removeClass("fc-state-highlight");
60836 //                this.cells.each(function(c){
60837 //                   if(c.dateValue == t){
60838 //                       c.addClass("fc-state-highlight");
60839 //                       setTimeout(function(){
60840 //                            try{c.dom.firstChild.focus();}catch(e){}
60841 //                       }, 50);
60842 //                       return false;
60843 //                   }
60844 //                   return true;
60845 //                });
60846 //                return;
60847 //            }
60848 //        }
60849         
60850         var days = date.getDaysInMonth();
60851         
60852         var firstOfMonth = date.getFirstDateOfMonth();
60853         var startingPos = firstOfMonth.getDay()-this.startDay;
60854         
60855         if(startingPos < this.startDay){
60856             startingPos += 7;
60857         }
60858         
60859         var pm = date.add(Date.MONTH, -1);
60860         var prevStart = pm.getDaysInMonth()-startingPos;
60861 //        
60862         
60863         
60864         this.cells = this.view.el.select('.x-grid-row .x-grid-col',true);
60865         
60866         this.textNodes = this.view.el.query('.x-grid-row .x-grid-col .x-grid-cell-text');
60867         //this.cells.addClassOnOver('fc-state-hover');
60868         
60869         var cells = this.cells.elements;
60870         var textEls = this.textNodes;
60871         
60872         //Roo.each(cells, function(cell){
60873         //    cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
60874         //});
60875         
60876         days += startingPos;
60877
60878         // convert everything to numbers so it's fast
60879         var day = 86400000;
60880         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
60881         //Roo.log(d);
60882         //Roo.log(pm);
60883         //Roo.log(prevStart);
60884         
60885         var today = new Date().clearTime().getTime();
60886         var sel = date.clearTime().getTime();
60887         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
60888         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
60889         var ddMatch = this.disabledDatesRE;
60890         var ddText = this.disabledDatesText;
60891         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
60892         var ddaysText = this.disabledDaysText;
60893         var format = this.format;
60894         
60895         var setCellClass = function(cal, cell){
60896             
60897             //Roo.log('set Cell Class');
60898             cell.title = "";
60899             var t = d.getTime();
60900             
60901             //Roo.log(d);
60902             
60903             
60904             cell.dateValue = t;
60905             if(t == today){
60906                 cell.className += " fc-today";
60907                 cell.className += " fc-state-highlight";
60908                 cell.title = cal.todayText;
60909             }
60910             if(t == sel){
60911                 // disable highlight in other month..
60912                 cell.className += " fc-state-highlight";
60913                 
60914             }
60915             // disabling
60916             if(t < min) {
60917                 //cell.className = " fc-state-disabled";
60918                 cell.title = cal.minText;
60919                 return;
60920             }
60921             if(t > max) {
60922                 //cell.className = " fc-state-disabled";
60923                 cell.title = cal.maxText;
60924                 return;
60925             }
60926             if(ddays){
60927                 if(ddays.indexOf(d.getDay()) != -1){
60928                     // cell.title = ddaysText;
60929                    // cell.className = " fc-state-disabled";
60930                 }
60931             }
60932             if(ddMatch && format){
60933                 var fvalue = d.dateFormat(format);
60934                 if(ddMatch.test(fvalue)){
60935                     cell.title = ddText.replace("%0", fvalue);
60936                    cell.className = " fc-state-disabled";
60937                 }
60938             }
60939             
60940             if (!cell.initialClassName) {
60941                 cell.initialClassName = cell.dom.className;
60942             }
60943             
60944             cell.dom.className = cell.initialClassName  + ' ' +  cell.className;
60945         };
60946
60947         var i = 0;
60948         
60949         for(; i < startingPos; i++) {
60950             cells[i].dayName =  (++prevStart);
60951             Roo.log(textEls[i]);
60952             d.setDate(d.getDate()+1);
60953             
60954             //cells[i].className = "fc-past fc-other-month";
60955             setCellClass(this, cells[i]);
60956         }
60957         
60958         var intDay = 0;
60959         
60960         for(; i < days; i++){
60961             intDay = i - startingPos + 1;
60962             cells[i].dayName =  (intDay);
60963             d.setDate(d.getDate()+1);
60964             
60965             cells[i].className = ''; // "x-date-active";
60966             setCellClass(this, cells[i]);
60967         }
60968         var extraDays = 0;
60969         
60970         for(; i < 42; i++) {
60971             //textEls[i].innerHTML = (++extraDays);
60972             
60973             d.setDate(d.getDate()+1);
60974             cells[i].dayName = (++extraDays);
60975             cells[i].className = "fc-future fc-other-month";
60976             setCellClass(this, cells[i]);
60977         }
60978         
60979         //this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
60980         
60981         var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
60982         
60983         // this will cause all the cells to mis
60984         var rows= [];
60985         var i =0;
60986         for (var r = 0;r < 6;r++) {
60987             for (var c =0;c < 7;c++) {
60988                 this.ds.getAt(r).set('weekday' + c ,cells[i++].dayName );
60989             }    
60990         }
60991         
60992         this.cells = this.view.el.select('.x-grid-row .x-grid-col',true);
60993         for(i=0;i<cells.length;i++) {
60994             
60995             this.cells.elements[i].dayName = cells[i].dayName ;
60996             this.cells.elements[i].className = cells[i].className;
60997             this.cells.elements[i].initialClassName = cells[i].initialClassName ;
60998             this.cells.elements[i].title = cells[i].title ;
60999             this.cells.elements[i].dateValue = cells[i].dateValue ;
61000         }
61001         
61002         
61003         
61004         
61005         //this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
61006         //this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
61007         
61008         ////if(totalRows != 6){
61009             //this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
61010            // this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
61011        // }
61012         
61013         this.fireEvent('monthchange', this, date);
61014         
61015         
61016     },
61017  /**
61018      * Returns the grid's SelectionModel.
61019      * @return {SelectionModel}
61020      */
61021     getSelectionModel : function(){
61022         if(!this.selModel){
61023             this.selModel = new Roo.grid.CellSelectionModel();
61024         }
61025         return this.selModel;
61026     },
61027
61028     load: function() {
61029         this.eventStore.load()
61030         
61031         
61032         
61033     },
61034     
61035     findCell : function(dt) {
61036         dt = dt.clearTime().getTime();
61037         var ret = false;
61038         this.cells.each(function(c){
61039             //Roo.log("check " +c.dateValue + '?=' + dt);
61040             if(c.dateValue == dt){
61041                 ret = c;
61042                 return false;
61043             }
61044             return true;
61045         });
61046         
61047         return ret;
61048     },
61049     
61050     findCells : function(rec) {
61051         var s = rec.data.start_dt.clone().clearTime().getTime();
61052        // Roo.log(s);
61053         var e= rec.data.end_dt.clone().clearTime().getTime();
61054        // Roo.log(e);
61055         var ret = [];
61056         this.cells.each(function(c){
61057              ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
61058             
61059             if(c.dateValue > e){
61060                 return ;
61061             }
61062             if(c.dateValue < s){
61063                 return ;
61064             }
61065             ret.push(c);
61066         });
61067         
61068         return ret;    
61069     },
61070     
61071     findBestRow: function(cells)
61072     {
61073         var ret = 0;
61074         
61075         for (var i =0 ; i < cells.length;i++) {
61076             ret  = Math.max(cells[i].rows || 0,ret);
61077         }
61078         return ret;
61079         
61080     },
61081     
61082     
61083     addItem : function(rec)
61084     {
61085         // look for vertical location slot in
61086         var cells = this.findCells(rec);
61087         
61088         rec.row = this.findBestRow(cells);
61089         
61090         // work out the location.
61091         
61092         var crow = false;
61093         var rows = [];
61094         for(var i =0; i < cells.length; i++) {
61095             if (!crow) {
61096                 crow = {
61097                     start : cells[i],
61098                     end :  cells[i]
61099                 };
61100                 continue;
61101             }
61102             if (crow.start.getY() == cells[i].getY()) {
61103                 // on same row.
61104                 crow.end = cells[i];
61105                 continue;
61106             }
61107             // different row.
61108             rows.push(crow);
61109             crow = {
61110                 start: cells[i],
61111                 end : cells[i]
61112             };
61113             
61114         }
61115         
61116         rows.push(crow);
61117         rec.els = [];
61118         rec.rows = rows;
61119         rec.cells = cells;
61120         for (var i = 0; i < cells.length;i++) {
61121             cells[i].rows = Math.max(cells[i].rows || 0 , rec.row + 1 );
61122             
61123         }
61124         
61125         
61126     },
61127     
61128     clearEvents: function() {
61129         
61130         if (!this.eventStore.getCount()) {
61131             return;
61132         }
61133         // reset number of rows in cells.
61134         Roo.each(this.cells.elements, function(c){
61135             c.rows = 0;
61136         });
61137         
61138         this.eventStore.each(function(e) {
61139             this.clearEvent(e);
61140         },this);
61141         
61142     },
61143     
61144     clearEvent : function(ev)
61145     {
61146         if (ev.els) {
61147             Roo.each(ev.els, function(el) {
61148                 el.un('mouseenter' ,this.onEventEnter, this);
61149                 el.un('mouseleave' ,this.onEventLeave, this);
61150                 el.remove();
61151             },this);
61152             ev.els = [];
61153         }
61154     },
61155     
61156     
61157     renderEvent : function(ev,ctr) {
61158         if (!ctr) {
61159              ctr = this.view.el.select('.fc-event-container',true).first();
61160         }
61161         
61162          
61163         this.clearEvent(ev);
61164             //code
61165        
61166         
61167         
61168         ev.els = [];
61169         var cells = ev.cells;
61170         var rows = ev.rows;
61171         this.fireEvent('eventrender', this, ev);
61172         
61173         for(var i =0; i < rows.length; i++) {
61174             
61175             cls = '';
61176             if (i == 0) {
61177                 cls += ' fc-event-start';
61178             }
61179             if ((i+1) == rows.length) {
61180                 cls += ' fc-event-end';
61181             }
61182             
61183             //Roo.log(ev.data);
61184             // how many rows should it span..
61185             var cg = this.eventTmpl.append(ctr,Roo.apply({
61186                 fccls : cls
61187                 
61188             }, ev.data) , true);
61189             
61190             
61191             cg.on('mouseenter' ,this.onEventEnter, this, ev);
61192             cg.on('mouseleave' ,this.onEventLeave, this, ev);
61193             cg.on('click', this.onEventClick, this, ev);
61194             
61195             ev.els.push(cg);
61196             
61197             var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
61198             var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
61199             //Roo.log(cg);
61200              
61201             cg.setXY([sbox.x +2, sbox.y +(ev.row * 20)]);    
61202             cg.setWidth(ebox.right - sbox.x -2);
61203         }
61204     },
61205     
61206     renderEvents: function()
61207     {   
61208         // first make sure there is enough space..
61209         
61210         if (!this.eventTmpl) {
61211             this.eventTmpl = new Roo.Template(
61212                 '<div class="roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable {fccls} {cls}"  style="position: absolute" unselectable="on">' +
61213                     '<div class="fc-event-inner">' +
61214                         '<span class="fc-event-time">{time}</span>' +
61215                         '<span class="fc-event-title" qtip="{qtip}">{title}</span>' +
61216                     '</div>' +
61217                     '<div class="ui-resizable-heandle ui-resizable-e">&nbsp;&nbsp;&nbsp;</div>' +
61218                 '</div>'
61219             );
61220                 
61221         }
61222                
61223         
61224         
61225         this.cells.each(function(c) {
61226             //Roo.log(c.select('.fc-day-content div',true).first());
61227             c.select('.fc-day-content div',true).first().setHeight(Math.max(34, (c.rows || 1) * 20));
61228         });
61229         
61230         var ctr = this.view.el.select('.fc-event-container',true).first();
61231         
61232         var cls;
61233         this.eventStore.each(function(ev){
61234             
61235             this.renderEvent(ev);
61236              
61237              
61238         }, this);
61239         this.view.layout();
61240         
61241     },
61242     
61243     onEventEnter: function (e, el,event,d) {
61244         this.fireEvent('evententer', this, el, event);
61245     },
61246     
61247     onEventLeave: function (e, el,event,d) {
61248         this.fireEvent('eventleave', this, el, event);
61249     },
61250     
61251     onEventClick: function (e, el,event,d) {
61252         this.fireEvent('eventclick', this, el, event);
61253     },
61254     
61255     onMonthChange: function () {
61256         this.store.load();
61257     },
61258     
61259     onLoad: function () {
61260         
61261         //Roo.log('calendar onload');
61262 //         
61263         if(this.eventStore.getCount() > 0){
61264             
61265            
61266             
61267             this.eventStore.each(function(d){
61268                 
61269                 
61270                 // FIXME..
61271                 var add =   d.data;
61272                 if (typeof(add.end_dt) == 'undefined')  {
61273                     Roo.log("Missing End time in calendar data: ");
61274                     Roo.log(d);
61275                     return;
61276                 }
61277                 if (typeof(add.start_dt) == 'undefined')  {
61278                     Roo.log("Missing Start time in calendar data: ");
61279                     Roo.log(d);
61280                     return;
61281                 }
61282                 add.start_dt = typeof(add.start_dt) == 'string' ? Date.parseDate(add.start_dt,'Y-m-d H:i:s') : add.start_dt,
61283                 add.end_dt = typeof(add.end_dt) == 'string' ? Date.parseDate(add.end_dt,'Y-m-d H:i:s') : add.end_dt,
61284                 add.id = add.id || d.id;
61285                 add.title = add.title || '??';
61286                 
61287                 this.addItem(d);
61288                 
61289              
61290             },this);
61291         }
61292         
61293         this.renderEvents();
61294     }
61295     
61296
61297 });
61298 /*
61299  grid : {
61300                 xtype: 'Grid',
61301                 xns: Roo.grid,
61302                 listeners : {
61303                     render : function ()
61304                     {
61305                         _this.grid = this;
61306                         
61307                         if (!this.view.el.hasClass('course-timesheet')) {
61308                             this.view.el.addClass('course-timesheet');
61309                         }
61310                         if (this.tsStyle) {
61311                             this.ds.load({});
61312                             return; 
61313                         }
61314                         Roo.log('width');
61315                         Roo.log(_this.grid.view.el.getWidth());
61316                         
61317                         
61318                         this.tsStyle =  Roo.util.CSS.createStyleSheet({
61319                             '.course-timesheet .x-grid-row' : {
61320                                 height: '80px'
61321                             },
61322                             '.x-grid-row td' : {
61323                                 'vertical-align' : 0
61324                             },
61325                             '.course-edit-link' : {
61326                                 'color' : 'blue',
61327                                 'text-overflow' : 'ellipsis',
61328                                 'overflow' : 'hidden',
61329                                 'white-space' : 'nowrap',
61330                                 'cursor' : 'pointer'
61331                             },
61332                             '.sub-link' : {
61333                                 'color' : 'green'
61334                             },
61335                             '.de-act-sup-link' : {
61336                                 'color' : 'purple',
61337                                 'text-decoration' : 'line-through'
61338                             },
61339                             '.de-act-link' : {
61340                                 'color' : 'red',
61341                                 'text-decoration' : 'line-through'
61342                             },
61343                             '.course-timesheet .course-highlight' : {
61344                                 'border-top-style': 'dashed !important',
61345                                 'border-bottom-bottom': 'dashed !important'
61346                             },
61347                             '.course-timesheet .course-item' : {
61348                                 'font-family'   : 'tahoma, arial, helvetica',
61349                                 'font-size'     : '11px',
61350                                 'overflow'      : 'hidden',
61351                                 'padding-left'  : '10px',
61352                                 'padding-right' : '10px',
61353                                 'padding-top' : '10px' 
61354                             }
61355                             
61356                         }, Roo.id());
61357                                 this.ds.load({});
61358                     }
61359                 },
61360                 autoWidth : true,
61361                 monitorWindowResize : false,
61362                 cellrenderer : function(v,x,r)
61363                 {
61364                     return v;
61365                 },
61366                 sm : {
61367                     xtype: 'CellSelectionModel',
61368                     xns: Roo.grid
61369                 },
61370                 dataSource : {
61371                     xtype: 'Store',
61372                     xns: Roo.data,
61373                     listeners : {
61374                         beforeload : function (_self, options)
61375                         {
61376                             options.params = options.params || {};
61377                             options.params._month = _this.monthField.getValue();
61378                             options.params.limit = 9999;
61379                             options.params['sort'] = 'when_dt';    
61380                             options.params['dir'] = 'ASC';    
61381                             this.proxy.loadResponse = this.loadResponse;
61382                             Roo.log("load?");
61383                             //this.addColumns();
61384                         },
61385                         load : function (_self, records, options)
61386                         {
61387                             _this.grid.view.el.select('.course-edit-link', true).on('click', function() {
61388                                 // if you click on the translation.. you can edit it...
61389                                 var el = Roo.get(this);
61390                                 var id = el.dom.getAttribute('data-id');
61391                                 var d = el.dom.getAttribute('data-date');
61392                                 var t = el.dom.getAttribute('data-time');
61393                                 //var id = this.child('span').dom.textContent;
61394                                 
61395                                 //Roo.log(this);
61396                                 Pman.Dialog.CourseCalendar.show({
61397                                     id : id,
61398                                     when_d : d,
61399                                     when_t : t,
61400                                     productitem_active : id ? 1 : 0
61401                                 }, function() {
61402                                     _this.grid.ds.load({});
61403                                 });
61404                            
61405                            });
61406                            
61407                            _this.panel.fireEvent('resize', [ '', '' ]);
61408                         }
61409                     },
61410                     loadResponse : function(o, success, response){
61411                             // this is overridden on before load..
61412                             
61413                             Roo.log("our code?");       
61414                             //Roo.log(success);
61415                             //Roo.log(response)
61416                             delete this.activeRequest;
61417                             if(!success){
61418                                 this.fireEvent("loadexception", this, o, response);
61419                                 o.request.callback.call(o.request.scope, null, o.request.arg, false);
61420                                 return;
61421                             }
61422                             var result;
61423                             try {
61424                                 result = o.reader.read(response);
61425                             }catch(e){
61426                                 Roo.log("load exception?");
61427                                 this.fireEvent("loadexception", this, o, response, e);
61428                                 o.request.callback.call(o.request.scope, null, o.request.arg, false);
61429                                 return;
61430                             }
61431                             Roo.log("ready...");        
61432                             // loop through result.records;
61433                             // and set this.tdate[date] = [] << array of records..
61434                             _this.tdata  = {};
61435                             Roo.each(result.records, function(r){
61436                                 //Roo.log(r.data);
61437                                 if(typeof(_this.tdata[r.data.when_dt.format('j')]) == 'undefined'){
61438                                     _this.tdata[r.data.when_dt.format('j')] = [];
61439                                 }
61440                                 _this.tdata[r.data.when_dt.format('j')].push(r.data);
61441                             });
61442                             
61443                             //Roo.log(_this.tdata);
61444                             
61445                             result.records = [];
61446                             result.totalRecords = 6;
61447                     
61448                             // let's generate some duumy records for the rows.
61449                             //var st = _this.dateField.getValue();
61450                             
61451                             // work out monday..
61452                             //st = st.add(Date.DAY, -1 * st.format('w'));
61453                             
61454                             var date = Date.parseDate(_this.monthField.getValue(), "Y-m-d");
61455                             
61456                             var firstOfMonth = date.getFirstDayOfMonth();
61457                             var days = date.getDaysInMonth();
61458                             var d = 1;
61459                             var firstAdded = false;
61460                             for (var i = 0; i < result.totalRecords ; i++) {
61461                                 //var d= st.add(Date.DAY, i);
61462                                 var row = {};
61463                                 var added = 0;
61464                                 for(var w = 0 ; w < 7 ; w++){
61465                                     if(!firstAdded && firstOfMonth != w){
61466                                         continue;
61467                                     }
61468                                     if(d > days){
61469                                         continue;
61470                                     }
61471                                     firstAdded = true;
61472                                     var dd = (d > 0 && d < 10) ? "0"+d : d;
61473                                     row['weekday'+w] = String.format(
61474                                                     '<span style="font-size: 16px;"><b>{0}</b></span>'+
61475                                                     '<span class="course-edit-link" style="color:blue;" data-id="0" data-date="{1}"> Add New</span>',
61476                                                     d,
61477                                                     date.format('Y-m-')+dd
61478                                                 );
61479                                     added++;
61480                                     if(typeof(_this.tdata[d]) != 'undefined'){
61481                                         Roo.each(_this.tdata[d], function(r){
61482                                             var is_sub = '';
61483                                             var deactive = '';
61484                                             var id = r.id;
61485                                             var desc = (r.productitem_id_descrip) ? r.productitem_id_descrip : '';
61486                                             if(r.parent_id*1>0){
61487                                                 is_sub = (r.productitem_id_visible*1 < 1) ? 'de-act-sup-link' :'sub-link';
61488                                                 id = r.parent_id;
61489                                             }
61490                                             if(r.productitem_id_visible*1 < 1 && r.parent_id*1 < 1){
61491                                                 deactive = 'de-act-link';
61492                                             }
61493                                             
61494                                             row['weekday'+w] += String.format(
61495                                                     '<br /><span class="course-edit-link {3} {4}" qtip="{5}" data-id="{0}">{2} - {1}</span>',
61496                                                     id, //0
61497                                                     r.product_id_name, //1
61498                                                     r.when_dt.format('h:ia'), //2
61499                                                     is_sub, //3
61500                                                     deactive, //4
61501                                                     desc // 5
61502                                             );
61503                                         });
61504                                     }
61505                                     d++;
61506                                 }
61507                                 
61508                                 // only do this if something added..
61509                                 if(added > 0){ 
61510                                     result.records.push(_this.grid.dataSource.reader.newRow(row));
61511                                 }
61512                                 
61513                                 
61514                                 // push it twice. (second one with an hour..
61515                                 
61516                             }
61517                             //Roo.log(result);
61518                             this.fireEvent("load", this, o, o.request.arg);
61519                             o.request.callback.call(o.request.scope, result, o.request.arg, true);
61520                         },
61521                     sortInfo : {field: 'when_dt', direction : 'ASC' },
61522                     proxy : {
61523                         xtype: 'HttpProxy',
61524                         xns: Roo.data,
61525                         method : 'GET',
61526                         url : baseURL + '/Roo/Shop_course.php'
61527                     },
61528                     reader : {
61529                         xtype: 'JsonReader',
61530                         xns: Roo.data,
61531                         id : 'id',
61532                         fields : [
61533                             {
61534                                 'name': 'id',
61535                                 'type': 'int'
61536                             },
61537                             {
61538                                 'name': 'when_dt',
61539                                 'type': 'string'
61540                             },
61541                             {
61542                                 'name': 'end_dt',
61543                                 'type': 'string'
61544                             },
61545                             {
61546                                 'name': 'parent_id',
61547                                 'type': 'int'
61548                             },
61549                             {
61550                                 'name': 'product_id',
61551                                 'type': 'int'
61552                             },
61553                             {
61554                                 'name': 'productitem_id',
61555                                 'type': 'int'
61556                             },
61557                             {
61558                                 'name': 'guid',
61559                                 'type': 'int'
61560                             }
61561                         ]
61562                     }
61563                 },
61564                 toolbar : {
61565                     xtype: 'Toolbar',
61566                     xns: Roo,
61567                     items : [
61568                         {
61569                             xtype: 'Button',
61570                             xns: Roo.Toolbar,
61571                             listeners : {
61572                                 click : function (_self, e)
61573                                 {
61574                                     var sd = Date.parseDate(_this.monthField.getValue(), "Y-m-d");
61575                                     sd.setMonth(sd.getMonth()-1);
61576                                     _this.monthField.setValue(sd.format('Y-m-d'));
61577                                     _this.grid.ds.load({});
61578                                 }
61579                             },
61580                             text : "Back"
61581                         },
61582                         {
61583                             xtype: 'Separator',
61584                             xns: Roo.Toolbar
61585                         },
61586                         {
61587                             xtype: 'MonthField',
61588                             xns: Roo.form,
61589                             listeners : {
61590                                 render : function (_self)
61591                                 {
61592                                     _this.monthField = _self;
61593                                    // _this.monthField.set  today
61594                                 },
61595                                 select : function (combo, date)
61596                                 {
61597                                     _this.grid.ds.load({});
61598                                 }
61599                             },
61600                             value : (function() { return new Date(); })()
61601                         },
61602                         {
61603                             xtype: 'Separator',
61604                             xns: Roo.Toolbar
61605                         },
61606                         {
61607                             xtype: 'TextItem',
61608                             xns: Roo.Toolbar,
61609                             text : "Blue: in-active, green: in-active sup-event, red: de-active, purple: de-active sup-event"
61610                         },
61611                         {
61612                             xtype: 'Fill',
61613                             xns: Roo.Toolbar
61614                         },
61615                         {
61616                             xtype: 'Button',
61617                             xns: Roo.Toolbar,
61618                             listeners : {
61619                                 click : function (_self, e)
61620                                 {
61621                                     var sd = Date.parseDate(_this.monthField.getValue(), "Y-m-d");
61622                                     sd.setMonth(sd.getMonth()+1);
61623                                     _this.monthField.setValue(sd.format('Y-m-d'));
61624                                     _this.grid.ds.load({});
61625                                 }
61626                             },
61627                             text : "Next"
61628                         }
61629                     ]
61630                 },
61631                  
61632             }
61633         };
61634         
61635         *//*
61636  * Based on:
61637  * Ext JS Library 1.1.1
61638  * Copyright(c) 2006-2007, Ext JS, LLC.
61639  *
61640  * Originally Released Under LGPL - original licence link has changed is not relivant.
61641  *
61642  * Fork - LGPL
61643  * <script type="text/javascript">
61644  */
61645  
61646 /**
61647  * @class Roo.LoadMask
61648  * A simple utility class for generically masking elements while loading data.  If the element being masked has
61649  * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
61650  * process and the mask element will be cached for reuse.  For all other elements, this mask will replace the
61651  * element's UpdateManager load indicator and will be destroyed after the initial load.
61652  * @constructor
61653  * Create a new LoadMask
61654  * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
61655  * @param {Object} config The config object
61656  */
61657 Roo.LoadMask = function(el, config){
61658     this.el = Roo.get(el);
61659     Roo.apply(this, config);
61660     if(this.store){
61661         this.store.on('beforeload', this.onBeforeLoad, this);
61662         this.store.on('load', this.onLoad, this);
61663         this.store.on('loadexception', this.onLoadException, this);
61664         this.removeMask = false;
61665     }else{
61666         var um = this.el.getUpdateManager();
61667         um.showLoadIndicator = false; // disable the default indicator
61668         um.on('beforeupdate', this.onBeforeLoad, this);
61669         um.on('update', this.onLoad, this);
61670         um.on('failure', this.onLoad, this);
61671         this.removeMask = true;
61672     }
61673 };
61674
61675 Roo.LoadMask.prototype = {
61676     /**
61677      * @cfg {Boolean} removeMask
61678      * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
61679      * False to persist the mask element reference for multiple uses (e.g., for paged data widgets).  Defaults to false.
61680      */
61681     removeMask : false,
61682     /**
61683      * @cfg {String} msg
61684      * The text to display in a centered loading message box (defaults to 'Loading...')
61685      */
61686     msg : 'Loading...',
61687     /**
61688      * @cfg {String} msgCls
61689      * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
61690      */
61691     msgCls : 'x-mask-loading',
61692
61693     /**
61694      * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
61695      * @type Boolean
61696      */
61697     disabled: false,
61698
61699     /**
61700      * Disables the mask to prevent it from being displayed
61701      */
61702     disable : function(){
61703        this.disabled = true;
61704     },
61705
61706     /**
61707      * Enables the mask so that it can be displayed
61708      */
61709     enable : function(){
61710         this.disabled = false;
61711     },
61712     
61713     onLoadException : function()
61714     {
61715         Roo.log(arguments);
61716         
61717         if (typeof(arguments[3]) != 'undefined') {
61718             Roo.MessageBox.alert("Error loading",arguments[3]);
61719         } 
61720         /*
61721         try {
61722             if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
61723                 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
61724             }   
61725         } catch(e) {
61726             
61727         }
61728         */
61729     
61730         (function() { this.el.unmask(this.removeMask); }).defer(50, this);
61731     },
61732     // private
61733     onLoad : function()
61734     {
61735         (function() { this.el.unmask(this.removeMask); }).defer(50, this);
61736     },
61737
61738     // private
61739     onBeforeLoad : function(){
61740         if(!this.disabled){
61741             (function() { this.el.mask(this.msg, this.msgCls); }).defer(50, this);
61742         }
61743     },
61744
61745     // private
61746     destroy : function(){
61747         if(this.store){
61748             this.store.un('beforeload', this.onBeforeLoad, this);
61749             this.store.un('load', this.onLoad, this);
61750             this.store.un('loadexception', this.onLoadException, this);
61751         }else{
61752             var um = this.el.getUpdateManager();
61753             um.un('beforeupdate', this.onBeforeLoad, this);
61754             um.un('update', this.onLoad, this);
61755             um.un('failure', this.onLoad, this);
61756         }
61757     }
61758 };/*
61759  * Based on:
61760  * Ext JS Library 1.1.1
61761  * Copyright(c) 2006-2007, Ext JS, LLC.
61762  *
61763  * Originally Released Under LGPL - original licence link has changed is not relivant.
61764  *
61765  * Fork - LGPL
61766  * <script type="text/javascript">
61767  */
61768
61769
61770 /**
61771  * @class Roo.XTemplate
61772  * @extends Roo.Template
61773  * Provides a template that can have nested templates for loops or conditionals. The syntax is:
61774 <pre><code>
61775 var t = new Roo.XTemplate(
61776         '&lt;select name="{name}"&gt;',
61777                 '&lt;tpl for="options"&gt;&lt;option value="{value:trim}"&gt;{text:ellipsis(10)}&lt;/option&gt;&lt;/tpl&gt;',
61778         '&lt;/select&gt;'
61779 );
61780  
61781 // then append, applying the master template values
61782  </code></pre>
61783  *
61784  * Supported features:
61785  *
61786  *  Tags:
61787
61788 <pre><code>
61789       {a_variable} - output encoded.
61790       {a_variable.format:("Y-m-d")} - call a method on the variable
61791       {a_variable:raw} - unencoded output
61792       {a_variable:toFixed(1,2)} - Roo.util.Format."toFixed"
61793       {a_variable:this.method_on_template(...)} - call a method on the template object.
61794  
61795 </code></pre>
61796  *  The tpl tag:
61797 <pre><code>
61798         &lt;tpl for="a_variable or condition.."&gt;&lt;/tpl&gt;
61799         &lt;tpl if="a_variable or condition"&gt;&lt;/tpl&gt;
61800         &lt;tpl exec="some javascript"&gt;&lt;/tpl&gt;
61801         &lt;tpl name="named_template"&gt;&lt;/tpl&gt; (experimental)
61802   
61803         &lt;tpl for="."&gt;&lt;/tpl&gt; - just iterate the property..
61804         &lt;tpl for=".."&gt;&lt;/tpl&gt; - iterates with the parent (probably the template) 
61805 </code></pre>
61806  *      
61807  */
61808 Roo.XTemplate = function()
61809 {
61810     Roo.XTemplate.superclass.constructor.apply(this, arguments);
61811     if (this.html) {
61812         this.compile();
61813     }
61814 };
61815
61816
61817 Roo.extend(Roo.XTemplate, Roo.Template, {
61818
61819     /**
61820      * The various sub templates
61821      */
61822     tpls : false,
61823     /**
61824      *
61825      * basic tag replacing syntax
61826      * WORD:WORD()
61827      *
61828      * // you can fake an object call by doing this
61829      *  x.t:(test,tesT) 
61830      * 
61831      */
61832     re : /\{([\w-\.]+)(?:\:([\w\.]*)(?:\((.*?)?\))?)?\}/g,
61833
61834     /**
61835      * compile the template
61836      *
61837      * This is not recursive, so I'm not sure how nested templates are really going to be handled..
61838      *
61839      */
61840     compile: function()
61841     {
61842         var s = this.html;
61843      
61844         s = ['<tpl>', s, '</tpl>'].join('');
61845     
61846         var re     = /<tpl\b[^>]*>((?:(?=([^<]+))\2|<(?!tpl\b[^>]*>))*?)<\/tpl>/,
61847             nameRe = /^<tpl\b[^>]*?for="(.*?)"/,
61848             ifRe   = /^<tpl\b[^>]*?if="(.*?)"/,
61849             execRe = /^<tpl\b[^>]*?exec="(.*?)"/,
61850             namedRe = /^<tpl\b[^>]*?name="(\w+)"/,  // named templates..
61851             m,
61852             id     = 0,
61853             tpls   = [];
61854     
61855         while(true == !!(m = s.match(re))){
61856             var forMatch   = m[0].match(nameRe),
61857                 ifMatch   = m[0].match(ifRe),
61858                 execMatch   = m[0].match(execRe),
61859                 namedMatch   = m[0].match(namedRe),
61860                 
61861                 exp  = null, 
61862                 fn   = null,
61863                 exec = null,
61864                 name = forMatch && forMatch[1] ? forMatch[1] : '';
61865                 
61866             if (ifMatch) {
61867                 // if - puts fn into test..
61868                 exp = ifMatch && ifMatch[1] ? ifMatch[1] : null;
61869                 if(exp){
61870                    fn = new Function('values', 'parent', 'with(values){ return '+(Roo.util.Format.htmlDecode(exp))+'; }');
61871                 }
61872             }
61873             
61874             if (execMatch) {
61875                 // exec - calls a function... returns empty if true is  returned.
61876                 exp = execMatch && execMatch[1] ? execMatch[1] : null;
61877                 if(exp){
61878                    exec = new Function('values', 'parent', 'with(values){ '+(Roo.util.Format.htmlDecode(exp))+'; }');
61879                 }
61880             }
61881             
61882             
61883             if (name) {
61884                 // for = 
61885                 switch(name){
61886                     case '.':  name = new Function('values', 'parent', 'with(values){ return values; }'); break;
61887                     case '..': name = new Function('values', 'parent', 'with(values){ return parent; }'); break;
61888                     default:   name = new Function('values', 'parent', 'with(values){ return '+name+'; }');
61889                 }
61890             }
61891             var uid = namedMatch ? namedMatch[1] : id;
61892             
61893             
61894             tpls.push({
61895                 id:     namedMatch ? namedMatch[1] : id,
61896                 target: name,
61897                 exec:   exec,
61898                 test:   fn,
61899                 body:   m[1] || ''
61900             });
61901             if (namedMatch) {
61902                 s = s.replace(m[0], '');
61903             } else { 
61904                 s = s.replace(m[0], '{xtpl'+ id + '}');
61905             }
61906             ++id;
61907         }
61908         this.tpls = [];
61909         for(var i = tpls.length-1; i >= 0; --i){
61910             this.compileTpl(tpls[i]);
61911             this.tpls[tpls[i].id] = tpls[i];
61912         }
61913         this.master = tpls[tpls.length-1];
61914         return this;
61915     },
61916     /**
61917      * same as applyTemplate, except it's done to one of the subTemplates
61918      * when using named templates, you can do:
61919      *
61920      * var str = pl.applySubTemplate('your-name', values);
61921      *
61922      * 
61923      * @param {Number} id of the template
61924      * @param {Object} values to apply to template
61925      * @param {Object} parent (normaly the instance of this object)
61926      */
61927     applySubTemplate : function(id, values, parent)
61928     {
61929         
61930         
61931         var t = this.tpls[id];
61932         
61933         
61934         try { 
61935             if(t.test && !t.test.call(this, values, parent)){
61936                 return '';
61937             }
61938         } catch(e) {
61939             Roo.log("Xtemplate.applySubTemplate 'test': Exception thrown");
61940             Roo.log(e.toString());
61941             Roo.log(t.test);
61942             return ''
61943         }
61944         try { 
61945             
61946             if(t.exec && t.exec.call(this, values, parent)){
61947                 return '';
61948             }
61949         } catch(e) {
61950             Roo.log("Xtemplate.applySubTemplate 'exec': Exception thrown");
61951             Roo.log(e.toString());
61952             Roo.log(t.exec);
61953             return ''
61954         }
61955         try {
61956             var vs = t.target ? t.target.call(this, values, parent) : values;
61957             parent = t.target ? values : parent;
61958             if(t.target && vs instanceof Array){
61959                 var buf = [];
61960                 for(var i = 0, len = vs.length; i < len; i++){
61961                     buf[buf.length] = t.compiled.call(this, vs[i], parent);
61962                 }
61963                 return buf.join('');
61964             }
61965             return t.compiled.call(this, vs, parent);
61966         } catch (e) {
61967             Roo.log("Xtemplate.applySubTemplate : Exception thrown");
61968             Roo.log(e.toString());
61969             Roo.log(t.compiled);
61970             return '';
61971         }
61972     },
61973
61974     compileTpl : function(tpl)
61975     {
61976         var fm = Roo.util.Format;
61977         var useF = this.disableFormats !== true;
61978         var sep = Roo.isGecko ? "+" : ",";
61979         var undef = function(str) {
61980             Roo.log("Property not found :"  + str);
61981             return '';
61982         };
61983         
61984         var fn = function(m, name, format, args)
61985         {
61986             //Roo.log(arguments);
61987             args = args ? args.replace(/\\'/g,"'") : args;
61988             //["{TEST:(a,b,c)}", "TEST", "", "a,b,c", 0, "{TEST:(a,b,c)}"]
61989             if (typeof(format) == 'undefined') {
61990                 format= 'htmlEncode';
61991             }
61992             if (format == 'raw' ) {
61993                 format = false;
61994             }
61995             
61996             if(name.substr(0, 4) == 'xtpl'){
61997                 return "'"+ sep +'this.applySubTemplate('+name.substr(4)+', values, parent)'+sep+"'";
61998             }
61999             
62000             // build an array of options to determine if value is undefined..
62001             
62002             // basically get 'xxxx.yyyy' then do
62003             // (typeof(xxxx) == 'undefined' || typeof(xxx.yyyy) == 'undefined') ?
62004             //    (function () { Roo.log("Property not found"); return ''; })() :
62005             //    ......
62006             
62007             var udef_ar = [];
62008             var lookfor = '';
62009             Roo.each(name.split('.'), function(st) {
62010                 lookfor += (lookfor.length ? '.': '') + st;
62011                 udef_ar.push(  "(typeof(" + lookfor + ") == 'undefined')"  );
62012             });
62013             
62014             var udef_st = '((' + udef_ar.join(" || ") +") ? undef('" + name + "') : "; // .. needs )
62015             
62016             
62017             if(format && useF){
62018                 
62019                 args = args ? ',' + args : "";
62020                  
62021                 if(format.substr(0, 5) != "this."){
62022                     format = "fm." + format + '(';
62023                 }else{
62024                     format = 'this.call("'+ format.substr(5) + '", ';
62025                     args = ", values";
62026                 }
62027                 
62028                 return "'"+ sep +   udef_st   +    format + name + args + "))"+sep+"'";
62029             }
62030              
62031             if (args.length) {
62032                 // called with xxyx.yuu:(test,test)
62033                 // change to ()
62034                 return "'"+ sep + udef_st  + name + '(' +  args + "))"+sep+"'";
62035             }
62036             // raw.. - :raw modifier..
62037             return "'"+ sep + udef_st  + name + ")"+sep+"'";
62038             
62039         };
62040         var body;
62041         // branched to use + in gecko and [].join() in others
62042         if(Roo.isGecko){
62043             body = "tpl.compiled = function(values, parent){  with(values) { return '" +
62044                    tpl.body.replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn) +
62045                     "';};};";
62046         }else{
62047             body = ["tpl.compiled = function(values, parent){  with (values) { return ['"];
62048             body.push(tpl.body.replace(/(\r\n|\n)/g,
62049                             '\\n').replace(/'/g, "\\'").replace(this.re, fn));
62050             body.push("'].join('');};};");
62051             body = body.join('');
62052         }
62053         
62054         Roo.debug && Roo.log(body.replace(/\\n/,'\n'));
62055        
62056         /** eval:var:tpl eval:var:fm eval:var:useF eval:var:undef  */
62057         eval(body);
62058         
62059         return this;
62060     },
62061
62062     applyTemplate : function(values){
62063         return this.master.compiled.call(this, values, {});
62064         //var s = this.subs;
62065     },
62066
62067     apply : function(){
62068         return this.applyTemplate.apply(this, arguments);
62069     }
62070
62071  });
62072
62073 Roo.XTemplate.from = function(el){
62074     el = Roo.getDom(el);
62075     return new Roo.XTemplate(el.value || el.innerHTML);
62076 };